python3 装饰器笔记

python中存在两种装饰器类型:

  • 函数装饰器
  • 类装饰器

装饰器是任何可调用对象,用来实现修改函数或类。
在python中圆括号意味着调用函数。在没有圆括号的情况下,python会把函数当作普通对象。(摘抄自:python语言及其应用)
学习装饰器,需要了解一些基本的概念,函数名是某个函数的引用(reference),所以,我们可以对同一个函数设置不同的函数名。(可以理解为,函数也是对象,可以通过赋值来设置不同的对象名)

>>> def succ(x):
    return x+1

>>> successor = succ
>>> successor(10)
11
>>> succ(10)
11
>>>

successor和succ都指向同一个函数(应该是对象)。同时需要注意,可以删除successor或succ。

>>> id(successor)
50546488
>>> id(succ)
50546488
>>> del succ
>>> successor(10)
11
>>> type(successor)
<class 'function'>

通过id()函数,可以看到,successor和succ都指向同一个内存区域。

NOTICE:以下代码都摘抄自http://www.wklken.me/posts/2012/10/27/python-base-decorator.html#_8
装饰器的四种形式:

  • 无参数装饰器-包装无参数函数
  • 无参数装饰器-包装带参数函数
  • 带参数装饰器-包装无参数函数
  • 带参数装饰器-包装带参数函数

无参数装饰器-包装无参数函数

def decorator(func):
    print('hello')
    return func

@decorator
def foo():
    print('foo end')

foo()

foo()等价于

foo_text = decorator(foo)
foo_text()

无参数装饰器-包装带参数函数

def decorator_func_args(func):
    def handle_args(*args, **kwargs):
        print("handle args start")
        func(*args, **kwargs)
        print("handle args end")
    return handle_args

@decorator_func_args
def foo2(a, b=2):
    print(a,b)

foo2(1)

运行结果:

handle args start
1 2
handle args end

foo2(1)等价于:

foo2_text = decorator_func_args(foo2)
foo2_text(1)

带参数装饰器 – 包装无参数函数

def decorator_with_params(arg_of_decorator):
    print(arg_of_decorator)
    def newDecorator(func):
        print(func)
        return func
    return newDecorator

@decorator_with_params("deco_args")
def foo3():
    pass

foo3()

先传递参数,再传递函数名,等价于:

foo3_text = decorator_with_params("deco_args")
foo3 = foo3_text(foo3)
foo3()

带参数装饰器– 包装带参数函数

def decorator_with_params_and_func_args(arg_of_decorator):
    def handle_func(func):
        def handle_args(*args, **kwargs):
            print("begin")
            func(*args, **kwargs)
            print("end")
            print(arg_of_decorator, func, args, kwargs)
        return handle_args
    return handle_func

@decorator_with_params_and_func_args("123")
def foo4(a, b=2):
    print("content")

foo4(1, b=3)

运行结果如下:

begin
content
end
123 <function foo4 at 0x005403D8> (1,) {'b': 3}

foo4(1,b=4)等同于:

foo4_dec = decorator_with_params_and_func_args("123")
foo4_func = foo4_dec(foo4)
foo4_text = foo4_func(1,b=3)
  1. 给decorator参数赋值,返回handle_func对象
  2. handle_func()函数接收foo4对象,返回handle_args对象
  3. handle_args()函数接收传递给foo4的参数。

函数的元数据

函数的一些属性:

  • __name__(函数名),
  • __doc__(描述)和
  • __module__(模块位置)
    在使用装饰器的时候,函数会丢失这些元数据。
def greeting(func):
    def function_wrapper(x):
        """function_wrapper of greeting"""
        print("Hi, " + func.__name__ + " returns:")
        return func(x)
    return function_wrapper

@greeting
def f(x):
    """just some silly function"""
    return x + 4

f(10)
print("function name: " + f.__name__)
print("docstring: " + f.__doc__)
print("module name:" + f.__module__)

运行结果如下:

Hi, f returns:
function name: function_wrapper
docstring: function_wrapper of greeting
module name:__main__

在正常情况下,f.__name__应该为f,而通过装饰器之后,由于没有保存函数的元数据,所以变成了function_wrapper
每当定义一个装饰器时,应该总是记得为底层的包装函数添加functools库中的@wraps装饰器

from functools import wraps

def greeting(func):
    @wraps(func)
    def function_wrapper(x):
        """function_wrapper of greeting"""
        print("Hi, " + func.__name__ + " returns:")
        return func(x)
    return function_wrapper

@greeting
def f(x):
    """just some silly function"""
    return x + 4

f(10)
print("function name: " + f.__name__)
print("docstring: " + f.__doc__)
print("module name:" + f.__module__)

运行结果为:

Hi, f returns:
function name: f
docstring: just some silly function
module name:__main__

参考:http://www.python-course.eu/python3_decorators.php

发表评论

电子邮件地址不会被公开。 必填项已用*标注