SlideShare a Scribd company logo
1 of 38
Download to read offline
Лекция 6: Классы 1
Сергей Лебедев
sergei.a.lebedev@gmail.com
12 октября 2015 г.
Классы
Синтаксис объявления классов
>>> class Counter:
... """I count. That is all."""
... def __init__(self, initial=0): # конструктор
... self.value = initial # запись атрибута
...
... def increment(self):
... self.value += 1
...
... def get(self):
... return self.value # чтение атрибута
...
>>> c = Counter(42)
>>> c.increment()
>>> c.get()
43
1 / 35
Классы и self
• В отличие от Java и C++ в Python нет “магического”
ключевого слова this. Первый аргумент конструктора
__init__ и всех остальных методов — экземпляр класса,
который принято называть self.
• Синтаксис языка не запрещает называть его по-другому, но
так делать не рекомендуется:
>>> class Noop:
... def __init__(ego):
... pass
...
>>> Noop = Noop()
2 / 35
Классы, экземпляры и атрибуты
• Аналогично другим ООП языкам Python разделяет
атрибуты экземпляра и атрибуты класса.
• Атрибуты добавляются к экземпляру посредством
присваивания к self конструкцией вида:
self.some_attribute = value
• Атрибуты класса объявляются в теле класса или прямым
присваиванием к классу:
>>> class Counter:
... all_counters = []
...
... def __init__(self, initial=0):
... Counter.all_counters.append(self)
... # ...
...
>>> Counter.some_other_attribute = 42
3 / 35
Соглашения об именовании атрибутов и методов
• В Python нет модификаторов доступа к атрибутам и
методам: почти всё можно читать и присваивать.
• Для того чтобы различать публичные и внутренние
атрибуты визуально, к внутренним атрибутам добавляют в
начало символ подчеркивания:
>>> class Noop:
... some_attribute = 42
... _internal_attribute = []
• Особо ярые любители контроля используют два
подчёркивания:
>>> class Noop:
... __very_internal_attribute = []
...
>>> Noop.__very_internal_attribute
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Noop' has no attribute [...]
>>> Noop._Noop__very_internal_attribute
4 / 35
Привет от калибровочного теста
class MemorizingDict(dict):
history = deque(maxlen=10)
def set(self, key, value):
self.history.append(key)
self[key] = value
def get_history(self):
return self.history
d = MemorizingDict({"foo": 42})
d.set("baz", 100500)
print(d.get_history()) # ==> ?
d = MemorizingDict()
d.set("boo", 500100)
print(d.get_history()) # ==> ?
5 / 35
Внутренние атрибуты классов и экземляров
>>> class Noop:
... """I do nothing at all."""
...
>>> Noop.__doc__
'I do nothing at all.'
>>> Noop.__name__
'Noop'
>>> Noop.__module__
'__main__'
>>> Noop.__bases__
(<class 'object'>,)
>>> noop = Noop()
>>> noop.__class__
<class '__main__.Noop'>
>>> noop.__dict__ # словарь атрибутов объекта
{}
Вопрос
Как вы думаете, чему равняются Noop.__class__ и
Noop.__dict__?
6 / 35
Подробнее о __dict__
• Все атрибуты объекта доступны в виде словаря:
>>> noop.some_attribute = 42
>>> noop.__dict__
{'some_attribute': 42}
• Очевидные следствия:
• Добавление, изменение и удаление атрибутов — это
фактически операции со словарём.
>>> noop.__dict__["some_other_attribute"] = 100500
>>> noop.some_other_attribute
100500
>>> del noop.some_other_attribute
• Поиск значения атрибута происходит динамически в
момент выполнения программы.
• Для доступа к словарю атрибутов можно также
использовать функцию vars:
>>> vars(noop)
{'some_attribute': 42}
7 / 35
Классы и __slots__
• С помощью специального аттрибута класса __slots__
можно зафиксировать множество возможных атрибутов
экземпляра:
>>> class Noop:
... __slots__ = ["some_attribute"]
...
>>> noop = Noop()
>>> noop.some_attribute = 42
>>> noop.some_attribute
42
>>> noop.some_other_attribute = 100500
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Noop' object has no attribute [...]
• Экземпляры класса с указанным __slots__ требуют
меньше памяти, потому что у них отсутствует __dict__.
8 / 35
Связанные и несвязанные методы
• У связанного метода первый аргумент уже зафиксирован и
равен соответствующему экземпляру:
>>> class SomeClass:
... def do_something(self):
... print("Doing something.")
...
>>> SomeClass().do_something # связанный
<bound method SomeClass.do_something of [...]>
>>> SomeClass().do_something()
Doing something.
• Несвязанному методу необходимо явно передать
экземпляр первым аргументом в момент вызова:
>>> SomeClass.do_something # несвязанный
<function SomeClass.do_something at 0x105466a60>
>>> instance = SomeClass()
>>> SomeClass.do_something(instance)
Doing something.
9 / 35
Свойства
• Механизм свойств позволяет объявлять атрибуты,
значение которых вычисляется в момент обращения:
>>> class Path:
... def __init__(self, current):
... self.current = current
...
... def __repr__(self):
... return "Path({})".format(self.current)
...
... @property
... def parent(self):
... return Path(dirname(self.current))
...
>>> p = Path("./examples/some_file.txt")
>>> p.parent
Path('./examples')
• Можно также переопределить логику изменения и
удаления таких атрибутов.
10 / 35
Свойства, изменение и удаление
>>> class BigDataModel:
... def __init__(self):
... self._params = []
...
... @property
... def params(self):
... return self._params
...
... @params.setter
... def params(self, new_params):
... assert all(map(lambda p: p > 0, new_params))
... self._params = new_params
...
... @params.deleter
... def params(self):
... del self._params
...
>>> model = BigDataModel()
>>> model.params = [0.1, 0.5, 0.4]
>>> model.params
[0.1, 0.5, 0.4]
11 / 35
Наследование
• Синтаксис оператора class позволяет унаследовать
объявляемый класс от произвольного количества других
классов:
>>> class Counter:
... def __init__(self, initial=0):
... self.value = initial
...
>>> class OtherCounter(Counter):
... def get(self):
... return self.value
• Поиск имени при обращении к атрибуту или методу
ведётся сначала в __dict__ экземпляра. Если там имя не
найдено, оно ищется в классе, а затем рекурсивно во всей
иерархии наследования.
>>> c = OtherCounter() # вызывает Counter.__init__
>>> c.get() # вызывает OtherCounter.get
0
>>> c.value # c.__dict__["value"]
12 / 35
Перегрузка методов и функция super
>>> class Counter:
... all_counters = []
...
... def __init__(self, initial=0):
... self.__class__.all_counters.append(self)
... self.value = initial
...
>>> class OtherCounter(Counter):
... def __init__(self, initial=0):
... self.initial = initial
... super().__init__(initial)
...
>>> oc = OtherCounter()
>>> vars(oc)
{'initial': 0, 'value': 0}
Вопрос
Как можно было бы реализовать функцию super?
13 / 35
Предикат isinstance
• Предикат isinstance принимает объект и класс и
проверяет, что объект является экземпляром класса:
>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> isinstance(B(), A)
True
• В качестве второго аргумента можно также передать
кортеж классов:
>>> class C:
... pass
...
>>> isinstance(B(), (A, C))
True
>>> isinstance(B(), A) or isinstance(B(), C)
True
14 / 35
Предикат issubclass
• Предикат issubclass принимает два класса и проверяет,
что первый класс является потомком второго:
>>> class A:
... pass
...
>>> class B(A):
... pass
...
>>> issubclass(B, A)
True
• Аналогично isinstance второй аргумент может быть
кортежем классов:
>>> class C:
... pass
...
>>> issubclass(B, (A, C))
True
>>> issubclass(B, A) or issubclass(B, C)
True
15 / 35
Множественное наследование
Python не запрещает множественное наследование, например,
можно определить следующую иерархию:
>>> class A:
... def f(self):
... print("A.f")
...
>>> class C(A, B):
... pass
...
>>> class B:
... def f(self):
... print("B.f")
...
Вопрос
Что выведет следующий фрагмент кода?
>>> C().f()
???
16 / 35
Множественное наследование и алгоритм C3
• В случае множественного наследования Python использует
алгоритм линеаризации C3 для определения метода,
который нужно вызвать.
• Получить линеаризацию иерархии наследования можно с
помощью метода mro:
>>> C.mro() # == C.__mro__
[<class '__main__.C'>, <class '__main__.A'>,
<class '__main__.B'>, <class 'object'>]
>>> C().f()
A.f
• Результат работы алгоритма C3 далеко не всегда
тривиален, поэтому использовать сложные иерархии
множественого наследования не рекомендуется.
• Больше примеров, а также реализацию алгоритма C3 на
Python можно найти по ссылке:
http://bit.ly/c3-mro.
17 / 35
Множественное наследование и классы-примеси
• Классы-примеси позволяют выборочно модифицировать
поведение класса в предположении, что класс реализует
некоторый интерфейс.
• Продолжая пример со счётчиком:
>>> class ThreadSafeMixin:
... get_lock = ...
...
... def increment(self):
... with self.get_lock():
... super().increment()
...
... def get(self):
... with self.get_lock():
... return super().get()
...
>>> class ThreadSafeCounter(ThreadSafeMixin,
... Counter):
... pass
...
18 / 35
Декораторы классов
• Синтаксис декораторов работает не только для функций,
но и для классов1:
>>> @deco
... class Noop:
... pass
...
>>> class Noop:
... pass
...
>>> Noop = deco(Noop)
• В этом случае декоратор — это функция, которая
принимает класс и возвращает другой, возможно,
преобразованный, класс.
• Декораторы классов можно также использовать вместо
чуть более магических классов-примесей.
1
http://python.org/dev/peps/pep-3129
19 / 35
Декораторы классов и классы-примеси
• Декораторы классов можно использовать вместо
классов-примесей.
• Например, ThreadSafeMixin в виде декоратора класса
выглядит следующим образом образом:
>>> def thread_safe(cls):
... orig_increment = cls.increment
... orig_get = cls.get
...
... def increment(self):
... with self.get_lock():
... orig_increment(self)
...
... def get(self):
... with self.get_lock():
... return orig_get(self)
...
... cls.get_lock = ...
... cls.increment = increment
... cls.get = get
... return cls
20 / 35
Декораторы классов: @singleton
>>> def singleton(cls):
... instance = None
...
... @functools.wraps(cls)
... def inner(*args, **kwargs):
... nonlocal instance
... if instance is None:
... instance = cls(*args, **kwargs)
... return instance
...
... return inner
...
>>> @singleton
... class Noop:
... "I do nothing at all."
...
>>> id(Noop())
4383371952
>>> id(Noop())
4383371952
21 / 35
Декораторы классов: @deprecated
>>> import warnings
>>> def deprecated(cls):
... orig_init = cls.__init__
...
... @functools.wraps(cls.__init__)
... def new_init(self, *args, **kwargs):
... warnings.warn(
... cls.__name__ + " is deprecated.",
... category=DeprecationWarning)
... orig_init(self, *args, **kwargs)
...
... cls.__init__ = new_init
... return cls
...
>>> @deprecated
... class Counter:
... def __init__(self, initial=0):
... self.value = initial
...
>>> c = Counter()
__main__:6: DeprecationWarning: Counter is deprecated.
22 / 35
Классы: резюме
• Синтаксис объявления классов в Python:
>>> class SomeClass(Base1, Base2, ...):
... """Useful documentation."""
... class_attr = ...
...
... def __init__(self, some_arg):
... self.instance_attr = some_arg
...
... def do_something(self):
... pass
• В отличие от большинства объектно-ориентированных
языков Python:
• делает передачу ссылки на экземпляр явной, self —
первый аргумент каждого метода,
• реализует механим свойств — динамически вычисляемых
атрибутов,
• поддерживает изменение классов с помощью декораторов.
23 / 35
“Магические”
методы
“Магические” методы
• “Магическими” называются внутренние методы классов,
например, метод __init__.
• С помощью “магических” методов можно:
• управлять доступом к атрибутам экземпляра,
• перегрузить операторы, например, операторы сравнения
или арифметические операторы,
• определить строковое представление экземпляра или
изменить способ его хеширования.
• Мы рассмотрим только часть наиболее используемых
методов.
• Подробное описание всех “магических” методов можно
найти в документации языка2.
2
http://bit.ly/magic-methods
24 / 35
“Магические” методы: __getattr__
• Метод __getattr__ вызывается при попытке прочитать
значение несуществующего атрибута:
>>> class Noop:
... pass
...
>>> Noop().foobar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Noop' object has no attribute 'foobar'
• Определим метод __getattr__ для класса Noop:
>>> class Noop:
... def __getattr__(self, name):
... return name # identity
...
>>> Noop().foobar
'foobar'
25 / 35
“Магические” методы: __setattr__ и __delattr__
• Методы __setattr__ и __delattr__ позволяют управлять
изменением значения и удалением атрибутов.
• В отличие от __getattr__ они вызываются для всех
атрибутов, а не только для несуществующих.
• Пример, запрещающий изменять значение некоторых
атрибутов:
>>> class Guarded:
... guarded = []
...
... def __setattr__(self, name, value):
... assert name not in self.guarded
... super().__setattr__(name, value)
...
>>> class Noop(Guarded):
... guarded = ["foobar"]
...
... def __init__(self):
... self.__dict__["foobar"] = 42 # Зачем это?
...
26 / 35
Функции getattr, setattr и delattr
• Функция getattr позволяет безопасно получить значение
атрибута экземпляра класса по его имени:
>>> class Noop:
... some_attribute = 42
...
>>> noop = Noop()
>>> getattr(noop, "some_attribute")
42
>>> getattr(noop, "some_other_attribute", 100500)
100500
• Комплементарные функции setattr и delattr добавляют
и удаляют атрибут:
>>> setattr(noop, "some_other_attribute", 100500)
>>> delattr(noop, "some_other_attribute")
27 / 35
“Магические” методы: операторы сравнения
• Чтобы экземпляры класса поддерживали все операторы
сравнения, нужно реализовать внушительное количество
“магических” методов:
instance.__eq__(other) # instance == other
instance.__ne__(other) # instance != other
instance.__lt__(other) # instance < other
instance.__le__(other) # instance <= other
instance.__gt__(other) # instance > other
instance.__ge__(other) # instance >= other
• В уже знакомом нам модуле functools есть декоратор,
облегчающий реализацию операторов сравнения:
>>> import functools
>>> @functools.total_ordering
... class Counter:
... def __eq__(self, other):
... return self.value == other.value
...
... def __lt__(self, other): # или <=, >, >=
... return self.value < other.value
28 / 35
“Магический” метод __call__
• Метод __call__ позволяет “вызывать” экземпляры классов,
имитируя интерфейс фнукций:
>>> class Identity:
... def __call__(self, x):
... return x
...
>>> Identity()(42)
42
• Как это можно использовать?
29 / 35
Декораторы с аргументами на основе классов
>>> class trace:
... def __init__(self, handle):
... self.handle = handle
...
... def __call__(self, func):
... @functools.wraps(func)
... def inner(*args, **kwargs):
... print(func.__name__, args, kwargs,
... file=self.handle)
... return func(*args, **kwargs)
... return inner
...
>>> @trace(sys.stderr)
... def identity(x):
... return x
...
>>> identity(42)
identity (42,) {}
42
30 / 35
“Магические” методы для преобразования в строку
• Напоминание: в Python есть две различных по смыслу
функции для преобразования объекта в строку: repr и str.
• Для каждой из них существует одноимённый “магический”
метод:
>>> class Counter:
... def __init__(self, initial=0):
... self.value = initial
...
... def __repr__(self):
... return "Counter({})".format(self.value)
...
... def __str__(self):
... return "Counted to {}".format(self.value)
...
>>> c = Counter(42)
>>> c
'Counter(42)'
>>> print(c)
Counted to 42
31 / 35
Расширение format через “магические” методы
>>> class Counter:
... def __init__(self, initial=0):
... self.value = initial
...
... def __format__(self, format_spec):
... return self.value.__format__(format_spec)
...
>>> c = Counter(42)
>>> "Counted to {:b}".format(c)
'Counted to 101010'
32 / 35
“Магический” метод __hash__
• Метод __hash__ используется для вычисления значения
хеш-функции.
• Реализация по умолчанию гарантирует, что одинаковое
значение хеш функции будет только у физически
одинаковых объектов, то есть:
x is y <=> hash(x) == hash(y).
• Несколько очевидных рекомендаций:
• Метод __hash__ имеет смысл реализовывать только вместе
с методом __eq__. При этом реализация __hash__ должна
удовлетворять: x == y => hash(x) == hash(y)
• Для изменяемых объектов можно ограничиться только
методом __eq__.
33 / 35
“Магический” метод __bool__
• Метод __bool__ для проверки значения на истинность,
например в условии оператора if.
• Для класса Counter реализация __bool__ тривиальна:
>>> class Counter:
... def __init__(self, initial=0):
... self.value = initial
...
... def __bool__(self):
... return bool(self.value)
...
>>> c = Counter()
>>> if not c:
... print("No counts yet.")
...
No counts yet.
34 / 35
“Магические” методы: резюме
• “Магические” методы позволяют уточнить поведение
экземпляров класса в различных конструкциях языка.
• Например, с помощью магического метода __str__ можно
указать способ приведения экземпляра класса, а с
помощью метода __hash__ — алгоритм хеширования
состояния класса.
• Мы рассмотрели небольшое подмножество “магических”
методов, на самом деле их много больше: практически
любое действие с экземпляром можно специализировать
для конкретного класса.
35 / 35

