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) връща типа на xtypetype# обекти
>>> 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().