Python中的装饰器是通过利用了函数特性的闭包实现的,所以在说装饰器之前,我们需要先了解函数特性,以及闭包是怎么利用了函数特性的。
1.函数特性
Python中的函数特性总的来说有四点:
1.函数作为变量传递
def add(x):return x + 1a = add
2.函数作为参数传递
def add(x):return x + 1def excute(f):return f(3)excute(add)
3.函数作为返回值
def add(x):return x + 1def get_add():return add
4.函数嵌套及跨域访问
def outer():x = 1def inner():print(x) inner()outer()
2.闭包的实现
Python中的装饰器是通过闭包实现的,简单地讲,闭包就是引用了外部变量的内部函数,而闭包的实现正是利用了以上函数特性,下面我们来看看闭包是如何实现的:
def outer(x):def inner(): # 函数嵌套return x # 跨域访问,引用了外部变量xreturn inner # 函数作为返回值closure = outer('外部变量') # 函数作为变量赋给closure
print(closure()) # 执行闭包#结果
外部变量
在这个流程中,outer接收到'外部变量',传给inner,作为它return的参数,最后outer返回inner函数,返回的inner函数作为变量传递给closure,最后执行closure这个函数对象,实际上是执行了inner这个函数,返回了 '外部变量',这样就实现了一个简单的闭包。
上面这个闭包例子只用到了之前说的三个函数特性,函数作为参数这个特性好像并没有用上,下面做一下延伸,把outer的参数x用一个函数对象替代
def func():return '函数func'def outer(x):def inner(): # 函数嵌套return '戴了inner牌帽子的 ' + x() # 跨域访问,引用了外部变量xreturn inner # 函数作为返回值closure = outer(func) # 函数func作为outer的参数;函数作为变量赋给closureprint(func()) # 执行原始函数
print(closure()) # 执行闭包# 结果
函数func
戴了inner牌帽子的 函数func
观察上面的例子,从func()到closure(),函数func就是被装饰了一番变成了clousre,分析一下具体过程:
closure实际上是outer(func),func作为参数传进outer,outer的子函数inner对func返回的结果进行了一番装饰,返回了一个装饰后的结果,最后outer返回inner,可以说inner就是装饰后的func,这就是一个函数被装饰的过程,重点在于执行 outer(func) 这个步骤。
3.装饰器语法糖@
Python给我们提供了语法糖 @,我们想执行 outer(func) 的时候,只需要把outer函数@到func函数的上面就可以了。
def outer(x):def inner():return '戴了inner牌帽子的 ' + x()return inner@outer
def func():return '函数func'print(func())#结果
戴了inner牌帽子的 函数func
打印的结果跟我们执行closure()的结果是一样的,也就说明 加了outer装饰器的func 等价于 outer(func),所以我们很清楚地知道装饰器@的作用是什么了,就是拿来把被装饰的函数作为参数传递到装饰器函数里面加工的,最后执行被装饰函数的时候,就相当于执行了一个加工后的函数。
以上就是Python中装饰器的实现原理。
4.装饰器的使用
Python中装饰器用处很多,下面写一个例子感受一下.
类中的私有属性在类外部是无法访问的(就当无法访问好了,毕竟鬼叔是不推荐访问的),而这时可以在类中定义一个方法返回这个私有属性然后在外部调用就可以得到这个私有属性,但是这样看起来就和正常的调用属性的方式不一样了(obj.属性),这时候就可以用@property来实现想要的效果.
class Person:def __init__(self,name,age):self.name = nameif type(age) is int:self.__age = ageelse:print( '你输入的年龄的类型有误,请输入数字')@propertydef age(self):return self.__age@age.setterdef age(self,a):'''判断,你修改的年龄必须是数字'''if type(a) is int:self.__age = aelse:print('你输入的年龄的类型有误,请输入数字')@age.deleterdef age(self):del self.__agep1 = Person('帅哥',20) print(p1.age) del p1.age