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

Post on 19-Jan-2017

224 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

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

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

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

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

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

>>> 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'

Класс — это объект>>> 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>

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

Функция type

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

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

Функция type

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

…и не только

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

или же так

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

Посложнее>>> 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

Наследуемся

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

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

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

Добавим метод>>> 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

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

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

MyClass = MetaClass() MyObject = MyClass()

type — метакласс

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

Все — объект

>>> 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'>

Класс класса

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

Атрибут __metaclass__

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

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

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

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

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)

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

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

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

А теперь класс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)

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

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)

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

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)

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

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

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

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

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

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

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

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

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

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

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

instance = constructor()

instance = MyClass()

Переопределение __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

А что в python3?

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

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

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

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

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

class Meta(type): pass

class MyClass(metaclass=Meta): pass

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

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

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

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

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

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

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

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

exec(body, globals(), namespace)

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

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

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

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

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

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

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

• ORM

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

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

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

ORM

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

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

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

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

• Monkey-patching

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

top related