36
Магия метаклассов Андрей Захаревич

Магия метаклассов

Embed Size (px)

Citation preview

Page 1: Магия метаклассов

Магия метаклассовАндрей Захаревич

Page 2: Магия метаклассов

Что такое объект• поля • методы

>>> foo = Foo() >>> foo.bar 'bar' >>> foo.baz() 'baz' >>> print foo <__main__.Foo object at 0x109e48a10>

Page 3: Магия метаклассов

Что такое класс

class Foo(object): bar = 'bar' def baz(self): return 'baz'

Page 4: Магия метаклассов

Что такое классКласс — тоже объект

>>> print Foo <class '__main__.Foo'> >>> print hasattr(Foo, 'new_attribute') False >>> Foo.new_attribute = 'foo' >>> print hasattr(Foo, 'new_attribute') True >>> print Foo.new_attribute 'foo'

Page 5: Магия метаклассов

Класс — это объект>>> def choose_class(name): ... if name == 'foo': ... class Foo(object): ... pass ... return Foo ... else: ... class Bar(object): ... pass ... return Bar ... >>> MyClass = choose_class('foo') >>> print MyClass <class '__main__.Foo'> >>> print MyClass() <__main__.Foo object at 0x89c6d4c>

Page 6: Магия метаклассов

Не очень динамично?

Page 7: Магия метаклассов

Функция type

>>> print type(1) <type 'int'> >>> print type('1') <type 'str'> >>> print type(Foo) <type 'type'> >>> print type(Foo()) <class '__main__.Foo'>

Определение типа…

Page 8: Магия метаклассов

Функция type

