Python闭包
什么是闭包?
# 定义一个函数
def test(number):
# 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
def test_in(number_in):
print("in test_in 函数, number_in is %d" % number_in)
return number+number_in
# 其实这里返回的就是闭包的结果
return test_in
# 给test函数赋值,这个20就是给参数number
ret = test(20)
# 这里的100其实给参数number_in
print(ret(100))
# 这里的200其实给参数number_in
print(ret(200))
运行结果:
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包。(说人话)在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量合起来称之为闭包。
如果需要在一系列函数调用中保持某个状态,使用闭包是一种非常高效的方式。举个栗子:
def countdown(n):
def next():
# global适用于函数内部修改全局变量的值
# nonlocal适用于嵌套函数中内部函数修改外部变量的值
nonlocal n
r = n
n -= 1
return r
return next
next = countdown(5)
while True:
v = next()
print(v, end=" ")
if not v:
break
运行结果:
5 4 3 2 1 0
在这段代码中,闭包用于保存内部计数器的值n,每次调用内部函数next()时,它都更新并返回这个计数器变量的前一个值。
Python装饰器
写代码要遵循开放封闭
原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
如果要在已经写好的程序上添加一些验证或者修饰内容怎么办?
这就需要装饰器了!!!
def makeBold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
# 如果对于@语法糖不理解的话,下面就是装饰器的执行过程
# def test1():
# return "hello world-1"
# test1 = makeBlod(test1)
@makeBold
def test1():
return "hello world-1"
@makeItalic
def test2():
return "hello world-2"
@makeBold
@makeItalic
# 装饰器是有顺序的,由内到外的执行顺序,
# 即先执行@makeItalic,包装返回结果<i>hello world-3</i>
# 再执行@makeBold,包装返回结果<b><i>hello world-3</i></b>
def test3():
return "hello world-3"
print(test1())
print(test2())
print(test3())
运行结果:
<b>hello world-1</b>
<i>hello world-2</i>
<b><i>hello world-3</i></b>
装饰器(decorator)功能
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
装饰器示例
1.被装饰的函数无参数, 无返回值
def decroate_fun(func):
def wrapper():
print("函数%s 开始运行" % (func.__name__))
# 装饰器进行装饰的时候就是在这里将func()换成被装饰的函数内容
# 不如下面的foo函数被装饰,就将foo的内容 print("I am foo")替换到这里
func()
print("函数%s 结束运行" % (func.__name__))
print("------------------------------------")
return wrapper
@decroate_fun
def foo():
print("I am foo")
foo()
运行结果:
函数foo 开始运行
I am foo
函数foo 结束运行
------------------------------------
2.被装饰的函数有参数无返回值
def decroate_fun(func):
def wrapper(*args, **kwargs):
print("函数%s 开始运行" % (func.__name__))
func(*args, **kwargs)
print("函数%s 结束运行" % (func.__name__))
print("------------------------------------")
return wrapper
@decroate_fun
def foo():
print("I am foo")
@decroate_fun
def foo2(a, b):
print("a + b =", a + b)
foo()
foo2(2, 3)
运行结果:
函数foo 开始运行
I am foo
函数foo 结束运行
------------------------------------
函数foo2 开始运行
a + b = 5
函数foo2 结束运行
------------------------------------
3.被装饰的参数有参数,有返回值
from time import time
def time_fun(func):
"""测试程序运行时间的装饰器"""
def wrapper(*args, **kwargs):
# 记录程序开始的时间
start = time()
# 对于装饰有返回值的函数,很多人的第一反应是在这里return func(*args, **kwargs)
# 但是像测试运行时间这样的装饰器,需要在函数执行完毕后还要执行一步,所以取个变量result 记录函数返回值
result = func(*args, **kwargs)
# 计算函数运行时间,并显示
print("函数%s 运行时间为%s" % (func.__name__, time()-start))
print("------------------------------------")
return result
return wrapper
@time_fun
def foo():
print("I am foo")
@time_fun
def foo2(a, b):
print("a + b =", a + b)
@time_fun
def foo3(a, b):
print("现在在foo3里面")
return a * b
foo()
foo2(2, 3)
ret = foo3(3, 4)
print(ret)
运行结果:
I am foo
函数foo 运行时间为3.9577484130859375e-05
------------------------------------
a + b = 5
函数foo2 运行时间为5.9604644775390625e-06
------------------------------------
现在在foo3里面
函数foo3 运行时间为2.6226043701171875e-06
------------------------------------
12
4.装饰器有参数
当我们需要添加不同的装饰内容,但是格式相同的时候,因为我们不能为每个内容写一个装饰器,这时就需要另外一种方法——装饰器工厂——给装饰器传参数。
"""
有一项任务,要求函数运行时间测试函数要求记录是谁测的
比如: 输出结果:[Jack测试:]函数foo3 运行时间为1秒
"""
from time import time
def timefunc_factory(name):
def time_fun(func):
"""测试程序运行时间的装饰器"""
def wrapper(*args, **kwargs):
# 记录程序开始的时间
start = time()
# 对于装饰有返回值的函数,很多人的第一反应是在这里return func(*args, **kwargs)
# 但是像测试运行时间这样的装饰器,需要在函数执行完毕后还要执行一步,所以取个变量result 记录函数返回值
result = func(*args, **kwargs)
# 计算函数运行时间,并显示
print("[%s测试:]函数%s 运行时间为%s秒" % (name, func.__name__, time()-start))
print("------------------------------------")
return result
return wrapper
return time_fun
@timefunc_factory("Jack")
def foo4(a, b):
print("现在在foo4里面")
return a * b
result = foo4(2, 4)
print(result)
运行结果:
现在在foo3里面
[Jack测试:]函数foo4 运行时间为2.2649765014648438e-05秒
------------------------------------
8
注意点:
# 定义一个简单的装饰器
def decroate_func(func):
def wrapper():
pass
return wrapper
@decroate_func
def test1():
pass
@decroate_func
def test2():
pass
# 打印函数test1和test2的名字
print(test1.__name__)
print(test2.__name__)
运行结果:
wrapper
wrapper
不难发现,当装饰器decroate_func装饰函数时,会将函数的名字(__name__
)以及其他属性(__doc__
等)修改为装饰器中的内部函数wrapper函数的相关属性。当多个函数被修饰时,会造成函数的调用出现二义性(即有好多个wrapper函数,程序不知道要调用哪个函数)。
解决方法一(只作为了解,不建议使用):
根据自己的需求,手动修改(__name__
)等属性
def decroate_func(func):
def wrapper():
pass
# 在装饰器返回内部函数之前,将wrapper的__name__属性修改为func的__name__
# 其他属性也是这样修改
wrapper.__name__ = func.__name__
return wrapper
@decroate_func
def test1():
pass
@decroate_func
def test2():
pass
print(test1.__name__)
print(test2.__name__)
运行结果:
test1
test2
解决方法二:
使用Python自带的functools模块中定义的@wraps(func)装饰器可以将属性从func传递给要定义的包装器函数。
这种方法将所有的属性都进行了修改,操作简单,而且不用担心哪个属性忘记修改了
from functools import wraps
def decroate_func(func):
@wraps(func)
def wrapper():
pass
return wrapper
@decroate_func
def test1():
pass
@decroate_func
def test2():
pass
print(test1.__name__)
print(test2.__name__)
运行结果:
test1
test2