Метапрограмиране'

„ Програмиране с Python“, ФМИ

2010/05/10

С 22 думи

Metaprogramming is the language feature that helps you write code that you won't be able to understand once the cocaine wears off.

— twitter.com/bos31337

Предишният път

Метаобектният протокол на Python

Tim Peters on metaclasses

[Metaclasses] are deeper magic than 99% of the users should ever worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

— Tim Peters

Метакласове

Забележка

В Пайтън type значи няколко неща:

Type „help“

# обекти
>>> type(42), type("42"), type(object())
(<class 'int'>, <class 'str'>, <class 'object'>)

# типове
>>> type(int), type(str), type(object)
(<class 'type'>, <class 'type'>, <class 'type'>)

# връзката
>>> issubclass(int, type)
False
>>> isinstance(int, type)
True

Help more

>>> issubclass(object, type)
False
>>> isinstance(object, type)
True

>>> issubclass(type, object)
True
>>> issubclass(object, type)
False

>>> isinstance(type, object)
True # естествено
>>> isinstance(type, type)
True # втф?!

Типът на всички типове

type разбира се. Инстанции на type създавате с:
type(name, bases, dict)

Какво е инстанция на type - просто клас.

Нинджа

def човек_инициализирай(self, name):
    self.name = name

def човек_кажи_здрасти(self):
    print("Здрасти, аз съм", self.name)

Човек = type( 'Човек', (), {
            '__init__' : човек_инициализирай,
            'кажи_здрасти': човек_кажи_здрасти }
        )

Човек('¡Испанска нинджа!').кажи_здрасти()

Как указваме метаклас

class Foo(A, B, C, metaclass=Bar):
    pass

Просто синтактична захар

class Foo(A, B, C, metaclass=Bar):
    x = 1
    y = 2

# е захар за (почти)

Foo = Bar('Foo', (A, B, C), {'x':1, 'y':2})

Простичко

class metacls(type):
    def __new__(mcs, name, bases, dict):
        dict['foo'] = 'metacls was here'
        return type.__new__(mcs, name, bases, dict)

Един пример

class R(metaclass=ReverseNames):
    def forward(self):
        print('forward')
>>> r = R()
>>> r.forward()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'R' object has no attribute 'forward'
>>> r.drawrof()
forward

Как?

class ReverseNames(type):
    def __new__(klass, name, bases, _dict):
        reversed = [(k[::-1], v) for k, v in _dict.items()]
        return type.__new__(klass, name, bases, dict(reversed))

Атрибути в метакласа

class Meta(type):
    def bar(self):
        print(self)
class Foo(metaclass=Meta):
    pass
>>> f = Foo()
>>> f.bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'bar'

>>> Foo.bar()
<class '__main__.Foo'>

>>> Meta.bar()
# ???

Самоотвержена Нинджа

class Person(metaclass=selfless):
    def __init__(name):
        self.name = name

    def say_hi():
        print("Hi, I am", self.name)

Person("忍者").say_hi()

Самоотвежен питон

def without_ego(func):
    def wrapped(self, *args, **kwargs):
        old_self = func.__globals__.get('self')
        func.__globals__['self'] = self
        result = func(*args, **kwargs)
        func.__globals__['self'] = old_self
        return result
    wrapped.__name__ = func.__name__
    return wrapped

class selfless(type):
    def __new__(cls, name, bases, attrs):
        for key, value in attrs.items():
            if not hasattr(value, '__call__'): continue
            attrs[key] = without_ego(value)
        return type.__new__(cls, name, bases, attrs)

2eval|!2eval?

Не ползвайте eval().

Връзки

Още въпроси?