2010-05-15 42 views
2

是否有可能创建一个装饰器,它可以是带有一组参数的__init__'d,然后以其他参数调用方法?装饰器,可以采取初始参数和调用参数?

例如:

from foo import MyDecorator 

bar = MyDecorator(debug=True) 

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

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

如果可能的,你可以提供一个工作的例子吗?

+2

请问你有什么要完成?我很好奇。 – 2010-05-15 08:09:19

回答

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 

,我想不出用例的这个假顶我的头。但我确定他们在外面。

+0

第一个抱怨我隐含的函数和实例方法是一样的东西在脸上得到一个馅饼;-) – 2010-05-15 08:25:36

+0

好吧,一个装饰器必须是一个'可调用',需要一个'callable'并返回一些东西 - 但通常也应该调用某些东西,否则你最终会混淆读者。反正我得到一块馅饼? ;-) – 2010-05-15 15:17:37

+0

华夫饼怎么样? – 2010-05-15 23:31:30

0

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

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

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

0

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