注意:本文中“对象”和“实例”是一个意思。
https://www.zhihu.com/question/20021164
https://blog.csdn.net/yhy1271927580/article/details/72758577
一、类中的内置装饰器
有三种我们经常会用到的装饰器, @classmethod、 @staticmethod、 @property,他们有个共同点,都是作用于类方法之上。
类中定义的普通方法(即实例方法),需要先实例化类的一个对象再调用,无法直接用类调用。而被@classmethod或@staticmethod装饰过的方法,可以不需要实例化,直接以“类名.方法名()”的方式来调用。
被@classmethod装饰过的方法称为类方法,被@staticmethod装饰过的方法称为静态方法。
如果希望在实例化某个类之前就提供某些方法,最简单的办法就是使用@classmethod和@staticmethod对方法进行装饰。
类方法必须含有隐含调用参数cls,它指向类本身;实例方法必须含有隐含调用参数self,它指向类的对象;静态方法既没有cls,也没有self。
类方法和静态方法都可以被类和类实例调用,实例方法(类中的普通方法)仅可以被实例调用。
1) @classmethod 装饰器
@classmethod 用于装饰“类方法”,意味着该类方法可以直接被调用,而无需实例化。且该类方法没有 self 参数,也无法访问实例化后的对象,但它会接收一个指向类本身的参数cls,该类方法只能访问类属性,而无法访问实例属性。
例:
下面是一个常规的类定义及实例化过程:
#类定义
class Date_test(object):
class_name = 'test date' #类属性
def __init__(self,year=0,month=0,day=0):
self.day=day #实例属性
self.month=month
self.year=year
def out_date(self):
print(self.year,self.month,self.day)
t=Date_test(2016,8,1) #类实例化
t.out_date() #类实例化后调用方法
#输出
2016 8 1
上述结果符合期望。
但如果用户输入的是 "2016-8-1" 这样的字符格式,那么就需要在调用Date_test 类实例化前做一下处理:
string_date='2016-8-1'
year,month,day=map(int,string_date.split('-')) #先预处理
t=Date_test(year,month,day) #再实例化一个对象
t.out_date
#输出
2016 8 1
上述结果也符合预期
我们想,出现‘2016-8-1’这种字符串的情况应该是常有的事,如果每次出现都要加上前面的预处理代码会是一件很繁琐的事情,而且也会让程序结构不简洁,不利于维护。
有没有可能将这个预处理的代码也放到类的定义中将它封闭起来呢?
显然是可以的,这就是@classmethod 装饰器的作用了。
直接上代码:
class Date_test(object):
class_name = 'test date' #类属性
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) #调用当前类cls实例化一个对象,并赋给data1
return date1 #返回初始化后的类data1
@classmethod
def print_calssname(cls):
print("类名:",cls.class_name) #只能访问类属性,无法访问实例属性
def out_date(self):
print(self.year,self.month,self.day)
上述代码中,增加了一个带有装饰器@classmethod的方法get_date(cls,string_date),这个方法跟其他方法的不同之处在于,首先它的第一个参数是cls(必须参数)(也可以换成其他字符,不建议这么做),他表示的是当前类名Data_test,其次该方法将待处理的字符串日期“string_date”作为输入(非必须参数)。
在该方法中,利用map()函数对字符串进行预处理;
紧接着,关键语句date1=cls(year,month,day),相当于date1=Data_test(year,month,day),即利用预处理好的year,month,day生成了一个Data_test对象date1,然后将date1作为返回值。
即:将字符串日期的预处理和利用预处理后的内容生成Date_test对象,都放在类Date_test的定义中了。
同时,还增加了一个带有装饰器@classmethod的方法print_calssname(cls),当然他必须带有cls参数,该方法打印了类属性class_name,其他啥也没干,注意在这个方法中没有self参数,因此它无法处理实例属性。
注意,虽然我们在类中增加了些新的代码,但实际上并没有破坏原类中跟实例对象相关的代码!
定义完了之后,该如何使用呢?紧接着上面代码:
t1 = Date_test(2016,8,6) #按正常方式生成的对象t1
t1.out_date()
t2 = Date_test.get_date("2016-8-6") #利用装饰器方法生成的对象t2
t2.out_date()
Date_test.print_calssname() #直接调用@classmethod装饰的方法
#输出
2016 8 6
2016 8 6
类名: test date
上述代码中t1是用正常方式获得的对象,t2是用被装饰的方法获得的对象,两者得到了一样的结果。Date_test.print_calssname()是直接以“类名.方法名()”的方式调用只属于类的方法。
继续往下看,下述代码说明t1和t2是完全相同的类型,这进一步说明了增加的装饰器方法并没有对原类的对象信息产生破坏:
print(type(t1))
print(type(t2))
#输出
<class '__main__.Date_test'>
<class '__main__.Date_test'>
继续往下看,下面代码利用对象t1和t2调用被装饰的类方法get_date()(文章开头已说明,这是可行的),我们知道get_date()返回的是一个类的实例,也就是说t3和t4就是该类的对象:
t3 = t1.get_date('2020-8-6')
t4 = t2.get_date("2020-8-6")
t3.print_calssname()
t4.out_date()
print(t3)
print(t4)
#输出
类名: test date
2020 8 6
<__main__.Date_test object at 0x000001ABD5FFE280>
<__main__.Date_test object at 0x000001ABD5FFE5E0>
2)@staticmethod 装饰器
经常有一些跟类有关系的功能,但在运行时又不直接需要类和实例参与,这些功能都可以归结为静态方法。 比如更改环境变量或者修改其他类的属性等。没错,这些方法完全可以拿到类外面用函数实现,但这么做会导致程序结构混乱,造成维护困难,因此通常也将他们放在类内部,并用@staticmethod进行标记(装饰)。
被@staticmethod装饰的方法只是名义上也属于“类方法”,但这些方法不使用类属性和实例属性,即不传入self(类的对象)或者cls(类本身)。
比如下例,为了对比,将类对象方法、类方法(@classmethod装饰)、静态方法(@staticmethod装饰)写在一起:
class Cat(object):
def __init__(self,name):
self.name = name
def get_cat_name(self): #对象方法,有self参数
print('cat name:',self.name)
@classmethod #类方法,有cls参数
def regular_name(cls,name): #该方法处理猫名字是数字的情况,先将数值转化为字母
if isinstance(name,int):
name = str(name)
cat =cls(name) #用处理过的名字实例化一只猫对象
return cat #返回这个对象
@staticmethod #静态方法,该方法既跟Cat类没关系(无cls参数),也跟Cat对象没关系(无self参数)
def other_inf(**kwargs):
for i,j in kwargs.items():
print(i,':',j)
Cat.other_inf(country='China',sex='male') #可以用“类.方法名()”直接调用静态方法,不需要提前生成对象
#输出
country : China
sex : male
#####################################################################################
my_cat = Cat('Jack') #先生成对象
my_cat.get_cat_name()
my_cat.other_inf(country='USA',color='black') #通过对象调用静态方法
#输出
cat name: Jack
country : USA
color : black
#####################################################################################
your_cat = Cat.regular_name(123) #先通过类方法生成一个对象
your_cat.get_cat_name()
your_cat.other_inf(country='England',color='white') #再通过对象调用静态方法
#输出
cat name: 123
country : England
color : white
3)@property 装饰器
property 装饰器用于类中的函数,使得我们可以像访问属性一样来获取一个函数的返回值。
https://blog.csdn.net/linyang_fangzhu/article/details/105711527
二、类装饰器
类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
https://www.cnblogs.com/f-ck-need-u/p/10205168.html
https://www.cnblogs.com/wickedpriest/p/11872402.html
网友评论