在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以对属性随便修改,不能限制输入。
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)
输出:
<class '__main__.Date'>
2017 4 22
<class '__main__.EuroDate'>
现在是2017年4月21日from EuroDate
@staticmethod
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
再来看这个例子看看两者的不同:
作者:李保银
链接: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'>,)
下面这个图解释了以上代码是怎么运行的:
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
从它们的使用上来看,
- @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
- @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。