Pull to refresh

Заметки о метапрограммировании в Python

Reading time 2 min
Views 6.9K
По мере накопления опыта программирования на одном языке, все мы нарабатываем стандартные для себя приемы программирования. Чем выше наш опыт, тем больше количество, более разносторонни методы их работы. При переходе на другой язык, мы стараемся их воспроизвести. Иногда, такое бывает что часть из них неактуальна или неэффективна. Большую часть времени от изучения языка отнимают новые несвойственные прежним нам, приемы. Я хотел бы отметить некоторые особенности Python, которые для меня были не очевидны поначалу освоения.

По большому счету в питоне все объекты хранятся в словарях(dict).Словарь — есть список объектов, которому противопоставлен ключ, уникальный на весь словарь.

class A(): i=1; s='string'
print dir(A)

> ['__doc__', '__module__', 'i', 's']


Это свойство, позволяет «вытворять» с классами и экземплярами классов разнообразные вещи. Например:

class A(): v0=1
class B(A):
    v1=2
    def __init__(self):
        self.v2 = 3

b=B()
print b.v2, b.v1, b.v0

> 3 2 1


Когда Python пытался достать v2 все ясно — оно получено из словаря экземпляра b. Далее интересней, свойство v1, уже не содержит элемент b, его содержит словарь B — питон взял его оттуда. И, наконец, при поиске элемента v0 обращаемся к словарю родителю B-A, и достаем его оттуда. Понятно что если определить в пределах b свойство v0, то его значение будет получено оттуда, не поднимаясь на более высокие уровни.

def __init__(self):
    print self

class A(): pass
setattr(A, '__init__', __init__)

a=A()

> <__main__.A instance at 0x009E9EB8>



В этом примере, мы переместили функцию __init__ в пределы класса A(мы могли переместить функцию с любым другим именем, __init__ приведена для наглядности). Декларация класса A эквивалентна:

class A():
    def __init__(self):
        print self



Классы в питоне есть экземпляры метаклассов. Это, есть неожиданность, потому как теперь вам позволено «на лету» определять классы, а не только их экземляры.

def __init__(self):
    print self

B = type('B', (), {})
setattr(B, '__init__', __init__)

b = B()


> <__main__.B object at 0x009E7CD0>



Заметьте, что функция определена лишь для экземпляров класса, вызвать её для самого класса A нельзя. С другой стороны, что нам мешает эту возможность добавить.

def Omd(obj):
    print obj

B = type('B', (), {})
setattr(B, 'c', classmethod(Omd))
setattr(B, 'o', Omd)

b = B()
b.c(), b.o()


> <class '__main__.B'>
> <__main__.B object at 0x009E7D50>

Весело правда?! Определили фукнцию, которая печатает первый свой аргумент. Далее создали класс, на основе метакласса (можно было его определить и стандартно, как class A(type): pass). Дальше классу, добавили статическую функцию «с», под которой скрывается наша, и функцию «о» для вызова её от экземпляра класса B. Смотрим, что получается, первым мы видим что печатается класс, а после него экземпляр. Вот такая вот у них любовь.
Tags:
Hubs:
+34
Comments 34
Comments Comments 34

Articles