Python核心丨装饰器

装饰器


函数 - > 装饰器
  • 在Python中,函数也是对象,可以把函数赋予变量**
def func(message):
    print('Got a message:{}'.format(message))

send_message = func
send_message('hello world')

# 输出
Got a message: hello world

函数func赋予了变量send_message,调用send_message,就相当于调用函数func()

  • 可以把函数当作参数,传入另一个函数中
def get_message(message):
    return 'Got a message:' + message

def root_call(func, message):
    print(func(message))

root_call(get_message, 'hello world')

# 输出
Got a message: hello world

把函数get_message以参数的形式,传入了函数root_call()中然后调用它。

  • 可以在函数里定义函数,也就是函数的嵌套。
def func(message):
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message(message)

func('hello world')

# 输出
Got a message: hello world

在函数func()里定义了新的函数get_message(),调用func()的返回值返回。

  • 函数的返回值也可以是函数对象(闭包)
def func_closure():
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message

send_message = func_closure()
send_message('hello world')

# 输出
Got a message: hello world

函数func_closure()的返回值是函数对象get_message。

简单的装饰器
def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

def greet():
    print('hello world')

greet = my_decorator(greet)
greet()

# 输出
wrapper of decorator
hello world

变量greet指向了内部函数wrapper(),而内部函数wrapper()中又调用原函数greet()。

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

Python中更简单、更优雅的表示

def my_decorator(func):
    def wrapper():
        print('wrapper of decorator')
        func()
    return wrapper

@my_decorator
def greet():
    print('hello world')

greet()

@为语法糖,@my_decorator就相当于前面的greet=my_decorator(greet)语句,只不过更加简洁。

带有参数的装饰器

如果原函数中,有参数需要传递给装饰器,那么可以在对应的装饰器函数上,加上相应的参数。

def my_decorator(func):
    def wrapper(message):
        print('wrapper of decorator')
        func(message)
    return wrapper


@my_decorator
def greet(message):
    print(message)


greet('hello world')

# 输出
wrapper of decorator
hello world

通常情况下,会把*args和**kwargs,作为装饰器内部函数的参数。

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper
带有自定义参数的装饰器

装饰器可以接受原函数任意类型和数量的参数,除此之外,还可以接受自己定义的参数。

实例

  • 定义一个参数,来表示装饰器内部函数被执行的次数
def repeat(num):
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                print('wrapper of decorator')
                func(*args, **kwargs)
        return wrapper
    return my_decorator


@repeat(4)
def greet(message):
    print(message)

greet('hello world')

# 输出:
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
wrapper of decorator
hello world
原函数还是原函数吗?

打印之前例子的元信息

greet.__name__
## 输出
'wrapper'

help(greet)
# 输出
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

greet()函数被装饰以后,它的元信息变了。不再是以前的greet()函数,而是被wrapper()函数取代了。

为了解决这个问题,通常使用内置的装饰器@functools.wrap,它会帮助保留言函数的元信息(也就是将原函数的元信息,拷贝到对一个的装饰器函数里)

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('wrapper of decorator')
        func(*args, **kwargs)
    return wrapper
    
@my_decorator
def greet(message):
    print(message)

greet.__name__

# 输出
'greet'
类装饰器

类装饰器主要以来于函数__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)

@Count
def example():
    print('hello world')

example()

# 输出
num of calls is: 1
hello world

example()

# 输出
num of calls is: 2
hello world

...
装饰器的嵌套

Python支持多个装饰器

@decorator1
@decorator2
@decorator3
def func():
    ...

执行顺序从里到外

decorator1(decorator2(decorator3(func)))

示例

import functools

def my_decorator1(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator1')
        func(*args, **kwargs)
    return wrapper


def my_decorator2(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('execute decorator2')
        func(*args, **kwargs)
    return wrapper


@my_decorator1
@my_decorator2
def greet(message):
    print(message)


greet('hello world')

# 输出
execute decorator1
execute decorator2
hello world
装饰器用法实例

身份认证

  • 登陆时,需要输入用户密码,然后点击确认,如果认证通过,就可以顺利登陆;如果不通过,就抛出异常并提示你登录失败。
import functools

def authenticate(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        request = args[0]
        if check_user_logged_in(request): # 如果用户处于登录状态
            return func(*args, **kwargs) # 执行函数post_comment() 
        else:
            raise Exception('Authentication failed')
    return wrapper
    
@authenticate
def post_comment(request, ...)
    ...
 

日记记录

找到某些函数的耗时过长,导致整个系统的latency(延迟)增加。

import time
import functools

def log_execution_time(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        res = func(*args, **kwargs)
        end = time.perf_counter()
        print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
        return res
    return wrapper
    
@log_execution_time
def calculate_similarity(items):
    ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值