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 не указывается, а подставляется интерпретатором автоматически. Подозреваю, что сначала это сбивает с толку и может показаться непоследовательным, так как вы еще не узнали о разнице между связаннымы и несвязанными методами.

Вот так. Понимаю, что здесь многое нужно запомнить, но вы быстро наловчитесь. Все классы в языке Python работают одинаково, так что изучив один, вы узнаете обо всех. Вы можете забыть обо всем остальном, но одно правило стоит запомнить (иначе, обещаю, вы когда-нибудь запутаетесь):

Замечание
Метод __init__ не является обязательным, но если вы его определяете, то не забудть вызвать методы __init__ базовых классов. Это правило верно и в более широком смысле: если производный класс хочет расширить поведение базового, то он должен в нужное время вызвать соответствующий метод базового класса с необходимыми ему аргументами.

Дополнительная литература