SlideShare a Scribd company logo
1 of 36
Download to read offline
Магия метаклассов
Андрей Захаревич
Что такое объект
• поля
• методы
>>> 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(<имя класса>,
<кортеж родительских классов>,
# для наследования, может быть пустым
<словарь, содержащий атрибуты и их значения>)
…и не только
Функция type
class 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
Спасибо за внимание

More Related Content

What's hot

Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Vasya Petrov
 
Быстрые конструкции в 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
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)Smolensk Computer Science Club
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.Roman Brovko
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности PythonPyNSK
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Sergey Schetinin
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.Roman Brovko
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Roman Brovko
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.Roman Brovko
 
Работа с БД в Drupal 7
Работа с БД в Drupal 7Работа с БД в Drupal 7
Работа с БД в Drupal 7Eugene Fidelin
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)Ontico
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Roman Brovko
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Roman Brovko
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Roman Brovko
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)Evgeny Kaziak
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.Roman Brovko
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6Dmitry Soshnikov
 

What's hot (20)

Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1Подробная презентация JavaScript 6 в 1
Подробная презентация JavaScript 6 в 1
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Charming python sc2-8
Charming python sc2-8Charming python sc2-8
Charming python sc2-8
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
 
Javascript
JavascriptJavascript
Javascript
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование
 
Лекция 10. Классы 2.
Лекция 10. Классы 2.Лекция 10. Классы 2.
Лекция 10. Классы 2.
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Работа с БД в Drupal 7
Работа с БД в Drupal 7Работа с БД в Drupal 7
Работа с БД в Drupal 7
 
SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)SQL Tricky (Иван Фролков)
SQL Tricky (Иван Фролков)
 
Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.Лекция 12. Быстрее, Python, ещё быстрее.
Лекция 12. Быстрее, Python, ещё быстрее.
 
Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.Лекция 9. Модули, пакеты и система импорта.
Лекция 9. Модули, пакеты и система импорта.
 
Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.Лекция 3. Декораторы и модуль functools.
Лекция 3. Декораторы и модуль functools.
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6DevConf. Дмитрий Сошников - ECMAScript 6
DevConf. Дмитрий Сошников - ECMAScript 6
 
Decorators' recipes
Decorators' recipesDecorators' recipes
Decorators' recipes
 

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