Metaprogramming is the language feature that helps you write code that you won't be able to understand once the cocaine wears off.
Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects.
class Person: def __init__(self, name): self.name = name def say_hi(self): print("Hi, I am", self.name)
Обектите отвътре са просто речници. Всеки обект си има специален речник който пази атрибутите му:
>>> goshko = Person('Gospodin Goshko') >>> hasattr(goshko, '__dict__') True >>> goshko.__dict__ {'name': 'Gospodin Goshko'} >>> goshko.__dict__['profession'] = 'Hacker' >>> goshko.profession 'Hacker' >>> goshko.__dict__ {'profession': 'Hacker', 'name': 'Commander Gosh'} >>> goshko.__dict__.clear() >>> goshko.name Traceback (most recent call last): File "", line 1, in AttributeError: 'Person' object has no attribute 'name'
class Ninja: def __init__(self, name, target): self.name = name self.target = target def say_hi(self): print("Ninja!") def kill_target(self): print("Slash ", self.target)
>>> goshko = Person('Gospodin Goshko') >>> goshko.say_hi() Hi, I am Gospodin Goshko >>> type(goshko) <class '__main__.Person'> >>> goshko.__class__ <class '__main__.Person'> >>> goshko.__class__ = Ninja >>> type(goshko)>>> goshko.say_hi() Ninja! >>> goshko.kill_target() Traceback (most recent call last): File " ", line 1, in File " ", line 4, in kill_target AttributeError: 'Ninja' object has no attribute 'target'
object.__new__(cls[, ...]) object.__init__(self[, ...])
__new__
е истинският конструктор на вашите обекти. __init__
е само инициализатор:
class Vector(tuple): def __new__(klass, x, y): return tuple.__new__(klass, (x, y)) def __add__(self, other): if not isinstance(other, Vector): return NotImplemented return Vector(self[0] + other[0], self[1] + other[1])
object.__getattr__(self, name) object.__setattr__(self, name, value) object.__delattr__(self, name) object.__dir__(self)
object.__getattribute__(self, name)
При извикване на obj.name
:
name
не присъства в obj.__dict__
. Ако да - връща се тази стойностobj.__class__
има такъв атрибут в своя __dict__
. Ако да, и той няма метод __get__
, се връщаobj.__dict__
има метод __get__
, то методът obj.__dict__.__get__
се изпълнява със съответните оргументиobject.__get__(self, instance, owner) object.__set__(self, instance, value) object.__delete__(self, instance)
# direct call x.__get__(a) # instance binding on a.x type(a).__dict__['x'].__get__(a, type(a)) # class binding on A.x A.__dict__['x'].__get__(None, A) # super binding # super black magic
Метаобектният протокол на 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()
.