Python 装饰器 & 闭包
文章目录
参考文章
理解装饰器和闭包
明确python变量作用域
按下边的函数嵌套示例:
- 读变量和C语言是一样的,首先搜本层楼,搜不到就下楼
- 改【下层楼】变量,要加关键字:
- global 用于函数或其他局部作用域中,声明全局变量
- nonlocal 用于内部嵌套函数中,声明外部嵌套函数变量
参考文章
建议,不论读或改,最好都声明变量的作用域。
msg = '我是一楼'
def secondFloor():
# global msg
# msg += 'hello' # 修改一楼msg
msg = '我是二楼'
def thirdFloor():
# global msg # 读出一楼msg
# nonlocal msg # 读出二楼msg
print(msg)
闭包
在函数嵌套的前提下:
- 内部函数使用了外部函数的变量
- 并且外部函数返回了内部函数的引用(即内部函数的内存地址)
其中的内部函数称为闭包。
def secondFloor():
msg = '我是二楼'
def thirdFloor():
print(msg)
return thirdFloor # 注意这里不加()
# 调用闭包
sf = secondFloor()
sf()
# ------------------------------------------------------------------
# 如果给secondFloor一个形参,也可满足闭包的第一个条件
def secondFloor(arg):
def thirdFloor():
print(arg)
return thirdFloor # 注意这里不加()
# 调用闭包
info = 'hello'
sf = secondFloor(info)
sf()
装饰器
装饰器的实现基于闭包。
闭包是指嵌套函数的内部函数。装饰器指嵌套函数的外部函数。
def secondFloor(func):
# func就是内部函数所使用的外部函数变量
def thirdFloor(arg):
print('原函数开始')
func(arg)
print('原函数结束')
return thirdFloor
@secondFloor
def origin(info):
#print('原函数')
print(info)
# 装饰器基本使用
# sf = secondFloor(origin)
# sf()
# 通过python语法糖,执行origin()会自动调用装饰器
info = 'hello'
origin(info)
装饰器调用起来更灵活和更方便,我们不需要修改原函数代码,甚至不用移动原代码块的位置,就可对函数增加一些附加功能。
闭包和装饰器的区别
功能上:两种都是用来实现函数的扩展的。
结构上:闭包的前提是函数嵌套,装饰器的实现则基于闭包的。但是,闭包是指嵌套函数的内部函数。装饰器指嵌套函数的外部函数。
实际应用
1.用闭包实现一个指数函数
# 闭包函数,其中 exponent 称为自由变量
def nth_power(exponent):
def exponent_of(base):
return base ** exponent
return exponent_of
square = nth_power(2) # 平方函数
cube = nth_power(3) # 立方函数
print(square(2)) # 计算 2 的平方
print(cube(2)) # 计算 2 的立方
当然指数函数也可用简单的方式实现:
def nth_power_rewrite(base, exponent):
return base ** exponent
# 不使用闭包
res1 = nth_power_rewrite(base1, 2)
res2 = nth_power_rewrite(base2, 2)
res3 = nth_power_rewrite(base3, 2)
对比可发现,使用闭包,可以让程序变得更简洁易读
闭包将那些额外工作的代码放在外部函数,就可以减少多次调用导致的不必要开销,提高程序的运行效率。
进阶
闭包的__closure__属性
闭包比普通的函数多了一个 closure 属性,该属性记录着自由变量的地址。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用。
def nth_power(exponent):
def exponent_of(base):
return base ** exponent
return exponent_of
square = nth_power(2)
#查看 __closure__ 的值
print(square.__closure__)
# 输出结果为:
# (<cell at 0x0000014454DFA948: int object at 0x00000000513CC6D0>,)
自定义装饰器
函数装饰器主要用于修饰一个函数
用于修改对象的装饰器
给被装饰的对象,添加一个name属性并且设置值为python
def warp(obj):
obj.name = 'python'
return obj
@warp # => Bar = warp(Bar)
class Bar(object):
def __init__(self):
pass
print(Bar.name) # => python
用于模拟对象的装饰器
函数装饰器
上面例子中的装饰器,是直接修改了传入对象;而装饰器最常用的方式却是模拟一个传入对象。即返回一个和原对象相似的对象(即调用接口完全一样的另一个对象),并且该模拟对象是包装了原对象在内的。
def outer(func): # 函数装饰器
def inner():
print('hello inner')
func()
return inner
@outer # foo = outer(foo)
def foo():
print('hello foo')
print(foo.__name__)
foo() # 其实质上是执行的inner()
类方法装饰器
def outer(obj): # 类方法装饰器
def inner(self):
print('hello inner')
obj(self)
return inner
class Zoo(object):
def __init__(self):
pass
@outer # => zoo = outer(zoo)
def zoo(self):
print('hello zoo')
zoo = Zoo()
print(zoo.zoo.__name__)
zoo.zoo()
唯一的区别就是多了一个默认的self参数
类装饰器
待补充
Python内置装饰器
三种类方法装饰器
@classmethod
类方法,不需要实例化,也不需要self参数,但需要一个cls参数,可以用类名调用,也可以用对象来调用。
@staticmethod
静态方法,不需要实例化,不需要self和cls等参数,就跟使用普通的函数一样,只是封装在类中
在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数
class Foo(object):
@classmethod
def cls_fun(cls):
return "hello world"
@staticmethod
def static_fun():
return "hello python"
foo = Foo()
print(Foo.cls_fun())
# print(foo.cls_fun())
print(Foo.static_fun())
@property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
@property 是经典类中的一种装饰器,新式类中具有三种:
1.@property获取属性
2.@方法名.setter 修改属性
3.@方法名.deleter 删除属性@property的使用:
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价*折扣
new_price = self.original_price*self.discount
return new_price
@price.setter
def price(self,value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
# print(obj.price)
obj.price = 200
print(obj.price)
del obj.price # 删除了类中的price属性若再次调用就会报错