@property、@staticmethod和@classmethod应用与区分

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以对属性随便修改,不能限制输入。

 1 class Student(object):
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5 
 6 s = Student('Mitsui', 24)
 7 s.name = 123
 8 s.age = '青年'
 9 print(s.name)
10 print(s.age)
11 >>>>
12 123
13 青年

这显然不合逻辑,也许名字可以叫123,但是年龄是青年就是有问题了。对于追求完美的Python程序员来说,这时候就需要引用一个装饰器 @property.

class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    @property      #引用装饰器property
    def age(self):
        return self.__age
    @age.setter    #函数修饰后可以作为装饰器使用调用后可以修改原始数据
    def age(self,value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')  #非int类型抛异常
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!') #年龄基本都是0-100以内,限制输入
        self.__age = value

s = Student('Mitsui', 24) #当age参数传入24,age会去找被property修饰的age 实际上age = self.__age而非self.age
print(s.name,s.age)       #也就是age参数会传入age()方法中的value参数,开始执行age.setter,实现规范输入的目的,
s.age = 18                #同时在调用时,把 age()方法变成一个可以赋值的属性,让调用者更加方便的调用
s.name = 'BOBO'
print(s.name)
print(s.age)            #没有赋值实际上执行了age方法下的 ‘return self.__age’

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

 

@classmethod

classmethod是用来指定一个类的方法为类方法,没有此参数指定的类的方法为实例方法,使用方法如下:

class C:
    @classmethod
    def f(cls,arg1,arg2, ...):
    ...

类方法既可以直接类调用(C.f()),也可以进行实例调用(C().f()),即 s=c(), s.f()

让我们来看一段代码:

class Kls(object):
    no_inst = 0
    def __init__(self):
        Kls.no_inst = Kls.no_inst + 1
    @classmethod
    def get_no_of_instance(cls):    #调用了@classmethod之后,定义方法会自动传一个cls参数,表示可以调用类
        return cls.no_inst            #所以这里 cls.no_inst相当于Kls.no_inst
ik1 = Kls()
ik2 = Kls()
print (ik1.get_no_of_instance())      #调用时既可以直接类调用,也可以用实例化的对象来调用里面的方法。
print (Kls.get_no_of_instance())

输出:
2
2
这样的好处是: 不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来.

再看一段代码:

import time   #引用时间模块
class Date():
    def __init__(self,year,month,day):   #定义一个时间,调用类或者实例化时可以传参
        self.year = year
        self.month = month
        self.day = day

    @classmethod   #@classmethod 可以调用类
    def now(cls):      #用cls把类传递过来
        print(cls)          #打印cls,验证是哪个类在调用它
        t = time.localtime()      #获得有格式的时间
        obj = cls(t.tm_year,t.tm_mon,t.tm_mday)     #将时间的对应参数按装饰后的cls传入类属性,隐藏丑陋的接口
        return obj                                 #调用时可以直接self.year 即 cls.year , cls.month如此调用
    @classmethod
    def Euro(cls):            #定义一个欧洲时间,以伦敦为例
        t=time.localtime(time.time()-28800)      #伦敦时间与北京时间差8个时区,减去计算机识别对应的秒数
        return cls(t.tm_year,t.tm_mon,t.tm_mday)    #同上

class EuroDate(Date):  #可以定义一个子类继承自Date类
    def __str__(self):   #__str__ 定义在类内部,必须返回一个字符串类型,打印由这个类产生的对象时,会触发执行
        return '现在是%s年%s月%s日from %s' % (self.year,self.month,self.day,__class__.__name__) #格式化输出这个str

n = Date(1,1,1)  #由于定义类时设置了3个参数,所以这里实例化要传参
n1 = n.now()    #经过@classmethod 实例一样可以像类一样调用内部方法。
print(n1.year,n1.month,n1.day)
e1 = EuroDate.Euro()  #使用子类调用继承的父类的方法
print(e1)
View Code

输出:

<class '__main__.Date'>
2017 4 22
<class '__main__.EuroDate'>
现在是2017年4月21日from EuroDate

@staticmethod

staticmethod  #类的工具包 给类用,函数不用传值,不用绑定传self,都可以调用这个函数
还是差不多相同的例子:
import time

class Date():
    def __init__(self,year,month,day):
        self.year = year
        self.month = month
        self.day = day

    @staticmethod
    def now():
        n = time.localtime()
        nowtime = Date(n.tm_year,n.tm_mon,n.tm_mday)
        return nowtime

    @staticmethod
    def tomorrow():
        t = time.localtime(time.time()+86400 )
        t_time = Date(t.tm_year,t.tm_mon,t.tm_mday)
        return t_time
d1 = Date(1,1,1)
nowtime = d1.now()
print(nowtime.year,nowtime.month,nowtime.day)

>>>>
2017 4 22
View Code

再来看这个例子看看两者的不同:

作者:李保银
链接:https://www.zhihu.com/question/20021164/answer/18224953
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Kls(object):
    def __init__(self, data):
        self.data = data
    def printd(self):
        print(self.data)
    @staticmethod
    def smethod(*arg):
        print('Static:', arg)
    @classmethod
    def cmethod(*arg):
        print('Class:', arg)
 
>>> ik = Kls(23)
>>> ik.printd()
23
>>> ik.smethod()
Static: ()
>>> ik.cmethod()
Class: (<class '__main__.Kls'>,)
>>> Kls.printd()
TypeError: unbound method printd() must be called with Kls instance as first argument (got nothing instead)
>>> Kls.smethod()
Static: ()
>>> Kls.cmethod()
Class: (<class '__main__.Kls'>,)
View Code

下面这个图解释了以上代码是怎么运行的:

 

 

一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。

而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。

这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。

既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢

从它们的使用上来看,

  • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
  • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

 

转载于:https://www.cnblogs.com/mitsui/p/6746322.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值