11. Метапрограмиране

С 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

Data Model (aka MOP)

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
        
             File "", line 1, in 
        
        AttributeError: 'Person' object has no attribute 'name'
    

Класът Ninja

         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__

__new__ е истинският конструктор на вашите обекти. __init__ е само инициализатор

        

             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:

  1. проверява се дали name не присъства в obj.__dict__. Ако да - връща се тази стойност
  2. ако не - проверява се дали класът, obj.__class__ има такъв атрибут в своя __dict__. Ако да, и той няма метод __get__, се връща
  3. ако атрибута на 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

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())
         (, , )

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

         # връзката
         >>> 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()
        
             File "", line 1, in 
        
        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()
        
             File "", line 1, in 
        
        AttributeError: 'Foo' object has no attribute 'bar'

        >>> Foo.bar()
        

        >>> 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().

Връзки

Още въпроси?