More Related Content

What's hot

Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.Roman Brovko
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.Roman Brovko
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Roman Brovko
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.Roman Brovko
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Sergey Schetinin
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Яковенко Кирилл
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в DjangoMoscowDjango
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonPython Meetup
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормозаAlexander Shigin
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка DjangoVladimir Rudnyh
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture libraryMERA_school
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности PythonPyNSK
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?PyNSK
 

What's hot (15)

Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормоза
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка Django
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture library
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
 

Viewers also liked

04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентовRoman Brovko
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммыRoman Brovko
 
01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?Roman Brovko
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработкеRoman Brovko
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframeRoman Brovko
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. ПрецедентыRoman Brovko
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложенияRoman Brovko
 

Viewers also liked (7)

04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы
 
01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения
 

Similar to Лекция 6. Классы 1.

Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioAndrey Karpov
 
Join the python_side
Join the python_sideJoin the python_side
Join the python_sidePaul Dmitryev
 
Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Levon Avakyan
 
Как мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовКак мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовBoris Tsema
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Dima Dzuba
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6Technopark
 
слайд питон БОН.pptx
слайд  питон  БОН.pptxслайд  питон  БОН.pptx
слайд питон БОН.pptxHSharipov
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
 
На что уходит память - Константин Лопухин, PyCon RU 2014
На что уходит память - Константин Лопухин, PyCon RU 2014На что уходит память - Константин Лопухин, PyCon RU 2014
На что уходит память - Константин Лопухин, PyCon RU 2014it-people
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9Technopark
 
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)IT-Доминанта
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#Andrey Karpov
 
Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013ScalaNsk
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному кодуVasiliy Deynega
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Andrey Karpov
 

