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

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

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

13 април 2016

Преди това

sentence = 'we are humans'
matched = re.match(r'(.*) (.*?) (.*)', sentence)
print(matched.groups())

('we', 'are', 'humans')

Ами това?

sentence = 'we are humans'
matched = re.match(r'(.*) (.*?) (.*)', sentence)
print(matched.group(2))

are

А това?

sentence = 'we are humans'
matched = re.match(r'(.*) (.*?) (.*)', sentence)
print(matched.group())

we are humans

Последно

sentence = 'horses are fast'
regex = re.compile('(?P<animal>\w+) (?P<verb>\w+) (?P<adjective>\w+)')
matched = re.search(regex, sentence)
print(matched.groupdict())

{'adjective': 'fast', 'animal': 'horses', 'verb': 'are'}

Ако искате още

https://regexcrossword.com/

Преговор: атрибути

dir(foo) -> foo.__dict__

getattr(foo, 'x' -> foo.__getattribute__('x') -> foo.__getattr__('x')

setattr(foo, 'x', 'y') -> foo.__setattr__('x', 'y')

del foo.x -> delattr(foo, 'x') -> foo.__delattr__('x')

Дескриптори: Теория

def __get__(self, instance, owner): ...
def __set__(self, instance, value): ...
def __delete__(self, instance): ...

Дескриптори: Практика

class B:
    def __get__(self, instance, owner):
        return "You came to the wrong neighborhood, motherflower!"

    def __set__(self, instance, value):
        print("What!? You think you can change my personality just like that!?")

    def __delete__(self, instance):
        print("Can't touch me!")

class A:
    foo = B()

a = A()
print(a.foo)
a.foo = 'bar'
del a.foo

Bound methods

>>> increment = (1).__add__
>>> map(increment, [0, 1, 2, 3])
[1, 2, 3, 4]

Bound methods: Проста имплементация!

class MyMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance:
            return lambda: self.func(instance)
        else:
            return lambda explicit_instance: self.func(explicit_instance)

class Python:
    name = 'Monty'
    greet = MyMethod(lambda self: 'My name issss %s' % self.name)

Bound methods: Проста имплементация!

snake = Python()
snake.greet() # 'My name issss Monty'
snake.name = 'Nagini'
Python.greet() # TypeError: <lambda>() takes exactly 1 argument (0 given)
Python.greet(snake) # 'My name issss Nagini'

Още достъп до атрибути

Редът за обхождане на базови класове

class A(int): pass

class B: pass

class C(A, B, int): pass

C.__mro__
# <class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,
# <class 'int'>, <class 'object'>

Всичко за достъп до атрибути

за пълната информация...

Кодът на object в Python

And now for something completely different

Как бихме могли да имплементираме клас Pair, наследник на tuple?

class FaultyPair(tuple):
    def __init__(self, a, b):
        self[0] = a
        self[1] = b

class FaultierPair(tuple):
    def __init__(self, a, b):
        self = (a, b)

В __init__ не можем да постигнем нищо

Конструктори

Помните ли как ви повторихме няколко пъти, че __init__ не е точно конструктора на всеки клас?

__new__

class LessFaultyPair(tuple):
    def __new__(cls, x, y):
         return (x, y)

type(LessFaultyPair(1, 2)) # tuple

Но ние не искахме точно това...

__new__ (2)

class Pair(tuple):
    def __new__(cls, x, y):
         return tuple.__new__(cls, (x, y))

type(Pair(1, 2)) # Pair

Мета класове

Shit just got real

Мета класове

type

Помните ли как ви казахме, че type може само да връща типа на обект?

We lied!

Забележка

В Python type значи няколко неща

Типът на типовете

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

type(name, bases, dict)

Начини да направим клас

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

Долното е синтактична захар за горното...

class 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_)

Foo = metacls('Foo', (A, B, C), {'x':1, 'y':2})
type(Foo)  # metacls
Foo.__dict__['foo']  # 'metacls was here'

Мета със захар

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

Един пример

class R(metaclass=ReverseNames):
    def forward(self):
        print('forward')

>>> r = R()
>>> r.forward()
AttributeError: 'R' object has no attribute 'forward'
>>> r.drawrof()
forward

dafuq!?

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

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

Bryan O'Sullivan on metaprogramming

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

— Bryan O'Sullivan (@bos31337) May 4, 2010

Въпроси?