Python的函数装饰器,@staticmethod、@classmethod 和 @property

什么是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 步操作:

  1. 将 B 作为参数传给 A() 函数;
  2. 将 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()函数找到了归属,即使是继承也能找到自己属于哪个类;

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值