Similar to Лекция 6. Классы 1. (20)

Принципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-StudioПринципы работы статического анализатора кода PVS-Studio
Принципы работы статического анализатора кода PVS-Studio
 
Join the python_side
Join the python_sideJoin the python_side
Join the python_side
 
Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей.
 
Как мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектовКак мы уменьшили сложность наших проектов
Как мы уменьшили сложность наших проектов
 
Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8. Объектно-ориентированное программирование. Лекция 7 и 8.
Объектно-ориентированное программирование. Лекция 7 и 8.
 
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
 
слайд питон БОН.pptx
слайд  питон  БОН.pptxслайд  питон  БОН.pptx
слайд питон БОН.pptx
 
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...
 
На что уходит память - Константин Лопухин, PyCon RU 2014
На что уходит память - Константин Лопухин, PyCon RU 2014На что уходит память - Константин Лопухин, PyCon RU 2014
На что уходит память - Константин Лопухин, PyCon RU 2014
 
C++ осень 2012 лекция 9
C++ осень 2012 лекция 9C++ осень 2012 лекция 9
C++ осень 2012 лекция 9
 
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)
"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)
 
Decorators' recipes
Decorators' recipesDecorators' recipes
Decorators' recipes
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
PVS-Studio. Статический анализатор кода. Windows/Linux, C/C++/C#
 
Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013
 
