Python - 细解装饰器
在说python装饰器之前,先了解一下函数的重要特性。
函数的重要特性
1.在python中,一切皆为对象,所以函数也是一个对象,从而函数可以赋值给变量。
定义一个add函数,函数的功能是实现两个数想加
# -*- coding: UTF-8 -*-
def add(num1, num2):
return num1 + num2
print(add)
# 结果:<function add at 0x00000000021B3798>
可以看到,系统为函数分配了一个内存地址:0x00000000021B3798
# -*- coding: UTF-8 -*-
def add(num1, num2):
return num1 + num2
print(add)
# 将函数这个对象赋值给 f
f = add
# 调用这个函数,传入两个实参,并且打印出来
print(f(1, 2)) # 结果等于 3
2.一个函数可以作为另一个函数的返回值
# -*- coding: UTF-8 -*-
def add(num1, num2):
return num1 + num2
print(add)
def do_sth():
return add
print(do_sth()(2, 3)) # 结果:5
3.一个函数可以嵌套定义在另一个函数中
# -*- coding: UTF-8 -*-
def outer():
def inner():
print("This is inner..")
return inner
outer()() # This is inner..
4.一个函数可以作为另一个函数的实参。
先来了解一下map函数
map函数是根据指定函数对指定序列做映射,可以有效提高程序运行效率。
看一下map函数的源码:
map(func, *iterables)
map函数接受两个参数:
func:指定函数函数
iterables:可迭代对象,一个序列或者多个序列,即函数对应的实参
通过调用map,就可以将指定函数应用到可迭代对象中的每一个元素中。
然后生成一个新的可迭代对象。
例子:
def eval_square(x):
return x * x
result = map(eval_square, [1, 2, 3])
print(list(result)) # [1, 4, 9]
装饰器
执行以下代码,很简单,打印“老大徒伤悲”
def test():
print("老大徒伤悲")
test() # 老大徒伤悲
想要实现一个功能,在不改变 test 函数内部结构的前提下,在前面加一句 “少壮不努力”
结果是:
少壮不努力
老大徒伤悲
弄清楚了装饰器的工作原理:
- 写一个高阶函数,即参数是函数,返回的也是函数;上面的例子,传函数 test 作为实参,最后返回函数 test ;
- 在利用语法糖@,简化赋值操作;
但这样的代码是有缺陷:test 函数直接返回了函数名,这样后面就不好调用了。
改进:
def name(func):
def wrapper():
print("函数%s被调用了" % func.__name__)
print("少壮不努力")
return func()
return wrapper
@name
def test():
print("老大徒伤悲")
test()
# test(): 调用test函数,相当于调用装饰器内函数 wrapper()
# 所以执行的序列就明了了。
print(test.__name__) # 验证调用函数 test 的时候,其实是调用 装饰器的内函数 wrapper,即test() = wrapper()
输出结果:
函数test被调用了
少壮不努力
老大徒伤悲
wrapper
以下是 ChatGPT的回答
基于上面的Python装饰器代码,下面是代码执行的顺序:
1. 定义函数`name`,并在其中定义闭包函数`wrapper`。
2. 在函数`test`上方添加`@name`装饰器,在调用`test`函数时,会自动触发`name`函数内部的`wrapper`函数。
3. 调用`test()`函数,由于已经被`@name`装饰器修饰过,因此实际上会执行`name(test)()`,即执行`name`函数,并将`test`函数作为参数传递进去。
4. `name`函数返回了一个新的函数对象`wrapper`,因此实际上执行的是`wrapper()`函数。
5. 执行`wrapper()`函数时,先打印出一条字符串提示信息"函数test被调用了"。
6. 然后打印出另一条固定的字符串"少壮不努力"。
7. 接着执行原函数`test`,打印出一条字符串"老大徒伤悲"。
8. `wrapper()`函数返回了`test()`函数的返回值(如果有),并结束执行。
9. `test()`函数的执行结束,整个程序也随之结束。
综上所述,这段Python装饰器代码的执行顺序是:`test()` -> `name(test)` -> `wrapper()` -> `test()`.
装饰器总结
什么是装饰器?
- 处理函数的函数,加一个功能,但是不影响原来函数的内部结构
- 生活中的例子:给手机加一个外壳,外壳保护了手机
装饰器有什么用?
- 增强函数的功能
装饰器使用场景
- 增加被装饰函数的行为
- 代码复用
一个函数接收另一个函数作为参数,这种函数称之为高阶函数。
------分界线------
那些迈不过去的坎儿,还不是因为你腿短!
俗话说:你笑,全世界都跟着你笑;你哭,全世界只有你一个人哭。