Декораторы и в классе - PullRequest
3 голосов
/ 01 апреля 2009

Есть ли способ написать декораторы в структуре классов, которые хорошо вкладываются? Например, это прекрасно работает без классов:

def wrap1(func):
    def loc(*args,**kwargs):
        print 1
        return func(*args,**kwargs)
    return loc

def wrap2(func):
    def loc(*args,**kwargs):
        print 2
        return func(*args,**kwargs)
    return loc


def wrap3(func):
    def loc(*args,**kwargs):
        print 3
        return func(*args,**kwargs)
    return loc

def merger(func):
    return wrap1(wrap2(wrap3(func)))


@merger
def merged():
    print "merged"


@wrap1
@wrap2
@wrap3
def individually_wrapped():
    print "individually wrapped"

merged()
individually_wrapped()

Вывод:

1
2
3
merged
1
2
3
individually wrapped

что я и хочу. Но теперь давайте скажем, что я хочу сделать merged и individually_wrapped как статические методы или методы класса. Это также будет работать, пока декораторы находятся вне пространства имен класса. Есть ли хороший способ поместить декораторы в пространство имен? Я бы предпочел не перечислять все способы, которые не будут работать, но основная проблема в том, что если merger является методом, он не может получить доступ к wrapX методам. Может быть, это глупая вещь, которую хочется делать, но кто-нибудь заставил работать что-то подобное со всеми декораторами и декорированными методами в одном классе?

Ответы [ 2 ]

5 голосов
/ 02 апреля 2009

«Есть ли хороший способ поместить декораторы в пространство имен?»

Нет веских причин для этого. У вас есть файлы модулей. Это аккуратный контейнер для класса и некоторых декораторов.

Вам никогда не нужны декораторы как методы класса - вы можете просто вызывать один метод из другого.

2 голосов
/ 02 апреля 2009

На самом деле, не должно быть проблем с размещением их всех внутри класса. Находясь внутри тела класса, вы можете назвать любую переменную, определенную до сих пор, просто назвав ее:

class A(object):
    a = 1
    b = 2
    c = a + b

print A.c

Это дает результат 3, потому что, пока Python выполняет тело класса, функции могут «видеть» объявленные переменные a и b. Таким образом, следующее также работает:

class B(object):
    @staticmethod
    def wrapper(*args, **kw):
       ...

    @wrapper
    def a(...):
        ...

А как насчет функции слияния? Проблема заключается в том, что функция-обертка запускается долго после , когда тело класса выполняется, а переменные, которые она определила, больше не находятся в области видимости. Как вы можете ссылаться на них, тогда? Используя имя класса в качестве префикса! Как это:

class C(object):
    @staticmethod
    def wrap1(*args, **kw):
        ...
    @staticmethod
    def wrap2(*args, **kw):
        ...
    @staticmethod
    def wrap3(*args, **kw):
        ...
    @staticmethod
    def merger(*args, **kw):
        C.wrap1(C.wrap2(C.wrap3(...)))

    @merger
    def plain(...):
        ...

Итак, общее правило для Python: код, который выполняется прямо на уровне тела класса, может говорить обо всех переменных и методах, которые были определены внутри него, и это означает, что вы можете использовать эти переменные класса как оберток. Но как только вы окажетесь «внутри» функции в классе, будь то функция класса, статическая функция или метод (и эта обертка является методом класса, поскольку она принимает аргументы, но не «self»), вам придется использовать имя класса, чтобы "получить" его содержимое.

...