11. Классы.
Общее понятие класса
- Класс (class) - пользовательский тип описывающий объект.
- На основе классов создаются объекты (экземпляры, class instance).
- Обозначаются ключевым словом class.
- В Python классы также являются объектами.
class Simple(object): pass simple_obj = Simple() # field will be set only for that particular object simple_obj.field = "some text" print simple_obj.field print isinstance(simple_obj, Simple) print isinstance(simple_obj, object)
some text True True
- В круглых скобках указывается предок.
- Начиная с некоторой версии классы записываются как в этом примере - с явным указанием предка object для всех.
- Это так называемые new-style classes и такую запись следует использовать.
Про определение класса
- Тело класа - последовательность выражений.
- Класс задаётся путём описания выражений внутри его.
- Класс создаётся после последовательного выполнения выражений: обычно это тела функций, иногда поля класса.
- Тело класса задаёт локальное пространство имён для определённых в нём объектов.
- После выполнения тела класса - он создаётся и связывается с переменной указанной в его имени.
Классы как объекты
Поддерживают две основных операции:
- Обращение к аттрибуту класа - через точку по имени.
- Создание объекта класса - с помощью круглых скобок, как вызов функции.
Экземпляры классов
- Поддерживают обращение к аттрибуту.
- Аттрибуты могут быть данными и методами.
- Методы != функции.
- Все аттрибуты, которые являются методами, при создании класса задают соответствующий метод.
class Logger(object): def log(self, message): print "Imitating logger: ", message logger = Logger() logger.log("Starting operation") print "Doing something" logger.log("Ending operation") Logger.log(logger, "Calling explicitly from class")
Imitating logger: Starting operation Doing something Imitating logger: Ending operation Imitating logger: Calling explicitly from class
- Экземплярный метод записывается по аналогии с функцией
- Но должен иметь явно указанный первый аргумент self
- self - аналог this из других языков, представляет конкретный объект класса.
- Вызывается через оперататор точка для конкретного объекта.
-
Можно явно вызвать от класса и передать объект первым аргументом.
-
Классам можно указывать аттрибуты, которые будут доступны из всех объектов этого класса.
- При изменении аттрибутов класса изменения видны и из объектов этого класса.
- Если присвоить значение по имени аттрибута класса объекту класса, то аттрибут класса в этом объекте закроется полем объекта.
- Важно различать понятия аттрибут класса и поле объекта.
Инициализация данных в классе
class Aggregator(object): def __init__(self): self.total_sum = 0 self.elements_count = 0 def add_value(self, value): self.total_sum += value self.elements_count += 1 def get_average(self): return self.total_sum / self.elements_count def get_sum(self): return self.total_sum
__init__
- метод-инициализатор, вызывается после создания объекта для его инициализации.- В отличие от других языков (конструкторы в C++), инициализатор вызывается уже для созданного объекта.
- Принятый способ задания полей объекта - в инициализаторе.
- Если поле изначально может не существовать, а будет создано в результате некоторой логики, то принято явно создать поле с начальным значением ({} или просто None).
__init__
- представитель так называемых “магических методов”.
Магические методы
- Специальные методы, которые начинаются и заканчиваются с двух нижних подчёркиваний.
- Обычно используются неявно и с их возможностью можно определять некоторое поведение для функций или библиотек.
Примеры: для арифметических операций (add, radd, iadd, mul, div …) etc.
Объект как словарь
- hasattr(object, attrname) - проверить есть ли аттрибут attrname у объекта.
- getattr(object, attrname, [default]) - взять у объекта значение аттрибута (default, если такого нет; можно описать
__getattr__
). - setattr(object, name, value) - установить аттрибут name в значение value.
Наследование
- Наследование - способ обобщения, механизм языка позволяющий описать новый класс на основе существующего.
- В Python также поддерживается множественное наследование (более одного предка).
- Во многих случах принято обходиться без наследования - добавляет сложностей - используйте только там, где есть разумная потребность.
- Множественное наследование требуется ещё реже и добавляет ещё больше сложностей.
- Одно из допустимых применений множественного наследования - создание классов-примесей (mixin).
Одиночное наследование
class Parent(object): def __init__(self, surname="Doe"): self.surname = surname def __str__(self): return "Surname: {surname}".format(surname=self.surname) class Child(Parent): def __init__(self, name, surname=None): if surname is not None: super(Child, self).__init__(surname=surname) else: super(Child, self).__init__() self.name = name def __str__(self): description = super(Child, self).__str__() return "Name: {name}, {suffix}".format(name=self.name, suffix=description) boy = Child("John") girl = Child("Jane") print boy print girl
Name: John, Surname: Doe Name: Jane, Surname: Doe
- Особая функция super() позволяет получать доступ к родительскому классу явно не указывая его имени.
- Возвращает прокси-объект через который осуществляется обращение к нужному объекту.
- С помощью её обычно вызывается метод предка, который перекрывается в текущем классе.
- Позволяет писать более обобщаемый код.
- Кроме этого, super() используется при множественном наследовании для динамического определения вызываемого объекта.
Множественное наследование
class Mother(object): def hello(self): print "Hello from mother" class Father(object): def hello(self): print "Hello from father" class Child(Mother, Father): def hello(self): print "Hello from child" child = Child() child.hello() print child.__class__.__mro__
Hello from child (<class '__main__.Child'>, <class '__main__.Mother'>, <class '__main__.Father'>, <type 'object'>)
- MRO - method resolution order - некоторый порядок выбора метода при вызове.
- Актуален при множественном наследовании.
- Для new-style classes определяется динамически и задаётся некоторым набором правил.
Приватные поля и методы
- В питоне нет привычных приватных полей и методов.
- Есть соглашение: переменные начинающиеся с нижнего подчёркивания стоит считать внутренними деталями реализации и не обращаться к ним снаружи.
Name mangling
- Есть дополнительный ограничивающий механизм - name mangling (искажение имён).
- Он применяется для переменных, имя которых начинается с двух нижних подчёркиваний.
- Имя таких переменных неявно приводится к другому виду:
__name -> _classname__name
. - Доступа из потомков по обычному имени нет совсем.
- Доступ снаружи только по искаженному имени.
- Применяется иногда для избегания коллизий в сложных иерархиях.
- Рекомендуется не злоупотребрять и исползовать только при осознаной необходимости.