Декоратор, который может принимать как аргументы инициализации, так и аргументы вызова? - PullRequest
2 голосов
/ 15 мая 2010

Можно ли создать декоратор, который может быть __init__ 'с набором аргументов, а затем иметь методы, вызываемые с другими аргументами?

Например:

from foo import MyDecorator

bar = MyDecorator(debug=True)

@bar.myfunc(a=100)
def spam():
    pass

@bar.myotherfunc(x=False)
def eggs():
    pass

Если это возможно , вы можете привести рабочий пример?

Ответы [ 4 ]

4 голосов
/ 15 мая 2010

Для этого вам нужен еще один уровень переноса, например, с помощью замыканий:

import functools

def say_when_called(what_to_say):
    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kw):
            print what_to_say
            return fn(*args, **kw)
        return wrapper
    return decorator

@say_when_called("spam")
def my_func(v):
    print v

my_func("eggs")

Выход:

spam
eggs

(см. http://codepad.org/uyJV56gk)

Обратите внимание, что я использовал functools.wraps, чтобы оформленная функция выглядела как оригинал. Это не требуется функционально, но хорошо, если код читает атрибуты __name__ или __doc__ вашей функции.

Пример на основе классов:

class SayWhenCalledWrapper(object):

    def __init__(self, fn, what_to_say):
        self.fn = fn
        self.what_to_say = what_to_say

    def __call__(self, *args, **kw):
        print self.what_to_say
        return self.fn(*args, **kw)


class SayWhenCalled(object):

    def __init__(self, what_to_say):
        self.what_to_say = what_to_say

    def __call__(self, fn):
        return SayWhenCalledWrapper(fn, self.what_to_say)


@SayWhenCalled("spam")
def my_func(v):
    print v

my_func("eggs")

Выход:

spam
eggs

(см. http://codepad.org/6Y2XffDN)

3 голосов
/ 15 мая 2010

Конечно, декоратор - это просто функция, которая принимает функцию и возвращает функцию. Нет причины, по которой функция не может быть (или, если у вас есть аргументы, не может быть возвращена) методом экземпляра. Вот очень простой пример (потому что я не уверен, что именно вы пытаетесь сделать с этим):

class MyDecorator(object):
    def __init__(self, debug):
        self.debug = debug
    def myfunc(self, a):
        def decorator(fn):
            def new_fn():
                if self.debug:
                    print a
                fn()
            return new_fn
        return decorator
    def myotherfunc(self, x):
        def decorator(fn):
            def new_fn():
                if self.debug:
                    print x
                fn()
            return new_fn
        return decorator

Как я уже сказал, я не могу придумать вариант использования этого для моей головы. Но я уверен, что они там.

0 голосов
/ 15 мая 2010

Хуин ответил очень хорошо. Его две опции выполняют код декоратора только тогда, когда определена декорированная функция (это не критика, а часто именно то, что вы хотите). Вот расширение его подхода на основе классов, который также выполняет некоторый код каждый раз, когда вы вызываете функцию. Это то, что вы делаете, например, когда используете декораторы для обеспечения безопасности потоков.

class MyInnerDecorator:
    def __init__( self, outer_decorator, *args, **kwargs ):
        self._outerDecorator = outer_decorator
        self._args = args
        self._kwargs = kwargs

    def __call__( self, f ):
        print "Decorating function\n"
        self._f = f
        return self.wrap


    def wrap( self, *args, **kwargs ):
        print "Calling decorated function"
        print "Debug is ",                          self._outerDecorator._debug
        print "Positional args to decorator: ",     self._args
        print "Keyword args to decorator: ",        self._kwargs
        print "Positional args to function call: ", args
        print "Keyword args to function call: ",    kwargs
        return self._f( *args, **kwargs )
        print "\n"



class MyDecorator:
    def __init__( self, debug ):
        self._debug = debug

    def myFunc( self, *args, **kwargs ):
        return MyInnerDecorator( self, "Wrapped by myFunc", *args, **kwargs )

    def myOtherFunc( self, *args, **kwargs ):
        return MyInnerDecorator( self, "Wrapped by myOtherFunc", *args, **kwargs )


bar = MyDecorator( debug=True )
@bar.myFunc( a=100 )
def spam( *args, **kwargs ):
    print "\nIn spam\n"

@bar.myOtherFunc( x=False )
def eggs( *args, **kwargs ):
    print "\nIn eggs\n"

spam( "penguin" )

eggs( "lumberjack" )

Что выводит это:

Decorating function

Decorating function

Calling decorated function
Debug is  True
Positional args to decorator:  ('Wrapped by myFunc',)
Keyword args to decorator:  {'a': 100}
Positional args to function call:  ('penguin',)
Keyword args to function call:  {}

In spam

Calling decorated function
Debug is  True
Positional args to decorator:  ('Wrapped by myOtherFunc',)
Keyword args to decorator:  {'x': False}
Positional args to function call:  ('lumberjack',)
Keyword args to function call:  {}

In eggs
0 голосов
/ 15 мая 2010

Декоратор свойств выглядит примерно так. @property декорирует функцию и заменяет ее объектом, имеющим функции получения, установки и удаления, которые также являются декораторами.

Это немного сложнее, чем в примере ОП, потому что есть два уровня оформления, но принцип тот же.

...