python中classmethod、propetry、staticmethod的区别

  • classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

class A(object):
    bar = 1
    def func1(self):  
        print ('foo') 
    @classmethod
    def func2(cls):
        print ('func2')
        print (cls.bar)
        cls().func1()   # 调用 foo 方法

A.func2()               # 不需要实例化
func2
1
foo
  • 再来举一个例子↓
class A(object):

    # 属性默认为类属性(可以给直接被类本身调用)
    num = "类属性"

    # 实例化方法(必须实例化类之后才能被调用)
    def func1(self): # self : 表示实例化类后的地址id
        print("func1")
        print(self)

    # 类方法(不需要实例化类就可以被类本身调用)
    @classmethod
    def func2(cls):  # cls : 表示没用被实例化的类本身
        print("func2")
        print(cls)
        print(cls.num)
        cls().func1()

    # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
    def func3():
        print("func3")
        print(A.num) # 属性是可以直接用类本身调用的

# A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
A.func2()
print('---------------------')
A.func3()
func2
<class '__main__.A'>
类属性
func1
<__main__.A object at 0x0000000005C75550>
---------------------
func3
类属性
  • 看下面的定义的一个时间类:
class Data_test(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year

    def out_date(self):
        print ("year :")
        print (self.year)
        print ("month :")
        print (self.month)
        print ("day :")
        print (self.day)
t=Data_test(2016,8,1)
t.out_date()
year :
2016
month :
8
day :
1

符合期望。

如果用户输入的是 “2016-8-1” 这样的字符格式,那么就需要调用Date_test 类前做一下处理:

string_date='2016-8-1'
year,month,day=map(int,string_date.split('-'))
s=Data_test(year,month,day)

先把‘2016-8-1’ 分解成 year,month,day 三个变量,然后转成int,再调用Date_test(year,month,day)函数。 也很符合期望。

那我可不可以把这个字符串处理的函数放到 Date_test 类当中呢?

那么@classmethod 就开始出场了

class Data_test2(object):
    day=0
    month=0
    year=0
    def __init__(self,year=0,month=0,day=0):
        self.day=day
        self.month=month
        self.year=year

    @classmethod
    def get_date(cls,
string_date):
        #这里第一个参数是cls, 表示调用当前的类名
        year,month,day=map(int,string_date.split('-'))
        date1=cls(year,month,day)
        #返回的是一个初始化后的类
        return date1

    def out_date(self):
        print ("year :")
        print (self.year)
        print ("month :")
        print (self.month)
        print ("day :")
        print (self.day)

在Date_test类里面创建一个成员函数, 前面用了@classmethod装饰。 它的作用就是有点像静态类,比静态类不一样的就是它可以传进来一个当前类作为第一个参数。

那么如何调用呢?

r=Data_test2.get_date("2016-8-6")
r.out_date()
year :
2016
month :
8
day :
6

这样子等于先调用get_date()对字符串进行处理,然后才使用Data_test的构造函数初始化。

这样的好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以了。

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)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

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:2019 month:4 day:13
  • 特性(property)
    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
 import math
class Circle:
     def __init__(self,radius): #圆的半径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(10)
 print(c.radius)
 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
 print(c.perimeter) #同上
10
314.1592653589793
62.83185307179586

注意:此时的特性arear和perimeter不能被赋值

c.area=3 #为特性area赋值
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-17-c6299e10c387> in <module>
----> 1 c.area=3 #为特性area赋值


AttributeError: can't set attribute
  • (静态方法) staticmethod
    主要是方便将外部函数集成到类体中,美化代码结构,重点在不需要类实例化的情况下调用方法

    如果你去掉staticmethod,在方法中加self也可以通过实例化访问方法也是可以集成代码

class A:
    @staticmethod
    def staticfunc():
        A.aa = 100
        print("这是一个静态方法")
A.staticfunc() # A
print(A.aa)

class B:
    @classmethod
    def classfunc(cls):
        cls.a = 200
        print('这是一个类方法')

B.classfunc() 
print(B.a) # 10
这是一个静态方法
100
这是一个类方法
200

通过上面的例子我们可以看到静态方法也可以操作类本身,为什么还要在发明一个类方法?上面例子我们观察到,静态方法是通过类名来操作类属性的写死在程序中,而类方法是通过参数来操作类属性的,如果子类继承了使用静态方法的类,那么子类继承的静态方法还是在操作父类,子类需要重新静态方法才能操作子类,类方法如果被继承,那么类参数会传入子类本身。

应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个init函数,此时静态方法就派上用场了

import time

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)
1987 11 27
2019 4 13
2019 4 14


   转载规则


《python中classmethod、propetry、staticmethod的区别》 旋律JOJO哒 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录