概述
闭包是指在一个函数中定义了一个另外一个函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。 闭包的使用,可以隐藏内部函数的工作细节,只给外部使用者提供一个可以执行的内部函数的引用。
装饰器是用于拓展已有函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,实际上就是利用闭包语法实现的。
使用装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
闭包
n = 1 # 全局变量
def show(): # 公有函数
print('show: ',n)
def callFunc(func): #公有函数
return func
s = callFunc(show) # 函数执行
s()
show()
>>>show
show
# 改进 # show() 因为 show 函数定义在 callFunc 内部,所以外部不可见,不能使用
def callfunc():
n = 1
def show():
print("show", n)
return show
s = callfunc()
s()
s()
闭包实现棋子移动
'''闭包实现棋子移动'''
# 定义一个外部函数
def outer():
# 在外部函数中定义一个保存坐标的列表
position = [0,0]
# 定义一个内部函数,参数为移动方式和步长
# 移动方式为列表 [x,y] x,y分别只能取 -1,0,1三个值,表示反向,不动,正向
def inner(direction,step):
# 计算坐标值
position[0] = position[0] + direction[0] * step
position[1] = position[1] + direction[1] * step
# 返回移动后的坐标
return position
# 返回内部函数
return inner
# 获取内部函数
move = outer()
# 移动
print(move([1, 0], 10))
print(move([0, 1], 10))
print(move([-1, 0], 10))
nolocal
nonlocal 的使用 如果在闭包的内部函数中直接使用外部函数的变量时,不需要任何操作,直接使用就可以了。
但是如果要修改外部变量的值,需要将变量声明为 nonlocal
装饰器
装饰器实际上就是一个函数 ,这个函数以闭包的形式定义
在使用这个装饰器函数时,在被装饰的函数的前一行,使用 @装饰器函数名 形式来装饰
# 这种形式实际上就是塌装饰器的实现原理。
import time
def my_count():
res = 0
for i in range(10000):
res += i
print(res)
def count_time(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('all spend time is %s' % (end_time - start_time)) # 使用%d会取整,不利于观察
return wrapper
my_count = count_time(my_count)
my_count()
>>>49995000
all spend time is 0.0006196498870849609
另一种实现方法
import time
#在执行 @xxx 时 ,实际就是将 原函数传递到闭包中,然后原函数的引用指向闭包返回的装饰过的内部函数的引用。
def count_time(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('all spend time is %s' % (end_time - start_time)) # 使用%d会取整,不利于观察
return wrapper
@count_time
def my_count():
res = 0
for i in range(10000):
res += i
print(res)
my_count()
万能装饰器
def setFunc(func):
def wrapper(*args, **kwargs):
print("wrapper context")
return func(*args, **kwargs)
return wrapper
@setFunc
def show(name, age):
print(name, age)
show('yang', 18)
>>>wrapper context
yang 18
类装饰器
在类中通过使用 init 和 call方法来实现
class Test(object):
# 通过初始化方法,将要被装饰的函数传进来并记录下来
def __init__(self, func):
self.__func = func
# 重写 __call__ 方法来实现装饰内容
def __call__(self, *args, **kwargs):
print('wrapper context')
self.__func(*args, **kwargs)
# 实际通过类的魔法方法call来实现
@Test # --> show = Test(show) show由原来引用函数,装饰后变成引用Test装饰类的对象
def show():
pass
show() # 对象调用方法,实际上是调用魔法方法call,实现了装饰器
函数被多个装饰器所装饰
def setFunc1(func):
def wrapper():
print("wrapper context 1 start")
func()
print("wrapper context 1 end")
return wrapper
def setFunc2(func):
def wrapper():
print("wrapper context 2 start")
func()
print("wrapper context 2 end")
return wrapper
@setFunc1
@setFunc2
def show(*args, **kwargs):
print("show is run ...")
show()
>>>wrapper context 1 start
wrapper context 2 start
show is run ...
wrapper context 2 end
wrapper context 1 end
这个装饰器在时,过程是
从下向上装饰,因为没有实际存在的函数,装饰器就没有意义。
所以先装饰函数,然后再一层一层装饰。
@setFunc2 -> show = setFunc2(show) -> show = setFunc2.wrapper2 @setFunc1 -> show = setFunc1(setFunc2.wrapper2) -> show = setFunc1.wrapper1(setFunc2.wrapper2(show))
_bianliang 文件私有变量
__bianliang 类私有属性
private和protect本来无区别,只是在继承的时候:
private 继承 __ 本身类中可以使用
protect 继承_ 子类中也可以使用
变量名_ 是为了避免冲突