【问题标题】:Decorator that can take both init args and call args?可以同时接受 init args 和 call args 的装饰器?
【发布时间】:2011-02-19 19:52:39
【问题描述】:

是否有可能创建一个装饰器,它可以是__init__'d 并带有一组参数,然后使用其他参数调用方法?

例如:

from foo import MyDecorator

bar = MyDecorator(debug=True)

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

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

如果这可能的,你能提供一个可行的例子吗?

【问题讨论】:

  • 请问你想完成什么?我真的很好奇。

标签: python design-patterns decorator


【解决方案1】:

为此,您需要另一个级别的包装,例如使用闭包:

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

【讨论】:

    【解决方案2】:

    当然,装饰器只是一个接受一个函数并返回一个函数的函数。没有理由函数不能是(或者,如果你有参数,不能被返回)实例方法。这是一个非常微不足道的例子(因为我不确定你到底想用这个做什么):

    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
    

    就像我说的,我想不出一个用例来解决这个问题。但我确定他们就在那里。

    【讨论】:

    • 第一个抱怨我在暗示函数和实例方法是同一件事的人得到了一个馅饼;-)
    • 好吧,装饰器必须是callable,它接受一个callable 并返回一些东西——但通常这些东西也应该是可调用的,否则你最终会让任何读者感到困惑。反正我有馅饼吗? ;-)
    【解决方案3】:

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

    这个比OP的例子稍微复杂一点,因为有两个层次的装饰,但是原理是一样的。

    【讨论】:

      【解决方案4】:

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

      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
      

      【讨论】:

        猜你喜欢
        • 2018-08-28
        • 2020-07-15
        • 2013-09-25
        • 1970-01-01
        • 1970-01-01
        • 2021-05-16
        • 1970-01-01
        • 1970-01-01
        • 2022-06-11
        相关资源
        最近更新 更多