所不为人知的Python装饰器

装饰器可以说是Python中非常重要的特性之一。有些人要么从没使用过装饰器,要么就是对装饰器的使用一知半解。也有些人觉得装饰器很简单:"装饰器不就是那些把函数作为参数并输出一个函数的函数"。这里介绍一些装饰器“所不为人知”的一些地方。

先回顾一下函数。函数在python中是一等公民,也就是说,函数也是对象,它可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值、也有属性。

例如:

1.1.输出函数f的所有属性

def f():
    print('something')
print(dir(f))
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

1.2.赋值给一个变量g

g = f
g()
something

1.3.作为参数传递给其他函数

def func_name(function):
    return function.__name__
func_name(f)
f

1.4.作为元素添加到集合对象list中

function_collection = [f, g]
for function in function_collection:
    function()
something
something

装饰器常常被描述成“装饰器就是那些把函数作为参数并输出一个函数的函数”,严格地讲,这并不是很准确。其实,他还有如下一些细节:

  • 在函数定义的时候就执行了一次装饰器的装饰过程
  • @d装饰一个函数定义x,等价于,先定义函数x,然后执行x=d(x)
  • @d和@e装饰一个函数定义x,等价于,先定义函数x,然后执行x=e(d(x))

举几个例子会更加让人理解。

def print_when_called(function):
    def new_function(*args, **kwargs):
        print("{} was called".format(function.__name__))
        return function(*args, **kwargs)
    return new_function

def one():
    return 1
one = print_when_called(one)

@print_when_called
def two():
    return 2

[one(), two(), one(), two()]
one was called
two was called
one was called
two was called
[1, 2, 1, 2]

你可能会注意到,上面的输出是在执行[one(), two(), one(), two()]后才打印出来,实际上,在one()或two()执行前,print_when_called就已经返回了负责打印的new_function。下面的例子会更容易理解些:

def print_when_applied(function):
    print("print_when_applied was applied to {}".format(function.__name__))
    return function

@print_when_applied
def never_called():
    print("never_called")
print_when_applied was applied to never_called

显而易见,never_called”没有被执行,但是“print_when_applied was applied to never_called”已经打印出来了。

下面再说一下装饰器执行的顺序。

@print_when_applied
@print_when_called
def this_name_will_be_printed_when_called_but_not_at_definition_time():
    pass

this_name_will_be_printed_when_called_but_not_at_definition_time()
print_when_applied was applied to new_function
this_name_will_be_printed_when_called_but_not_at_definition_time was calle

 

posted @ 2018-01-11 16:48  鱼果  阅读(140)  评论(0编辑  收藏  举报