• @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参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

     

  • 相关阅读:
    GET请求和POST请求的本质区别
    go切片的Add与Del
    滚动到指定位置的问题
    promise---批量调用接口,等待所有的请求发完
    this argument
    html2canvas截图 下载图片
    数组合并去重
    vue项目踩坑
    关于java中的栈和堆
    用python实现一个最简单版本的mysql数据库连接池
  • 原文地址:https://www.cnblogs.com/mitsui/p/6746322.html
Copyright © 2020-2023  润新知