C#. От основ к эффективному коду
C#. От основ к эффективному кодуC#. От основ к эффективному коду
C#. От основ к эффективному коду
 
Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?Статический анализ кода: Что? Как? Зачем?
Статический анализ кода: Что? Как? Зачем?
 
Обзор Ruby
Обзор RubyОбзор Ruby
Обзор Ruby
 
2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure
 

More from Roman Brovko

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task NetworkingRoman Brovko
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3Roman Brovko
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernetRoman Brovko
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2Roman Brovko
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1Roman Brovko
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flashRoman Brovko
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2CRoman Brovko
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uartRoman Brovko
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorRoman Brovko
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwmRoman Brovko
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsRoman Brovko
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_worldRoman Brovko
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisitesRoman Brovko
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advancedRoman Brovko
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advancedRoman Brovko
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advancedRoman Brovko
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окруженияRoman Brovko
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advancedRoman Brovko
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basicsRoman Brovko
 

More from Roman Brovko (20)

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task Networking
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernet
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flash
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2C
 
Glesk worshop
Glesk worshopGlesk worshop
Glesk worshop
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uart
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensor
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwm
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttons
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_world
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisites
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advanced
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advanced
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advanced
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окружения
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advanced
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basics
 

Лекция 6. Классы 1.

  • 1. Лекция 6: Классы 1 Сергей Лебедев sergei.a.lebedev@gmail.com 12 октября 2015 г.
  • 3. Синтаксис объявления классов >>> class Counter: ... """I count. That is all.""" ... def __init__(self, initial=0): # конструктор ... self.value = initial # запись атрибута ... ... def increment(self): ... self.value += 1 ... ... def get(self): ... return self.value # чтение атрибута ... >>> c = Counter(42) >>> c.increment() >>> c.get() 43 1 / 35
  • 4. Классы и self • В отличие от Java и C++ в Python нет “магического” ключевого слова this. Первый аргумент конструктора __init__ и всех остальных методов — экземпляр класса, который принято называть self. • Синтаксис языка не запрещает называть его по-другому, но так делать не рекомендуется: >>> class Noop: ... def __init__(ego): ... pass ... >>> Noop = Noop() 2 / 35
  • 5. Классы, экземпляры и атрибуты • Аналогично другим ООП языкам Python разделяет атрибуты экземпляра и атрибуты класса. • Атрибуты добавляются к экземпляру посредством присваивания к self конструкцией вида: self.some_attribute = value • Атрибуты класса объявляются в теле класса или прямым присваиванием к классу: >>> class Counter: ... all_counters = [] ... ... def __init__(self, initial=0): ... Counter.all_counters.append(self) ... # ... ... >>> Counter.some_other_attribute = 42 3 / 35
  • 6. Соглашения об именовании атрибутов и методов • В Python нет модификаторов доступа к атрибутам и методам: почти всё можно читать и присваивать. • Для того чтобы различать публичные и внутренние атрибуты визуально, к внутренним атрибутам добавляют в начало символ подчеркивания: >>> class Noop: ... some_attribute = 42 ... _internal_attribute = [] • Особо ярые любители контроля используют два подчёркивания: >>> class Noop: ... __very_internal_attribute = [] ... >>> Noop.__very_internal_attribute Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Noop' has no attribute [...] >>> Noop._Noop__very_internal_attribute 4 / 35
  • 7. Привет от калибровочного теста class MemorizingDict(dict): history = deque(maxlen=10) def set(self, key, value): self.history.append(key) self[key] = value def get_history(self): return self.history d = MemorizingDict({"foo": 42}) d.set("baz", 100500) print(d.get_history()) # ==> ? d = MemorizingDict() d.set("boo", 500100) print(d.get_history()) # ==> ? 5 / 35
  • 8. Внутренние атрибуты классов и экземляров >>> class Noop: ... """I do nothing at all.""" ... >>> Noop.__doc__ 'I do nothing at all.' >>> Noop.__name__ 'Noop' >>> Noop.__module__ '__main__' >>> Noop.__bases__ (<class 'object'>,) >>> noop = Noop() >>> noop.__class__ <class '__main__.Noop'> >>> noop.__dict__ # словарь атрибутов объекта {} Вопрос Как вы думаете, чему равняются Noop.__class__ и Noop.__dict__? 6 / 35
  • 9. Подробнее о __dict__ • Все атрибуты объекта доступны в виде словаря: >>> noop.some_attribute = 42 >>> noop.__dict__ {'some_attribute': 42} • Очевидные следствия: • Добавление, изменение и удаление атрибутов — это фактически операции со словарём. >>> noop.__dict__["some_other_attribute"] = 100500 >>> noop.some_other_attribute 100500 >>> del noop.some_other_attribute • Поиск значения атрибута происходит динамически в момент выполнения программы. • Для доступа к словарю атрибутов можно также использовать функцию vars: >>> vars(noop) {'some_attribute': 42} 7 / 35
  • 10. Классы и __slots__ • С помощью специального аттрибута класса __slots__ можно зафиксировать множество возможных атрибутов экземпляра: >>> class Noop: ... __slots__ = ["some_attribute"] ... >>> noop = Noop() >>> noop.some_attribute = 42 >>> noop.some_attribute 42 >>> noop.some_other_attribute = 100500 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Noop' object has no attribute [...] • Экземпляры класса с указанным __slots__ требуют меньше памяти, потому что у них отсутствует __dict__. 8 / 35
  • 11. Связанные и несвязанные методы • У связанного метода первый аргумент уже зафиксирован и равен соответствующему экземпляру: >>> class SomeClass: ... def do_something(self): ... print("Doing something.") ... >>> SomeClass().do_something # связанный <bound method SomeClass.do_something of [...]> >>> SomeClass().do_something() Doing something. • Несвязанному методу необходимо явно передать экземпляр первым аргументом в момент вызова: >>> SomeClass.do_something # несвязанный <function SomeClass.do_something at 0x105466a60> >>> instance = SomeClass() >>> SomeClass.do_something(instance) Doing something. 9 / 35
  • 12. Свойства • Механизм свойств позволяет объявлять атрибуты, значение которых вычисляется в момент обращения: >>> class Path: ... def __init__(self, current): ... self.current = current ... ... def __repr__(self): ... return "Path({})".format(self.current) ... ... @property ... def parent(self): ... return Path(dirname(self.current)) ... >>> p = Path("./examples/some_file.txt") >>> p.parent Path('./examples') • Можно также переопределить логику изменения и удаления таких атрибутов. 10 / 35
  • 13. Свойства, изменение и удаление >>> class BigDataModel: ... def __init__(self): ... self._params = [] ... ... @property ... def params(self): ... return self._params ... ... @params.setter ... def params(self, new_params): ... assert all(map(lambda p: p > 0, new_params)) ... self._params = new_params ... ... @params.deleter ... def params(self): ... del self._params ... >>> model = BigDataModel() >>> model.params = [0.1, 0.5, 0.4] >>> model.params [0.1, 0.5, 0.4] 11 / 35
  • 14. Наследование • Синтаксис оператора class позволяет унаследовать объявляемый класс от произвольного количества других классов: >>> class Counter: ... def __init__(self, initial=0): ... self.value = initial ... >>> class OtherCounter(Counter): ... def get(self): ... return self.value • Поиск имени при обращении к атрибуту или методу ведётся сначала в __dict__ экземпляра. Если там имя не найдено, оно ищется в классе, а затем рекурсивно во всей иерархии наследования. >>> c = OtherCounter() # вызывает Counter.__init__ >>> c.get() # вызывает OtherCounter.get 0 >>> c.value # c.__dict__["value"] 12 / 35
  • 15. Перегрузка методов и функция super >>> class Counter: ... all_counters = [] ... ... def __init__(self, initial=0): ... self.__class__.all_counters.append(self) ... self.value = initial ... >>> class OtherCounter(Counter): ... def __init__(self, initial=0): ... self.initial = initial ... super().__init__(initial) ... >>> oc = OtherCounter() >>> vars(oc) {'initial': 0, 'value': 0} Вопрос Как можно было бы реализовать функцию super? 13 / 35
  • 16. Предикат isinstance • Предикат isinstance принимает объект и класс и проверяет, что объект является экземпляром класса: >>> class A: ... pass ... >>> class B(A): ... pass ... >>> isinstance(B(), A) True • В качестве второго аргумента можно также передать кортеж классов: >>> class C: ... pass ... >>> isinstance(B(), (A, C)) True >>> isinstance(B(), A) or isinstance(B(), C) True 14 / 35
  • 17. Предикат issubclass • Предикат issubclass принимает два класса и проверяет, что первый класс является потомком второго: >>> class A: ... pass ... >>> class B(A): ... pass ... >>> issubclass(B, A) True • Аналогично isinstance второй аргумент может быть кортежем классов: >>> class C: ... pass ... >>> issubclass(B, (A, C)) True >>> issubclass(B, A) or issubclass(B, C) True 15 / 35
  • 18. Множественное наследование Python не запрещает множественное наследование, например, можно определить следующую иерархию: >>> class A: ... def f(self): ... print("A.f") ... >>> class C(A, B): ... pass ... >>> class B: ... def f(self): ... print("B.f") ... Вопрос Что выведет следующий фрагмент кода? >>> C().f() ??? 16 / 35
  • 19. Множественное наследование и алгоритм C3 • В случае множественного наследования Python использует алгоритм линеаризации C3 для определения метода, который нужно вызвать. • Получить линеаризацию иерархии наследования можно с помощью метода mro: >>> C.mro() # == C.__mro__ [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] >>> C().f() A.f • Результат работы алгоритма C3 далеко не всегда тривиален, поэтому использовать сложные иерархии множественого наследования не рекомендуется. • Больше примеров, а также реализацию алгоритма C3 на Python можно найти по ссылке: http://bit.ly/c3-mro. 17 / 35
  • 20. Множественное наследование и классы-примеси • Классы-примеси позволяют выборочно модифицировать поведение класса в предположении, что класс реализует некоторый интерфейс. • Продолжая пример со счётчиком: >>> class ThreadSafeMixin: ... get_lock = ... ... ... def increment(self): ... with self.get_lock(): ... super().increment() ... ... def get(self): ... with self.get_lock(): ... return super().get() ... >>> class ThreadSafeCounter(ThreadSafeMixin, ... Counter): ... pass ... 18 / 35
  • 21. Декораторы классов • Синтаксис декораторов работает не только для функций, но и для классов1: >>> @deco ... class Noop: ... pass ... >>> class Noop: ... pass ... >>> Noop = deco(Noop) • В этом случае декоратор — это функция, которая принимает класс и возвращает другой, возможно, преобразованный, класс. • Декораторы классов можно также использовать вместо чуть более магических классов-примесей. 1 http://python.org/dev/peps/pep-3129 19 / 35
  • 22. Декораторы классов и классы-примеси • Декораторы классов можно использовать вместо классов-примесей. • Например, ThreadSafeMixin в виде декоратора класса выглядит следующим образом образом: >>> def thread_safe(cls): ... orig_increment = cls.increment ... orig_get = cls.get ... ... def increment(self): ... with self.get_lock(): ... orig_increment(self) ... ... def get(self): ... with self.get_lock(): ... return orig_get(self) ... ... cls.get_lock = ... ... cls.increment = increment ... cls.get = get ... return cls 20 / 35
  • 23. Декораторы классов: @singleton >>> def singleton(cls): ... instance = None ... ... @functools.wraps(cls) ... def inner(*args, **kwargs): ... nonlocal instance ... if instance is None: ... instance = cls(*args, **kwargs) ... return instance ... ... return inner ... >>> @singleton ... class Noop: ... "I do nothing at all." ... >>> id(Noop()) 4383371952 >>> id(Noop()) 4383371952 21 / 35
  • 24. Декораторы классов: @deprecated >>> import warnings >>> def deprecated(cls): ... orig_init = cls.__init__ ... ... @functools.wraps(cls.__init__) ... def new_init(self, *args, **kwargs): ... warnings.warn( ... cls.__name__ + " is deprecated.", ... category=DeprecationWarning) ... orig_init(self, *args, **kwargs) ... ... cls.__init__ = new_init ... return cls ... >>> @deprecated ... class Counter: ... def __init__(self, initial=0): ... self.value = initial ... >>> c = Counter() __main__:6: DeprecationWarning: Counter is deprecated. 22 / 35
  • 25. Классы: резюме • Синтаксис объявления классов в Python: >>> class SomeClass(Base1, Base2, ...): ... """Useful documentation.""" ... class_attr = ... ... ... def __init__(self, some_arg): ... self.instance_attr = some_arg ... ... def do_something(self): ... pass • В отличие от большинства объектно-ориентированных языков Python: • делает передачу ссылки на экземпляр явной, self — первый аргумент каждого метода, • реализует механим свойств — динамически вычисляемых атрибутов, • поддерживает изменение классов с помощью декораторов. 23 / 35
  • 27. “Магические” методы • “Магическими” называются внутренние методы классов, например, метод __init__. • С помощью “магических” методов можно: • управлять доступом к атрибутам экземпляра, • перегрузить операторы, например, операторы сравнения или арифметические операторы, • определить строковое представление экземпляра или изменить способ его хеширования. • Мы рассмотрим только часть наиболее используемых методов. • Подробное описание всех “магических” методов можно найти в документации языка2. 2 http://bit.ly/magic-methods 24 / 35
  • 28. “Магические” методы: __getattr__ • Метод __getattr__ вызывается при попытке прочитать значение несуществующего атрибута: >>> class Noop: ... pass ... >>> Noop().foobar Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Noop' object has no attribute 'foobar' • Определим метод __getattr__ для класса Noop: >>> class Noop: ... def __getattr__(self, name): ... return name # identity ... >>> Noop().foobar 'foobar' 25 / 35
  • 29. “Магические” методы: __setattr__ и __delattr__ • Методы __setattr__ и __delattr__ позволяют управлять изменением значения и удалением атрибутов. • В отличие от __getattr__ они вызываются для всех атрибутов, а не только для несуществующих. • Пример, запрещающий изменять значение некоторых атрибутов: >>> class Guarded: ... guarded = [] ... ... def __setattr__(self, name, value): ... assert name not in self.guarded ... super().__setattr__(name, value) ... >>> class Noop(Guarded): ... guarded = ["foobar"] ... ... def __init__(self): ... self.__dict__["foobar"] = 42 # Зачем это? ... 26 / 35
  • 30. Функции getattr, setattr и delattr • Функция getattr позволяет безопасно получить значение атрибута экземпляра класса по его имени: >>> class Noop: ... some_attribute = 42 ... >>> noop = Noop() >>> getattr(noop, "some_attribute") 42 >>> getattr(noop, "some_other_attribute", 100500) 100500 • Комплементарные функции setattr и delattr добавляют и удаляют атрибут: >>> setattr(noop, "some_other_attribute", 100500) >>> delattr(noop, "some_other_attribute") 27 / 35
  • 31. “Магические” методы: операторы сравнения • Чтобы экземпляры класса поддерживали все операторы сравнения, нужно реализовать внушительное количество “магических” методов: instance.__eq__(other) # instance == other instance.__ne__(other) # instance != other instance.__lt__(other) # instance < other instance.__le__(other) # instance <= other instance.__gt__(other) # instance > other instance.__ge__(other) # instance >= other • В уже знакомом нам модуле functools есть декоратор, облегчающий реализацию операторов сравнения: >>> import functools >>> @functools.total_ordering ... class Counter: ... def __eq__(self, other): ... return self.value == other.value ... ... def __lt__(self, other): # или <=, >, >= ... return self.value < other.value 28 / 35
  • 32. “Магический” метод __call__ • Метод __call__ позволяет “вызывать” экземпляры классов, имитируя интерфейс фнукций: >>> class Identity: ... def __call__(self, x): ... return x ... >>> Identity()(42) 42 • Как это можно использовать? 29 / 35
  • 33. Декораторы с аргументами на основе классов >>> class trace: ... def __init__(self, handle): ... self.handle = handle ... ... def __call__(self, func): ... @functools.wraps(func) ... def inner(*args, **kwargs): ... print(func.__name__, args, kwargs, ... file=self.handle) ... return func(*args, **kwargs) ... return inner ... >>> @trace(sys.stderr) ... def identity(x): ... return x ... >>> identity(42) identity (42,) {} 42 30 / 35
  • 34. “Магические” методы для преобразования в строку • Напоминание: в Python есть две различных по смыслу функции для преобразования объекта в строку: repr и str. • Для каждой из них существует одноимённый “магический” метод: >>> class Counter: ... def __init__(self, initial=0): ... self.value = initial ... ... def __repr__(self): ... return "Counter({})".format(self.value) ... ... def __str__(self): ... return "Counted to {}".format(self.value) ... >>> c = Counter(42) >>> c 'Counter(42)' >>> print(c) Counted to 42 31 / 35
  • 35. Расширение format через “магические” методы >>> class Counter: ... def __init__(self, initial=0): ... self.value = initial ... ... def __format__(self, format_spec): ... return self.value.__format__(format_spec) ... >>> c = Counter(42) >>> "Counted to {:b}".format(c) 'Counted to 101010' 32 / 35
  • 36. “Магический” метод __hash__ • Метод __hash__ используется для вычисления значения хеш-функции. • Реализация по умолчанию гарантирует, что одинаковое значение хеш функции будет только у физически одинаковых объектов, то есть: x is y <=> hash(x) == hash(y). • Несколько очевидных рекомендаций: • Метод __hash__ имеет смысл реализовывать только вместе с методом __eq__. При этом реализация __hash__ должна удовлетворять: x == y => hash(x) == hash(y) • Для изменяемых объектов можно ограничиться только методом __eq__. 33 / 35
  • 37. “Магический” метод __bool__ • Метод __bool__ для проверки значения на истинность, например в условии оператора if. • Для класса Counter реализация __bool__ тривиальна: >>> class Counter: ... def __init__(self, initial=0): ... self.value = initial ... ... def __bool__(self): ... return bool(self.value) ... >>> c = Counter() >>> if not c: ... print("No counts yet.") ... No counts yet. 34 / 35
  • 38. “Магические” методы: резюме • “Магические” методы позволяют уточнить поведение экземпляров класса в различных конструкциях языка. • Например, с помощью магического метода __str__ можно указать способ приведения экземпляра класса, а с помощью метода __hash__ — алгоритм хеширования состояния класса. • Мы рассмотрели небольшое подмножество “магических” методов, на самом деле их много больше: практически любое действие с экземпляром можно специализировать для конкретного класса. 35 / 35