闭包
当有两个函数嵌套,且内层函数使用了外部函数的变量,且外层函数以内层函数为返回值,则称这个内层函数为闭包(closure),外层函数称为闭包函数。
闭包的作用是生成一个函数,这个函数可以以外部函数的局部变量为基点,进行各种操作。
闭包使得外层函数的局部变量在外部函数调用完后,不会被系统自动销毁。只有闭包调用完后才会销毁。
闭包的简单使用
def out_func(....):
#...
def inner_func(....):
#...(使用out_func中的局部变量)
return inner_func()
如:
def n_power(n): #外部函数
exponent=n
def power_of(base): #内部函数
result = base**exponent #使用外部函数的局部变量exponent
return result
return power_of #返回内部函数
闭包作为函数被返回,这使得外层函数就像一个函数生产器:以自己的变量(参数)为基准,制造出一个新函数。
square=n_power(2) #根据参数装配新函数
cube=n_power(3)
print(square(4))
#16
print(cube(3))
#27
又如:
def person_speak(name):
def speak(words):
print(f'{name} said:"{words}"')
return speak
jack_speak = person_speak("Jack")
rose_speak = person_speak("Rose")
rose_speak("I promise.")
jack_speak("Never let go.")
rose_speak("I'll never let go.I'll never let go,Jack.")
"""
Rose said:"I promise."
Jack said:"Never let go."
Rose said:"I'll never let go.I'll never let go,Jack."
"""
相比于直接用一个函数,两个参数,speak(name,words),闭包的代码更清晰,也更实用
在闭包内修改外部局部变量的值
类似于用关键字global
声明全局变量,用nonlocal
声明外部变量。
如:
def out_func():
i=5
def inner_func():
i=6
inner_func()
print(i)
return inner_func
out_func()
# 5
就跟在函数内直接重新定义全局变量一样,这只是为内层函数自己重新创建了一个新的变量i而已,并不是在修改外层函数的i.
def out_func():
i=5
def inner_func():
nonlocal i #声明i为外层函数的i
i=6
inner_func()
print(i)
return inner_func
out_func()
# 6
装饰器
装饰器是一个函数,它以已有的函数作为参数,再通过闭包在已有函数的基础上增加新的代码(功能),并将装饰后的函数返回。
特点
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加新功能
- 可重复利用
使用方法
#定义装饰器
def decorator(func_to_decotate):
def decorated(*args, **kwargs):
#...(额外功能)
result=func_to_decotate(*args, **kwargs)
#...(额外功能)
return result
return decorated
#使用装饰器之普通版本
def func_to_decotate(a,b,c):
#...(已有函数)
decorated_func=decorator(func_to_decotate) #获取装饰完后的新函数
decorated_func(...) #调用装饰完后的新函数
#使用装饰器之语法糖版本
@decorator #相当于自动执行func_to_decotate=decorator(func_to_decotate=decorator)
def func_to_decotate(a,b,c):
#...(已有函数)
- 装饰后的函数即为装饰器内部的闭包
- 在函数定义前加上
@decorator
,可以直接获得装饰完的函数,且函数名不变
如:
def lines(func):
def inner(*args, **kwargs):
print("*"+"-"*18+"*")
result=func(*args, **kwargs)
print("-"*20)
print("*"+"-"*18+"*")
return result
return inner
#普通版本
def print1(words):
print(words)
print1=lines(print1)
print1("Hello,world!")
"""
*------------------*
Hello,world!
--------------------
*------------------*
"""
#语法糖版本
@lines
def print2(words):
print(words)
print2("Hello,python!")
"""
*------------------*
Hello,python!
--------------------
*------------------*
"""
运行时间装饰器
import time
def timer(func):
def inner(*args, **kwargs):
print("*" * 2 + "-" * 16 + "*" * 2)
begin_time = time.time()
result=func(*args, **kwargs)
end_time = time.time()
run_time = end_time - begin_time
print(f"运行时间:{run_time:.2f} s")
print("*" * 2 + "-" * 16 + "*" * 2)
return result
return inner
@timer
def test(words):
for i in range(50000000):
if i == 4000:
print(words)
test("Python")
"""
**----------------**
Python
运行时间:1.78 s
**----------------**
"""
多个装饰器
@decorator1
@decorator2
def func(...):
...
等价于
func=decorator2(func)
func=decorator1(func)
- 即多个装饰器由内而外依次进行装饰
如:
import time
def lines(func):
def inner(*args, **kwargs):
print("*"+"-"*18+"*")
result=func(*args, **kwargs)
print("-"*20)
print("*"+"-"*18+"*")
return result
return inner
def timer(func):
def inner(*args, **kwargs):
begin_time = time.time()
result=func(*args, **kwargs)
end_time = time.time()
run_time = end_time - begin_time
print(f"运行时间:{run_time:.2f} s")
return result
return inner
@lines
@timer
def test1(words):
for i in range(50000000):
if i == 4000:
print(words)
@timer
@lines
def test2(words):
for i in range(50000000):
if i == 4000:
print(words)
test1("Python")
"""
*------------------*
Python
运行时间:1.67 s
--------------------
*------------------*
"""
test2("Python")
"""
*------------------*
Python
--------------------
*------------------*
运行时间:1.65 s
"""
含参装饰器
def decoratorName(param1,param2...):
def decorator(func):
def decorated(*args, **kwargs):
#...(额外功能,可能用到param1,param2...)
result=func_to_decotate(*args, **kwargs)
#...(额外功能,可能用到param1,param2...)
return result
return decorated
return decorator
@decoratorName(param1,param2....)
def func_to_decotate(a,b,c):
...
- 要想在使用装饰器时能够传入参数,可以在 原本的装饰器函数 外面再嵌套一层以 原本的装饰器函数 为返回值的函数
- 相当于先执行
decorator=decoratorName(param1,param2....)
再@decorator
如:
def math(flag):
def decorator(func):
def inner(*args,**kwargs):
if flag == "+":
print("---------加法运算中--------")
if flag == "*":
print("---------乘法运算中--------")
result=func(*args,**kwargs)
return result
return inner
return decorator
@math("+")
def plus(*args):
s=0
for i in args:
s+=i
return s
plus(1,2,3,4,5)
"""
---------加法运算中--------
15
"""
@math("*")
def multipy(*args):
s=1
for i in args:
s*=i
return s
multipy(1,2,3,4,5)
"""
---------乘法运算中--------
120
"""
类装饰器
重写了__call__
魔法方法后,类也能像函数一样进行调用。
class Decorator(object):
def __init__(self, func):
#加载需装饰的函数
self.__func = func
# 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
def __call__(self, *args, **kwargs): #__call__即为装饰后的新函数
#...(额外功能)
result=self.__func(*args, **kwargs)
#...(额外功能)
return result
@Decorator
def func(a,b,c):
#...已有函数
@Decorator
相当于func=Decorator(func)
,创建了一个名为func的Decorator实例。而Decorator的实例因为有了__call__
方法,所以可像函数一样可调用。