码迷,mamicode.com
首页 > 编程语言 > 详细

Python 函数装饰器入门

时间:2015-01-13 21:13:53      阅读:154      评论:0      收藏:0      [点我收藏+]

标签:

 

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的装饰器语法

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函数再装饰两次,分别使用divstrongtag去装饰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的。

函数装饰器的小bug

我们先看一段代码

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

 

Python 函数装饰器入门

标签:

原文地址:http://www.cnblogs.com/zk47/p/4222335.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!