标签:
Python 函数装饰器入门
原文链接: --> A guide to Python‘s function decorators
Python功能强劲,语法表现力强,尤其装饰器深深的吸引着我。在设计模式中,装饰器可以在不使用子类的情况下,动态的改变函数,方法以及类的功能。这个功能非常有用,特别在你想扩展函数的功能同时又不想改变原有的函数。的确,我们任意的实现装饰器设计模式,但是,python通过提供简单的语法和特性让装饰器的实现变的如此简单。
在本文中,我将用一组例子来深入浅入python 函数装饰器的功能,所有的例子都是在python2.7中测试通过的,如果应用到python3中,需要做一些修改。
本质上,装饰器就是包装的作用,改变函数执行前后的行为,但是不影响函数的执行。
在讲之前,有一些关于python函数的基本特性需要说明一下
def hello(name):
return ‘Hello ‘ + name
hello_someone = hello
hello_some(‘world‘)
# Outputs: hello world
def hello(name):
def get_message():
return ‘Hello ‘
result = get_message() + name
return result
print hello(‘world‘)
# Outputs: Hello world
def hello(name):
return ‘Hello ‘ + name
def call_func(func):
welcome_name = ‘world‘
return func(welcome_name)
print call_func(hello)
# Outputs: Hello world
也就是说函数可以生成其他的函数
def compose_hello_func():
def get_message():
return ‘Hello world‘
return get_message
hello = compose_hello_func()
print hello()
# Outputs: Hello world
这就是闭包,在我们建装饰器时非常有用的一个模式
def compose_hello_func(name):
def get_message():
return ‘Hello ‘ + name
return get_message
hello = compose_hello_func(‘world‘)
print hello()
# Outputs: Hello world
函数装饰器就是去粉饰现存的函数的,把上面的想法糅合一下,我们就可以构造一个装饰器。在下面的例子中,我们用p标签去装饰另外一个函数的字符串输出
def get_text(name):
return "hello {0}, good morning".format(name)
def p_decorator(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper
my_get_text = p_decorator(get_text)
print my_get_text(‘world‘)
# Output: <p>hello world, good morning</p>
这就是我们的第一个装饰器,将一个函数作为输入,生成另外一个函数,同时充实了原有函数的功能,返回了一个可以随意使用的函数,为了能够让get_text
函数能够被装饰器p_decorator
装饰,只需要将p_decorator
的结果赋给get_text
即可。
get_text = p_decorator(get_text)
print get_text(‘world‘)
# Output: <p>hello world, good morning</p>
python通过语法糖(syntactic sugar)可以使用和创建一个简洁清爽的装饰器。像上文中的装饰get_text
函数,我们不需要使用get_text = p_decorator(get_text)
,python给我们实现了一种简单的方式,只要通过@
符号即可实现。
def p_decorator(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper
@p_decorator
def get_text(name):
return "hello {0}, good morning".format(name)
print get_text(‘world‘)
# Output: <p>hello world, good morning</p>
现在我们想对get_text
函数再装饰两次,分别使用div
和strong
tag去装饰get_text
的字符串输出
def p_decorator(func):
def func_wrapper(name):
return "<p>{0}</p>".format(func(name))
return func_wrapper
def strong_decorator(func):
def func_wrapper(name):
return "<strong>{0}</strong>".format(func(name))
return func_wrapper
def div_decorator(func):
def func_wrapper(name):
return "<div>{0}</div>".format(func(name))
return func_wrapper
有了这些基本的方法,装饰get_text
只需要如下的表达式:
get_text = div_decorate(p_decorate(strong_decorate(get_text)))
利用python的语法糖,同样的事情,实现起来却更具有表现力
@div_decorator
@strong_decorator
@p_decorator
def get_text(name):
return "hello {0}, good morning".format(name)
print get_text(‘world‘)
# Outputs: <div><strong><p>hello world, good morning</p></strong></div>
一个需要注意的事情就是,装饰器的顺序不同,最后得到结果也会不一样。
在python中,装饰器也可以修饰class里面的函数
def p_decorator(func):
def wrapper(self):
return "<p>{0}</p>".format(func(self))
return wrapper
class Person(object):
def __init__(self):
self.name = ‘hello‘
self.family = ‘world‘
@p_decorator
def get_fullname(self):
return self.name + " " + self.family
my_person = Person()
print my_person.get_fullname()
#Output: <p>hello world</p>
一种更好的方法可以让我们的装饰器变得更为通用,就是将\*args
和\*\*kwargs
作为参数传给装饰函数,这样装饰函数就可以接受任意个单参数和key = value
类型的参数
def p_decorator(func):
def wrapper(*args, **kwargs):
return "<p>{0}</p>".format(func(*args, **kwargs))
return wrapper
class Person(object):
def __init__(self):
self.name = ‘hello‘
self.family = ‘world‘
@p_decorator
def get_fullname(self):
return self.name + " " + self.family
my_person = Person()
print my_person.get_fullname()
#Output: <p>hello world</p>
在上面的几个tag
装饰器,看起来有很大的冗余,那是不是可以传参数给装饰器呢,当然是可以的。
def tags(tag_name):
def tags_decorator(func):
def func_wrapper(name):
return "<{0}>{1}</{0}>".format(tag_name, func(name))
return func_wrapper
return tags_decorator
@tags("any_tag")
def get_text(name):
return "Hello " + name
print get_text("World")
#Output: <any_tag>Hello World</any_tag>
实际上函数tags
和函数tags_decorator
都是用来修饰函数get_text
的。
我们先看一段代码
def p_decorator(func):
def wrapper(name):
‘‘‘wrapper docs‘‘‘
return "<p>{0}</p>".format(func(name))
return wrapper
@p_decorator
def get_text(name):
‘‘‘hello docs‘‘‘
return "Hello " + name
print get_text.__doc__
print get_text.__name__
# Ouput:
#wrapper docs
#wrapper
我们发现get_text
被装饰器修饰过后,它的函数名和doc信息都变成了装饰函数的了。 有方法解决吗,当然,python为我们提供了functool模块中的wraps函数,该函数会会将装饰函数的特性(attribute)更新为原来的函数。
from functools import wraps
def p_decorator(func):
‘‘‘wrap docs‘‘‘
@wraps(func)
def wraper(name):
return "<p>{0}</p>".format(func(name))
return wraper
@p_decorator
def get_text(name):
‘‘‘hello docs‘‘‘
return "Hello " + name
print get_text.__doc__
print get_text.__name__
# Output:
#hello docs
#get_text
本文的例子相比于真正使用的装饰来说是比较简单的。记住一点,装饰器实际上就是在不改变函数本身的情况下,扩展函数的功能,详细的信息可以参考Python Decorator Library
标签:
原文地址:http://www.cnblogs.com/zk47/p/4222335.html