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
[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
значи няколко неща:
type(x)
връща типа на xtype
type
# обекти
>>> 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
>>> 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
- просто клас.
name
- име на новия класbases
- tuple
с базовите му класовеdict
- речник с полетата му (не по-различно от __dict__)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)
Не ползвайте eval().