python中的decorator装饰器(上)

python中的decorator装饰器(上)

function与inner function

  • function:在python中,function就像其他对象一样,可以当做参数或返回值。当函数作为返回值时,不要带括号,表示其会在将来被调用。

  • inner function:一个函数A(inner function)定义在另一个函数B中。函数A不能直接被外界调用,因此此时内部函数还未定义。但可以通过B将A的引用返回,用于在将来调用。

# 函数用做参数
def hello():
    print("hello")
def welcome():
    print("Welcome")
def say_something(func):
    func()
# 函数A在函数B中定义,并且用做返回值
def cal():
	def add(a,b):
		print(a+b)
	return add

image

decorator

装饰器就是结合了内部函数以及函数在python中是一级公民(可以用做参数、返回值)的性质。

decorator:封装了一个函数,并且改变了其行为

def hello():
    print("hello")
def my_decorator(func):
    def wrapper():
        print("before func work")
        func()
        print("after func work")
    return wrapper
hello_d = my_decorator(hello)
hello_d() 
"""
before func work
hello
after func work
"""

hello_d便是经过my_decorator装饰的函数。为使过程更加简化,python提供了语法糖。

syntactic sugar

@my_decorator
def hello():
    print("hello")
def my_decorator(func):
    def wrapper():
        print("before func work")
        func()
        print("after func work")
    return wrapper
hello()#与hello = my_decorator(hello)作用相同
"""
before func work
hello
after func work
"""

wrapper的命名可自定义。

使用@functools.wrap(func)

因为hello被my_decoratorx装饰过,此时在python shell查看hello的__name__,发现
image
这对我们来说,不是有用的信息,为了“正确”显示,使用@functools.wrap(func)

import functools
@my_decorator
def hello():
    print("hello")
def my_decorator(func):
	@functools.wrap(func)
    def wrapper():
        print("before func work")
        func()
        print("after func work")
    return wrapper

image

带参数的decorator以及几个例子

# do twice
def do_twice(func):
	 @functools.wraps(func)
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
        func(*args,**kwargs)
    return wrapper
# timer
def timer(func):
    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()
        value = func(*args, **kwargs)
        end_time = time.perf_counter()
        run_time = end_time - start_time
        print(f"Finished {func.__name__} in {run_time:.4f} seconds")
        return value
    return wrapper_timer

@timer
def waste_time(num):
    for _ in range(num):
        sum([i **2 for i in range(10000)])  

waste_time(1000)
# flask check user login
def login_required(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if g.user is None:
            return redirect(url_for("login",next=request.url))
        return func(*args, **kwargs)
    return wrapper
    
@app.route("/secret")
@login_required
def secret():
    ...

总结

decorator用于“包裹”一个函数,改变其行为。
在wrapper中并不一定要执行该函数,也可以保留函数的引用,用于插件的注册。

参考

realpython

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » python中的decorator装饰器(上)