可以同时接受init args和call args的装饰器?

时间:2010-05-15 07:58:19

标签: python design-patterns decorator

是否可以使用一组参数创建一个可以__init__'的装饰器,然后使用其他参数调用方法?

例如:

from foo import MyDecorator

bar = MyDecorator(debug=True)

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

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

如果 , 可以提供一个有效的例子吗?

4 个答案:

答案 0 :(得分:4)

您需要使用其他级别的包装,例如:

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

答案 1 :(得分:3)

当然,装饰器只是一个接受函数并返回函数的函数。没有理由不能将函数(或者,如果你有参数,不能返回)实例方法。这是一个非常简单的例子(因为我不确定你究竟要用这个做什么):

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

就像我说的那样,我无法想到这个问题的用例。但我确信他们在那里。

答案 2 :(得分:0)

property decorator有点像这样。 @property修饰一个函数,并用一个具有getter,setter和deleter函数的对象替换它,这些函数也是装饰器。

这比OP的例子稍微复杂一点,因为有两个级别的装饰,但原理是相同的。

答案 3 :(得分:0)

惠的回答非常好。他的两个选项只有在定义了装饰函数时才执行装饰器代码(这不是批评,因为这通常就是你想要的)。这是他基于类的方法的扩展,每次调用函数时也会执行一些代码。这是你做的事情,例如,当你使用装饰器来确保线程安全时。

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