Магические методы

Подробное описание: https://docs.python.org/3/reference/datamodel.html#special-method-names

Конструирование и инициализация

  • __init__(self, ...) - Инициализатор класса.
  • __del__(self) - "Деструктор" класса.

Пример:

In [1]:
from os.path import join


class FileObject:
    def __init__(self, filepath, filename):
        self.file = open(join(filepath, filename), 'r+')

    def __del__(self):
        self.file.close()
        del self.file

Методы сравнения

  • __cmp(self, other)__ - Метод должен возвращать отрицательное число если self < other, нуль в случае когда self == other, и поожительное число когда self > other.
  • __eq(self, other)__ - ==
  • __neq(self, other)__ - !=
  • __lt(self, other)__ - <
  • __gt(self, other)__ - >
  • __le(self, other)__ - <=
  • __ge(self, other)__ - >=

Числовые магические методы

Унарные операторы и функции

  • __pos__(self) - определяет поведение для унарного плюса
  • __neg__(self) - определяет поведение для унарного минуса
  • __abs__(self)
  • __round__(self, n), __floor__(self, n), __ceil__(self, n) - определяют поведение при вызове стандартного метода math.round(x),....

Обычные арифметические операторы

  • __add__(self, other), __sub__(self, other)
  • __mul__(self, other), __div__(self, other), __floordiv__(self, other), __mod__(self, other)
  • __lshift__(self, other), __rshift__(self, other)
  • __and__(self, other), __or__(self, other), __xor__(self, other)

Отражённые арифметические операторы

Было

{python}
self_object + other

Стало

{python}
other + self_object

Таким образом, все эти магические методы делают то же самое, что и их обычные версии, за исключением выполнения операции с other в качестве первого операнда и self в качестве второго. В большинстве случаев, результат отражённой операции такой же, как её обычный эквивалент, поэтому при определении __radd__ вы можете ограничиться вызовом __add__. Объект слева от оператора (other в примере) не должен иметь обычной неотражённой версии этого метода. Метод self_object.__radd__ будет вызван только если в other не определён __add__.

Пример:

  • __radd__(self, other)

Составное присваивание

Все теже методы, что и в списке обычных арифметических операторов. Пример:

  • __iadd__(self, other)

Преобразования типов

  • __int__(self)
  • __float__(self)
  • __oct__(self)

Callable objects

Для того чтобы можно было пользоваться объектом класса как функцией, нужно определить в классе магический метод

__call__(self, [args...]).

Итераторы

Объекты итераторы в питоне соответствуют протоколу итератора, то есть имеют два метода: __iter__() и __next__(). Метод __iter__ возвращает объект итератор, и неявно вызывается в начале цикла.

Метод __next__ возвращает следующее значение, и неявно вызывается во время каждой итерации цикла. Данный метод кидает StopIteration всякий раз, когда больше нет объектов, которые можно было бы вернуть.Исключение неявно ловится, для того чтобы прекратить итерирование.

Напоминание:

for x in xs:
    do_something(x)

Процесс исполнения оператора for можно концептуально записать так:

it = iter(xs)

while True:
    try:
        x = next(it)
    except StopIteration:
        break
    do_something(x)

Пример:

In [9]:
class inf_iterator:
    def __init__(self):
        self.counter = -1
        
    def __iter__(self):
        return self
    
    def __next__(self):
        self.counter += 1
        return self.counter
    
it = inf_iterator()
next(it)
Out[9]:
0
In [11]:
class counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def __next__(self):
        if self.current > self.high:
            raise StopIteration
        
        self.current += 1
        return self.current - 1
In [13]:
for c in counter(3, 8):
    print(c)
3
4
5
6
7
8

Генераторы

Генераторы позволяют писать функции, которые ведут себя как итераторы, то есть, в частности, могут быть использованы в циклах.

Пример использования:

In [14]:
def firstn(n):
    num, nums = 0, []
    while num < n:
        nums.append(num)
        num += 1
    return nums
In [15]:
def firstn(n):
    num = 0
    while num < n:
        yield num
        num += 1

Задание на пару:

In [16]:
class node:
    def __init__(self, l=None, r=None, x=None):
        self.key = x
        self.left = l
        self.right = r
        
def insert(root_node, x):
    raise NotImplementedError

def inorder_traversal(node):
    raise NotImplementedError

Менеджеры контекста

Реализация методов __enter__(self) и __exit__(self, exception_type, exception_value, traceback) позволяет использовать экземпляр класса с конструкцией with.

Пример:

class DatabaseConnection(object):

    def __enter__(self):
        # make a database connection and return it
        ...
        return self.dbconn

    def __exit__(self, exc_type, exc_val, exc_tb):
        # make sure the dbconnection gets closed
        self.dbconn.close()
        ...

использование

with DatabaseConnection() as mydbconn:
    # do stuff

Метаклассы

Открыть http://mit.spbau.ru/sewiki/images/3/34/Python_2014_05.pdf со слайда 4.

Примеры:

final

In [2]:
class final(type):
    def __init__(cls, name, bases, dct):
        super(final, cls).__init__(name, bases, dct)
        
        finals = [base.__name__ for base in bases if isinstance(base, final)]
        
        if len(finals) > 0:
            raise TypeError('Classes ' + str(finals) + ' are final')
In [3]:
class A(object): pass
class B(A, metaclass = final): pass
class C(object, metaclass = final): pass
In [4]:
class D(B, C): pass
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-18f21063fa63> in <module>()
----> 1 class D(B, C): pass

<ipython-input-2-246afdc1b14c> in __init__(cls, name, bases, dct)
      6 
      7         if len(finals) > 0:
----> 8             raise TypeError('Classes ' + str(finals) + 'are final')

TypeError: Classes ['B', 'C']are final

singleton

In [5]:
class singleton(type):
    instance = None
    
    def __call__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = super(singleton, cls).__call__(*args, **kwargs)
        return cls.instance

class Singleton(object, metaclass = singleton): pass

a = Singleton()
b = Singleton()
a is b
Out[5]:
True