Python3:装饰器高级应用
![](/skin/images/mar_09.png)
![](/skin/images/mar_10.png)
![](/skin/images/mar_07.png)
![](/skin/images/mar_08.png)
在装饰器函数里传入参数
# 这不是什么黑魔法,你只需要让包装器传递参数:def a_decorator_passing_arguments(function_to_decorate): def a_wrapper_accepting_arguments(arg1, arg2): print("I got args! Look:", arg1, arg2) function_to_decorate(arg1, arg2) return a_wrapper_accepting_arguments# 当你调用装饰器返回的函数时,也就调用了包装器,把参数传入包装器里,# 它将把参数传递给被装饰的函数里.@a_decorator_passing_argumentsdef print_full_name(first_name, last_name): print("My name is", first_name, last_name)print_full_name("Peter", "Venkman")# 输出:# I got args! Look: Peter Venkman# My name is Peter Venkman装饰方法
在Python里方法和函数几乎一样.唯一的区别就是方法的第一个参数是一个当前对象的(self)
也就是说你可以用同样的方式来装饰方法!只要记得把self加进去:
def method_friendly_decorator(method_to_decorate): def wrapper(self, lie): lie = lie - 3 return method_to_decorate(self, lie) return wrapperclass Lucy(object): def __init__(self): self.age = 32 @method_friendly_decorator def sayYourAge(self, lie): print("I am %s, what did you think?" % (self.age + lie))l = Lucy()l.sayYourAge(1)#输出: I am 30, what did you think?如果你想造一个更通用的可以同时满足方法和函数的装饰器,用*args,**kwargs就可以了
def a_decorator_passing_arbitrary_arguments(function_to_decorate): # 包装器接受所有参数 def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs): print("Do I have args?:") print(args) print(kwargs) # 现在把*args,**kwargs解包 # 如果你不明白什么是解包的话,请查阅: # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/ function_to_decorate(*args, **kwargs) return a_wrapper_accepting_arbitrary_arguments@a_decorator_passing_arbitrary_argumentsdef function_with_no_argument(): print("Python is cool, no argument here.")function_with_no_argument()#输出#Do I have args?:#()#{}#Python is cool, no argument here.@a_decorator_passing_arbitrary_argumentsdef function_with_arguments(a, b, c): print(a, b, c)function_with_arguments(1,2,3)#输出#Do I have args?:#(1, 2, 3)#{}#1 2 3@a_decorator_passing_arbitrary_argumentsdef function_with_named_arguments(a, b, c, platypus="Why not ?"): print("Do %s, %s and %s like platypus? %s" %(a, b, c, platypus))function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")#输出#Do I have args ? :#(Bill, Linus, Steve)#{platypus: Indeed!}#Do Bill, Linus and Steve like platypus? Indeed!class Mary(object): def __init__(self): self.age = 31 @a_decorator_passing_arbitrary_arguments def sayYourAge(self, lie=-3): # 可以加入一个默认值 print("I am %s, what did you think ?" % (self.age + lie))m = Mary()m.sayYourAge()#输出# Do I have args?:#(<__main__.Mary object at 0x0000027C4C34FB20>,)#{}#I am 28, what did you think?把参数传递给装饰器
好了,如何把参数传递给装饰器自己?
因为装饰器必须接收一个函数当做参数,所以有点麻烦,你不可以直接把被装饰函数的参数传递给装饰器.
在我们考虑这个问题时,让我们重新回顾下:
# 装饰器就是一个平常不过的函数def my_decorator(func): print("I am an ordinary function") def wrapper(): print("I am function returned by the decorator") func() return wrapper# 因此你可以不用"@"也可以调用他def lazy_function(): print("zzzzzzzz")decorated_function = my_decorator(lazy_function)#输出: I am an ordinary function# 之所以输出 "I am an ordinary function"是因为你调用了函数,# 并非什么魔法.@my_decoratordef lazy_function(): print("zzzzzzzz")#输出: I am an ordinary function看见了吗,和"my_decorator"一样只是被调用.所以当你用@my_decorator你只是告诉Python去掉用被变量my_decorator标记的函数.
这非常重要!你的标记能直接指向装饰器.
def decorator_maker(): print("I make decorators! I am executed only once: when you make me create a decorator.") def my_decorator(func): print("I am a decorator! I am executed only when you decorate a function.") def wrapped(): print("I am the wrapper around the decorated function. " "I am called when you call the decorated function. " "As the wrapper, I return the RESULT of the decorated function.") return func() print("As the decorator, I return the wrapped function.") return wrapped print("As a decorator maker, I return a decorator") return my_decorator# 让我们建一个装饰器.它只是一个新函数.new_decorator = decorator_maker()# 输出:# I make decorators! I am executed only once: when you make me create a decorator.# As a decorator maker, I return a decorator# 下面来装饰一个函数def decorated_function(): print("I am the decorated function.")decorated_function = new_decorator(decorated_function)# 输出:# I am a decorator! I am executed only when you decorate a function.# As the decorator, I return the wrapped function.# Let’s call the function:decorated_function()# 输出:# I am the wrapper around the decorated function. I am called when you call the decorated function. As the wrapper, I return the RESULT of the decorated function.# I am the decorated function.一点都不难把.
下面让我们去掉所有可恶的中间变量:
def decorated_function(): print("I am the decorated function.")decorated_function = decorator_maker()(decorated_function)#输出:#I make decorators! I am executed only once: when you make me create a decorator.#As a decorator maker, I return a decorator#I am a decorator! I am executed only when you decorate a function.#As the decorator, I return the wrapped function.# 最后:decorated_function()#输出:# I am the wrapper around the decorated function. I am called when you call the decorated function. As the wrapper, I return the RESULT of the decorated function.# I am the decorated function.让我们简化一下:
@decorator_maker()def decorated_function(): print("I am the decorated function.")#输出:#I make decorators! I am executed only once: when you make me create a decorator.#As a decorator maker, I return a decorator#I am a decorator! I am executed only when you decorate a function.#As the decorator, I return the wrapped function.#最终:decorated_function()#输出:# I am the wrapper around the decorated function. I am called when you call the decorated function. As the wrapper, I return the RESULT of the decorated function.# I am the decorated function.看到了吗?我们用一个函数调用"@"语法!
所以让我们回到装饰器的.如果我们在函数运行过程中动态生成装饰器,我们是不是可以把参数传递给函数?
def decorator_maker_with_arguments(decorator_arg1, decorator_arg2): print("I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2) def my_decorator(func): # 这里传递参数的能力是借鉴了 closures. # 如果对closures感到困惑可以看看下面这个: # http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python print("I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2) # 不要忘了装饰器参数和函数参数! def wrapped(function_arg1, function_arg2): print("I am the wrapper around the decorated function.\n" "I can access all the variables\n" "\t- from the decorator: {0} {1}\n" "\t- from the function call: {2} {3}\n" "Then I can pass them to the decorated function" .format(decorator_arg1, decorator_arg2, function_arg1, function_arg2)) return func(function_arg1, function_arg2) return wrapped return my_decorator@decorator_maker_with_arguments("Leonard", "Sheldon")def decorated_function_with_arguments(function_arg1, function_arg2): print("I am the decorated function and only knows about my arguments: {0}" " {1}".format(function_arg1, function_arg2))# 输出:# I make decorators! And I accept arguments: Leonard Sheldon# I am the decorator. Somehow you passed me arguments: Leonard Sheldondecorated_function_with_arguments("Rajesh", "Howard")# 输出:# I make decorators! And I accept arguments: Leonard Sheldon# I am the decorator. Somehow you passed me arguments: Leonard Sheldon# I am the wrapper around the decorated function.# I can access all the variables# - from the decorator: Leonard Sheldon# - from the function call: Rajesh Howard# Then I can pass them to the decorated function# I am the decorated function and only knows about my arguments: Rajesh Howard好了,上面就是带参数的装饰器.参数可以设置成变量:
c1 = "Penny"c2 = "Leslie"@decorator_maker_with_arguments("Leonard", c1)def decorated_function_with_arguments(function_arg1, function_arg2): print("I am the decorated function and only knows about my arguments:" " {0} {1}".format(function_arg1, function_arg2))decorated_function_with_arguments(c2, "Howard")#输出:#I make decorators! And I accept arguments: Leonard Penny#I am the decorator. Somehow you passed me arguments: Leonard Penny#I am the wrapper around the decorated function.#I can access all the variables# - from the decorator: Leonard Penny# - from the function call: Leslie Howard#Then I can pass them to the decorated function#I am the decorated function and only knows about my arguments: Leslie Howard你可以用这个小技巧把任何函数的参数传递给装饰器.如果你愿意还可以用
*args,**kwargs.但是一定要记住了装饰器只能被调用一次.当Python载入脚本后,你不可以动态的设置参数了.当你运行import x,函数已经被装饰,所以你什么都不能动了.