Python小技巧:装饰器(Decorator)及内嵌函数 与 闭包
装饰器
本质上也是一个python函数,它可以让其他函数在不做任何代码变动的前提下额外增加的功能,装饰器的返回值也是一个函数对象,它经常用于用于有[切面要求]的场景,比如:插入日志,性能测试,事务处理,缓存,权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量的与函数本身功能无关的雷同代码并继续重用.
执行被装饰函数的时候,程序将自动先执装饰器函数,然后依次执行
利用装饰器 实现2-10000 间的质数输出 并且 返还质数个数 :
import time
# 定义装饰器函数 将被装饰函数以参数形式传入
def display_time(func):
# 此处因为被装饰函数有参数 所以此处要携带参数
def wrapper(max_mum):
t1 = time.time()
# (2) 因为被装饰函数有返回值所以此处要重新赋值给一个 待返还 对象
result = func(max_mum)
t2 = time.time()
print(t2 - t1)
# (3) 因为被装饰函数有返回值 故此处要将 其返还 若不返还此处为 None
return result
return wrapper
def is_prime(num):
if num<2:
return False
elif num==2:
return True
else:
for i in range(2,num):
if num%i==0:
return False
return True
#使用 display_time 函数进行装饰
@display_time
def prime_nums(max_mum):
count = 0
for i in range(2,max_mum):
if is_prime(i):
count = count+1
print(i)
# (1) 当被装饰函数有返回值的时候 装饰器中被传过去后 也要将 func重新赋值给一个 对象最后返还
return count
# 实例化函数对象 并传入参数
count = prime_nums(5000)
# 输出retern返回的 计算质数个数的值
print(count)
https://www.bilibili.com/video/av25698102
python 闭包 closure 总结
1.内嵌函数的非本地变量
在另一个函数里面定义的函数,被称为内嵌函数.内嵌函数可以访问闭合范围内(就是外部函数范围)的变量,这些变量被称为非本地变量(nonlocal variable)
默认情况下,非本地变量是只读的.为了可以修改非本地变量,需要将他们声明为 nonlocal,如下例所示。
def print_msg(msg):
#"""This is the outer enclosing function"""
def printer():
#"""This is the nested function"""
print(msg)
printer()
# We execute the function
# Output: Hello
print_msg("Hello")
可以看到,内嵌函数是printer(),可以访问本地变量msg,msg定义在外部函数print_msg()里面。
2.定义一个闭包函数
在上面的例子中,如果print_msg()返回print()函数,而不是调用它,会发生什么?这要求函数被这样定义
def print_msg(msg):
"""This is the outer enclosing function"""
def printer():
"""This is the nested function"""
print(msg)
return printer # this got changed
# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()
print_msg()函数被通过'''Hello'所调用,返回的函数被绑定为another.在调用another()的时候,我们对print_msg()函数已经完成调用了,但是''Hello''仍然被记住了!
这种将一些数据(''Hello'')附加到了代码的技术,被称为python里面的closure.
闭合范围内的数据(非本地变量)能够一直被记住,即便它们已经脱离了必和范围或者外部函数已经被从命名空间删除。
在python shell 里面继续运行下面的代码,看看会发生什么.
- >>> del print_msg
- >>> another()
- Hello
- >>> print_msg("Hello")
- Traceback (most recent call last):
- ...
- NameError: name 'print_msg' is not defined
3.怎样得到一个闭包函数?
从上面的例子可以看出,当我们让内嵌函数引用了一个非本地变量,就得到了一个python closure.
python closure 必须满足一下三点标准:
1)必须有一个内嵌函数(函数里定义的函数)
2)内部函数必须引用一个定义在闭合范围内(外部函数里)的变量
3)外部函数必须返回内嵌函数
4.什么时候使用closure?
closure适合做什么?
closure 可以减少使用全局变量和提供一定程度的数据隐藏。
当一个类只有很少的方法(通常是一个),closure可以提供一种更优雅的替代方案。但是如果类的属性或者方法开始增多,最好还是实现一个类。
下面是一个closure也许比类更好的一个例子。当然,到底哪个更好最终还是取决于自己。
def make_multiplier_of(n):
def multiplier(x):
return x * n
return multiplier
# Multiplier of 3
times3 = make_multiplier_of(3)
# Multiplier of 5
times5 = make_multiplier_of(5)
# Output: 27
print(times3(9))
# Output: 15
print(times5(3))
# Output: 30
print(times5(times3(2)))
5.获取闭合数值
所有在外部函数定义的非本地变量,都可以被获取到。
所有的函数对象都有一个__closure__属性,如果它是一个闭包函数,那么它包含一个cell objects 元组。
就上面的例子,我们知道time3和time5是闭包函数。
- >>> make_multiplier_of.__closure__
- >>> times3.__closure__
- (<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
cell object有cell_contents属性,保存了闭合数值
- >>> times3.__closure__[0].cell_contents
- 3
- >>> times5.__closure__[0].cell_contents
- 5