什么是Python 的 函数装饰器?
Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property;
那么,函数装饰器的工作原理是怎样的呢?
假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:
#funA 作为装饰器函数
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
@funA
def funB():
#...
实际上,上面程序完全等价于下面的程序:
def funA(fn):
#...
fn() # 执行传入的fn参数
#...
return '...'
def funB():
#...
funB = funA(funB)
通过比对以上 2 段程序不难发现,使用函数装饰器 A() 去装饰另一个函数 B(),其底层执行了如下 2 步操作:
- 将 B 作为参数传给 A() 函数;
- 将 A() 函数执行完成的返回值反馈回 B。
举个栗子:
#funA 作为装饰器函数
def funA(fn):
print("Python简明教程")
fn() # 执行传入的fn参数
print("http://c.lier.net")
return "装饰器函数的str类型返回值"
@funA
def funB():
print("学习 Python")
由于这种定义方式相当于funB = funA(funB);所以程序可以直接运行。
程序执行结果为:
Python简明教程
学习 Python
http://c.lier.net
在此基础上,如果在程序末尾添加如下语句:
print(funB)
此时被饰的函数名funB 就变成了str类型的变量名;
其输出结果为:
装饰器函数的str类型返回值
显然,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值),即如果装饰器函数的返回值为普通变量,那么被修饰的函数名就变成了变量名;同样,如果装饰器返回的是一个函数的名称,怎么被修饰的函数名依然表示一个函数。
实际上,所谓函数装饰器,就是通过装饰器函数,在不修改原函数的前提下,来对函数的功能进行合理的扩充。
Python 内置的 3 种函数装饰器
一、@property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
# -*- encoding:utf-8 -*-
import math
class Circle:
def __init__(self,radius):
self.radius =radius
@property
def area(self):
return math.pi*self.radius**2 #计算圆的面积
@property
def perimeter(self):
return 2*math.pi*self.radius #计算圆的周长
c = Circle(5)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter)
"""
78.53981633974483
31.41592653589793
"""
注意:此时的特性arear和perimeter不能被赋值,一旦赋值就会报错
二、@staticmethod
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错。
staticmethod是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
举个栗子:
class Foo:
@staticmethod
def test(x,y,z):
print(x,y,z)
print(type(Foo.test))#类型本质就是函数
Foo.test(1,2,3) #调用函数应该有几个参数就传几个参数
f1=Foo()
f1.test(3,67,99) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制
"""
<class 'function'>
1 2 3
3 67 99
"""
什么时候使用静态方法呢?
编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
class Date:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
@staticmethod
def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
t=time.localtime() #获取结构化的时间格式
return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
@staticmethod
def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
t=time.localtime(time.time()+86400)
return Date(t.tm_year,t.tm_mon,t.tm_mday)
a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
三、@classmethod
举个栗子:
class A:
x=1
@classmethod
def test(cls):
print(cls,cls.x)
class B(A):
x=2
B.test()
'''
输出结果:
<class '__main__.B'> 2
'''
什么时候使用类方法呢?
# -*- encoding:utf-8 -*-
import time
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@staticmethod
def now():
t = time.localtime()
return Date(t.tm_year,t.tm_mon,t.tm_mday)
class EuroDate(Date):
def __str__(self):
return "year:%s month:%s day:%s" %(self.year,self.month,self.day)
e = EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
'''
输出结果:
<__main__.Date object at 0x00000000021CCCC0>
'''
因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
import time
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod #改成类方法
def now(cls):
t = time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday)
class EuroDate(Date):
def __str__(self):
return "year:%s month:%s day:%s" %(self.year,self.month,self.day)
e = EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
'''
输出结果:
year:2018 month:9 day:29
'''
这样我们运用类方法,为now()函数找到了归属,即使是继承也能找到自己属于哪个类;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)