3.3. Определение классов

Python имеет полноценную поддержку объектно-ориентированного программирования: вы божете определять собственные классы, наследоваться от встроенных и собственных классов, создавать экземпляры определенных вами классов.

Определять классы в языке Python просто. Как и для функций, для классов нет отдельного определения интерфейса. Определения класса в языке Python начинается с зарезервированного слова class и следующего за ним имени класса. Технически, это все что требуется, так как класс совсем необязательно должен быть производным от другого класса.

Пример 3.5. Простейший класс

class foo: 1 pass 2 3
1 Определяем класс с именем foo, который не является производным от других классов.
2 Этот класс не определяет никаких методов и атрибутов, но синтаксис требует наличие хотябы одной иструкции в определении, поэтому мы использовали pass. Это зарезервированное слово, которое означает, что ничего делать не нужно. Инструкция pass ничего не делает и полезна в качестве наполнителя в случаях, когда вы хотите поставить заглушку в определении функции или класса.
3 Как вы, наверное, уже догадались, тело класса записывается с отступом, как и тело функции или инструкций if, for и т. д. Первая строка, записанная без отступа, уже не попадает в определение класса.
Замечание
Инструкция pass в Python ведет себя аналогично пустым фигурным скобкам ({}) в Java и C.

Конечно, в реальных программах большинство классов будут производными от других классов и будут определять собственные атрибуты и методы. Но, как вы уже увидели, нет ничего, что класс обязательно должен иметь, кроме имени. В частности, программистам на C++ может показаться странным, что классы в языке Python не имеют явных конструкторов и деструкторов. В классах языка Python есть нечто? похожее на конструктор — метод __init__.

Пример 3.6. Определение класса FileInfo

from UserDict import UserDict class FileInfo(UserDict): 1
1 В языке Python родительские классы просто перечисляются в скобках сразу после имени класса. В данном случае класс FileInfo наследуется от класса UserDict (который был проимпортирован из модуля UserDict). UserDict — класс, который ведет себя аналогично словарю, позволяя от него наследоваться и изменять или дополнять его поведение. (Существуют аналогичные классы UserList и UserString, позволяющие определить класс, производный от списка и строки.) Здесь есть немного черной магии, которую мы раскроем позже в этой главе, когда будем подробнее исследовать класс UserDict.
Замечание
В языке Python родительские классы просто перечисляются в скобках после имени. Для этого не нужно использовать специальное ключевое слово, такое как extends в Java.
Замечание
Хотя я не буду буду рассказывать об этом подробно, Python поддерживает множественное наследование. В скобках после имени класса вы можете пересислить через запятую столько родительских классов, сколько вам нужно.

Пример 3.7. Инициализация класса FileInfo

class FileInfo(UserDict): "хранит метаинформацию о файле" 1 def __init__(self, filename=None): 2 3 4
1 Для классов можно (и желательно) определять строку документации, также как для модулей и функций.
2 Метод __init__ вызывается сразу после создания экземпляра класса. Соблазнительно, но не правильно называть этот метод конструктором. Соблазнительно, потому что он выглядит как конструктор (принято, чтобы __init__ был первым методом, определенным в классе), ведет себя как коструктор (это перый кусок кода, вызываемый в созданном экземпляре класса) и даже называется как коструктор. Неправильно, так как к тому времени, когда вызывается метод __init__, объект уже создан и вы имеете ссылку на созданный экземпляр класса. Но метод __init__ — это самое близкое к конструктору, из того что есть в языке Python.
3 Первым аргументом каждого метода класса, включая __init__, всегда является текущий экземпляр класса. Общепринято всегда называть этот аргумент self. В методе __init__ self ссылается на только что созданный объект, в других методах — на экземпляр класса, для которого метод вызывается. Хотя и необходимо явно указывать self при определении метода, вы его не указываете, когда вызываете метод; Python добавит его автоматически.
4 Метод __init__ может иметь несколько аргументов. Аргументы могут иметь значения по умолчанию, что сделает их необязательными. В данном случае аргумент filename имеет значение по умолчанию None.
Замечание
Первый аргумент метода класса (ссылка на текущий экземпляр) принято называть self. Этот аргумент играет роль зарезервированного слова this в C++ и Java, но self не является зарезервированным словом — просто соглашение. Несмотря на это, не стоит называть его иначе, чем self.

Пример 3.8. Тело класса FileInfo

class FileInfo(UserDict): "хранит метаинформацию о файле" def __init__(self, filename=None): UserDict.__init__(self) 1 self["name"] = filename 2 3
1 Некоторыми псевдо-объектно-ориентированными языками, например Powerbuilder, поддерживается концепция “расширения” конструкторов и других обработчиков событий: метод базового класса автоматически вызывается перед выполнением метода производного класса. В Python этого не происходит, необходимо явно вызывать метод в производном классе.
2 Я сказал, что наш класс ведет себя аналогично словарю, и вот первое проявление такого поведения: мы присваивает значение аргумента filename записи объекта с ключом name.
3 Обратите внимание, что метод __init__ никогда не возвращает значение.
Замечание
В определении методов необходимо явно указывать self в качестве первого аргумента любого метода, включая __init__. При вызове метода базового класса также необходимо включать self в список аргументов. Но при вызове метода извне аргумент self не указывается, а подставляется интерпретатором автоматически. I am aware that this is confusing at first; it's not really inconsistent, but it may appear inconsistent because it relies on a distinction (between bound and unbound methods) that you don't know about yet.

Whew. I realize that's a lot to absorb, but you'll get the hang of it. All Python classes work the same way, so once you learn one, you've learned them all. If you forget everything else, remember this one thing, because I promise it will trip you up:

Замечание
__init__ methods are optional, but when you define one, you must remember to explicitly call the ancestor's __init__ method. This is more generally true: whenever a descendant wants to extend the behavior of the ancestor, the descendant method must explicitly call the ancestor method at the proper time, with the proper arguments.

Further reading