讲在开始
这是针对以下内容的第二部分翻译
http://stackoverflow.com/a/1594484
基础部分在这里:
http://www.jianshu.com/p/c45fd47f3b85
原答案代码在2.7版本下可以全部运行成功,我将其中的代码用3.5版本重写,并改正了一些没有遵循PEP8规范的代码,以便自己学习理解的更深入一些
揭秘
在之前的例子中,我们直接使用的Python的装饰器语法:
@my_shiny_new_decorator
def another_stand_alone_function():
print("Leave me alone")
another_stand_alone_function()
# 输出 :
# Before the function runs
# Leave me alone
# After the function runs
以上就是全部了,而且很简单直观,@decorator的用法,就是简写了
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
Python中的装饰器是decorator design pattern的Python实现。当然,Python中还有很多对设计模式的实现,比如迭代器(iterator)。
下面给出一个叠加装饰器的简单例子:
def bread(func):
def wrapper():
print("''''''\>")
func()
print("<\______/>")
return wrapper
def ingredients(func):
def wrapper():
print("#tomatoes#")
func()
print("~salad~")
return wrapper
def sandwich(food="--ham--"):
print(food)
sandwich()
# 输出 : --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
# 输出 :
# ''''''\>
# #tomatoes#
# --ham--
# ~salad~
# <\______/>
更Pythonic的用法:
@bread
@ingredients
def sandwich(food="--ham--"):
print(food)
sandwich()
设置装饰器的顺序不能随意设定,因为
@ingredients
@bread
def sandwich(food="--ham--"):
print(food)
sandwich()
#输出 :
##tomatoes#
#''''''\>
# --ham--
#<\______/>
# ~salad~
和上一个例子的运行结果是不同的。
进阶
仅仅满足于现状是不够的,如果你希望学到更深层更Pythonic的用法,请继续读下去。
向装饰器函数中传入参数
# 这没什么神奇的,你只需要让负责“包装”的函数来传递参数就可以了
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_arguments
def 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
def another_print_full_name(first_name, last_name):
print("My name is", first_name, last_name)
# 直白版
a_decorator_passing_arguments((another_print_full_name("Jim", "Raynor")))
# 输出 :
# My name is Jim Raynor
在Python中一个有趣的事情是,无论是函数还是方法(指类中的方法)其实都是一样的。唯一的区别是方法的第一个参数是对当前对象的引用(即约定俗成的self)
因此,既然我们可以编写装饰函数,同样我们也可以为方法编写装饰器,只是这次我们要考虑到self的存在
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie -= 3 # very friendly, decrease age even more :-)
return method_to_decorate(self, lie)
return wrapper
class Lucy(object):
def __init__(self):
self.age = 32
@method_friendly_decorator
def say_age(self, lie):
print("I am %s, what did you think?" % (self.age + lie))
l = Lucy()
l.say_age(-3)
# 输出 : I am 26, what did you think?
Kulbear:插一句,从注释和例子中感受一下老外的幽默感?反正我在国外待了很久仍然觉得这个有的时候会很冷...
Kulbear:在下一段开始之前,如果你不明白什么是args和什么是kwargs,可以参考这里
http://book.pythontips.com/en/latest/args_and_kwargs.html
如果你希望你的装饰器(decorator)可以更通用一些,比如你不希望受到参数数量的制约,那么只需要使用*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)
# 现在你将这些参数传入
function_to_decorate(*args, **kwargs)
return a_wrapper_accepting_arbitrary_arguments
@a_decorator_passing_arbitrary_arguments
def 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_arguments
def 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_arguments
def 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 say_age(self, lie=-3): # You can now add a default value
print("I am %s, what did you think ?" % (self.age + lie))
m = Mary()
m.say_age()
# 输出 :
# Do I have args?:
# (<__main__.Mary object at 0xb7d303ac>,)
# {}
# I am 28, what did you think?
后记
在这里,再次推荐一个Python进阶的读物,感谢我看了半天也不会读名字的作者 Muhammad Yasoob Ullah Khalid
http://book.pythontips.com/en/latest/index.html
后面的部分会继续翻译,敬请期待