type(<имя класса>, <кортеж родительских классов>, # для наследования, может быть пустым <словарь, содержащий атрибуты и их значения>)

…и не только

Page 9: Магия метаклассов

Функция typeclass MyShinyClass(object): pass

или же так

>>> MyShinyClass = type('MyShinyClass', (), {}) >>> print MyShinyClass <class '__main__.MyShinyClass'> >>> print MyShinyClass() <__main__.MyShinyClass object at 0x109ee3290>

Page 10: Магия метаклассов

Посложнее>>> class Foo(object): ... bar = True

>>> Foo = type('Foo', (), {'bar':True})

>>> print Foo <class '__main__.Foo'> >>> print Foo.bar True >>> f = Foo() >>> print f <__main__.Foo object at 0x8a9b84c> >>> print f.bar True

Page 11: Магия метаклассов

Наследуемся

>>> class FooChild(Foo): ... pass

>>> FooChild = type('FooChild', (Foo,), {})

>>> print FooChild <class '__main__.FooChild'> >>> print FooChild.bar True

Page 12: Магия метаклассов

Добавим метод>>> def echo_bar(self): ... print self.bar

>>> FooChild = type('FooChild', (Foo,), ... {'echo_bar': echo_bar}) >>> hasattr(Foo, 'echo_bar') False >>> hasattr(FooChild, 'echo_bar') True >>> my_foo = FooChild() >>> my_foo.echo_bar() True

Page 13: Магия метаклассов

Что такое метакласс

• Класс создает объект • Класс — тоже объект • Метакласс создает класс

MyClass = MetaClass() MyObject = MyClass()

Page 14: Магия метаклассов

type — метакласс

MyClass = type('MyClass', (), {})

Page 15: Магия метаклассов

Все — объект

>>> 42.__class__ <type 'int'> >>> 'Hello world'.__class__ <type 'str'> >>> def foo(): pass >>> foo.__class__ <type 'function'> >>> class Bar(object): pass >>> b = Bar() >>> b.__class__ <class '__main__.Bar'>

Page 16: Магия метаклассов

Класс класса

>>> 42.__class__.__class__ <type 'type'> >>> 'Hello world'.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>

Page 17: Магия метаклассов

Атрибут __metaclass__

Приоритеты атрибута __metaclass__:

1. Класс 2. Родительские классы 3. Модуль (только для классов без родителя) 4. type

class Foo(object): __metaclass__ = something... [...]

Page 18: Магия метаклассов

Метакласс-функция

def upper_attr(future_cls_name, future_cls_parents, future_cls_attr): attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

return type(future_cls_name, future_cls_parents, attrs)

Page 19: Магия метаклассов

Метакласс-функция

class Foo(object): __metaclass__ = upper_attr bar = 'bar'

foo = Foo() print hasattr(foo, 'bar') # False print hasattr(foo, 'BAR') # True print foo.BAR # bar

Page 20: Магия метаклассов

А теперь классclass UpperAttrMetaclass(type): def __new__(up_metacls, future_cls_name, future_cls_parents, future_cls_attr):

attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

return type(future_cls_name, future_cls_parents, attrs)

Page 21: Магия метаклассов

А теперь класс

class UpperAttrMetaclass(type): def __new__(up_metacls, future_cls_name, future_cls_parents, future_cls_attr):

attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

return type.__new__(up_metacls, future_cls_name, future_cls_parents, attrs)

Page 22: Магия метаклассов

А теперь класс

class UpperAttrMetaclass(type): def __new__(mcs, name, bases, dct):

attrs = {} for name, val in dct.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

return super(UpperAttrMetaclass, mcs).\ __new__(mcs, name, bases, attrs)

Page 23: Магия метаклассов

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

В обычном классе:

• __new__(cls, *args, **kwargs) — создает инстанс объекта класса

• __init__(self, *args, **kwargs) — инициализирует инстанс объект класса

• __call__(self, *args, **kwargs) — вызывается при попытке вызвать инстанс класса

Page 24: Магия метаклассов

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

В метаклассе:

• __new__(mms, name, bases, dct) — создает объект-класс

• __init__(cls, name, bases, dct) — инициализирует объект-класс

• __call__(cls, *args, **kwargs) — вызывается при попытке создать инстанс класса

Page 25: Магия метаклассов

Переопределение __call__

instance = constructor()

instance = MyClass()

Page 26: Магия метаклассов

Переопределение __call__class MetaSingleton(type): instance = None def __call__(cls, *args, **kwargs): if cls.instance is None: cls.instance = super(MetaSingleton,cls).\ __call__(*args, **kwargs) return cls.instance

class Foo(object): __metaclass__ = MetaSingleton

a = Foo() b = Foo() assert a is b

Page 27: Магия метаклассов

А что в python3?

1. Определить нужный метакласс

2. Подготовить пространство имен

3. Выполнить тело класса

4. Создать объект-класс

Page 28: Магия метаклассов

Определение метакласса

class Meta(type): pass

class MyClass(metaclass=Meta): pass

• Метакласс передается параметром, а не атрибутом класса

• Метаклассов уровня модуля больше нет

Page 29: Магия метаклассов

Подготовка пространства имен

def __prepare__(mcs, cls, bases, **kwargs): return dict()

Возвращает dict-like объект, в который будут записаны атрибуты класса при интерпретации его тела.

Дополнительные параметры берутся из заголовка

class MyClass(metaclass=Meta, **kwargs):

Page 30: Магия метаклассов

Выполнение тела класса

exec(body, globals(), namespace)

namespace — то, что вернула функция __prepare__. Если её нет, используется обычный dict.

Page 31: Магия метаклассов

Создание класса

def __new__(cls, name, bases, namespace, **kwargs): return type(name, bases, namespace)

Вместо словаря атрибутов мы получаем тот dict-like объект, который создали в __prepare__.

Дополнительные параметры те же, что в __prepare__.

Page 32: Магия метаклассов

Зачем это все?

Page 33: Магия метаклассов

Скрываем сложности

• ORM

• Учет создаваемых объектов

• Проверка классов

• Прочая магия

Page 34: Магия метаклассов

ORM

class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField()

guy = Person(name='Bob', age='35') print guy.age

Page 35: Магия метаклассов

Если не метаклассы, то что?

• Декораторы классов

• Monkey-patching

Page 36: Магия метаклассов

Спасибо за внимание