装饰器微妙之谈

Python 知识大全

共 1986字,需浏览 4分钟

 · 2022-02-13


简单的装饰器

def my_decorate(fun):    def wrapper():        print('这是个装饰器!')        func()     reture wapper     def greet(): print('hello world')greet = my_decorator(greet)greet()# 输出这是个装饰器!hello world


这段代码中变量 greet 指向了内部函数 wrapper(),而内部函数 wrapper() 中又会调用原函数 greet(),因此,最后调用 greet() 时,就会先打印 'wrapper of decorator',然后输出'hello world'。大家应该都明白!


这里的函数 my_decorator() 就是一个装饰器,它把真正需 要执行的函数 greet() 包裹在其中,并且改变了它的行为, 但是原函数 greet() 不变。


我们换一种写法,使用Python来装饰它

def my_decorator(func):       def wrapper():           print('wrapper of decorator')           func()      return wrapper@my_decoratordef greet():    print('hello world')greet()

这里的@,我们称之为语法糖,@my_decorator就相当于 前面的greet=my_decorator(greet)语句,只不过更加 简洁。因此,如果你的程序中有其它函数需要做类似的装饰,你只需在它们的上方加上@decorator就可以了,这样 就大大提高了函数的重复利用和程序的可读性。


带有参数的装饰器


如果原函数 greet() 中,有参数需要传递给 装饰器怎么办?一个简单的办法,是可以在对应的装饰器函数 wrapper() 上,加上相应的参数,比如:

def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper@my_decoratordef greet(message): print(message)greet('hello world')# 输出wrapper of decoratorhello world


事实上,通常情况下,我们会把*args和**kwargs,作为 装饰器内部函数 wrapper() 的参数。

def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper

类装饰器

实际上,类也可 以作为装饰器。类装饰器主要依赖于函数__call_(),每当你调用一个类的示例时,函数__call__() 就会被执行一 次

class Count: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print('num of calls is: {}'.format(self.num_calls)) return self.func(*args, **kwargs)@Countdef example(): print("hello world") example()# 输出num of calls is: 1hello world
example()# 输出num of calls is: 2hello world

 这里,定义了类 Count,初始化时传入原函数 func(), 而__call__() 函数表示让变量 num_calls 自增 1,然后打印,并且调用原函数。因此,在我们第一次调用函数 example() 时,num_calls 的值是 1,而在第二次调用时, 它的值变成了 2。



浏览 15
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报