第二十八篇 静态属性、类方法、静态方法
静态属性
静态属性:实际上说的就是数据属性
需求:每个人都有自己的房子,想知道每个人自己的房子都有多大平米
实现分析:
每个人,表示有很多人,不可能一个人写一次计算面积的方法,那么就可以将它提炼出来,放到类里
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh # 计算面积 def cal_area(self): # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) # 实例化 R1=Room('大House','alex',100,100,100000) R2=Room('小平房','yuanhao',2,4,6) # 调用计算面积 r1.cal_area() r2.cal_area() # alex 住的 大House 总面积是10000 # yuanhao 住的 小平房 总面积是8
- 静态属性:@property
静态属性是绑定到实例上的,是通过实例.方法名 进行调用的。
静态属性的作用:就是封装逻辑,让用户再调用的时候完全感知不到后端的逻辑,就行在调用一个普通的数据属性一样。
特点:定义一个静态属性,内部会有一个self位置参数,self代表实例,实例可以访问到实例的数据属性,实例同样也可以访问到类的数据属性和类的函数属性,也就是说
静态属性,既可以访问实例属性,也可以访问类的属性(类的数据属性和类的函数属性)---> self.类属性 或者 self.实例属性
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property # 属性的意思。 def cal_area(self): return '%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length) # 需要return值 # 实例化 R1=Room('大House','alex',100,100,100000) R2=Room('小平房','yuanhao',2,4,6) # 调用静待属性时,就不用再加小括号了。 # 调用cal_area,实际上是个函数属性,但是加了@property 之后,再调用,直观感受上就跟调用实例的数据属性是一样的,调用者看不到背后的运行逻辑,
# 调用者更不知道调用的这个到底是数据属性还是函数属性----这就是静态属性的作用。 print(R1.cal_area) print(R2.cal_area) # 这是实例直接调用实例的数据属性。 print(R1.name) print(R2.name)
- 类方法:@calssmethod
不跟任何实例捆绑,只跟类捆绑的方法
作用和目的:跟实例没有任何关系了,只是类级别的操作,类来调用自己的方法。
特点:函数的位置参数默认就被写成了cls, cls代表类, 类能访问到类的数据属性和类的函数属性(---> cls.类属性 或者 cls.实例属性),但是类不能访问到实例的属性。
需求:
类调用类的函数属性
class Room: tag=1 # 类的数据属性 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property def cal_area(self): return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length) def test(self): print('from test',self.name) # 1. 类可以调用自己(类)的数据属性 print(Room.tag) # 1 # 2. 类调用自己(类)的函数属性 # 如果没有实例,直接调用函数属性 Room.test(r1) # 1.name # AttributeError: 'int' object has no attribute 'name' # 报错了,因为函数属性test(self), self指的就是实例,需要传入实例参数才能正常运行。所以要先实例化 r1=Room('大House','alex',100,100,100000) Room.test(r1) # r1.name #from test 大House
使用类方法,就可以不再通过实例,而可以直接调用类的函数属性了。
class Room: tag=1 # 类的数据属性 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property # 静态属性 def cal_area(self): return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length) def test(self): print('from test',self.name) @classmethod # 类方法:就把tell_info变成专门供类使用的方法 def tell_info(cls,x): # cls:本质上就是一个位置参数,但是python告诉你,cls接收的是一个类名,而这个类名是默认传的,不需要你再显示的写出来然后传进来 # 打印类名 print(cls) # 运行结果:<class '__main__.Room'> class表示类型是类,__main__表示当前文件,Room表示类名 # cls.tag:类调用类的数据属性。 print('--》',cls.tag) # 相当于print('--》',Room.tag) # 调用方式:类名.函数属性 # tell_info(cls,x): 有两个参数:第一个是位置参数cls,第二个是x # 可以为什么只传了一个参数12呢?因为该函数属性已经变成了类方法,第一个参数cls Python默认就给你传成了类名Room # 与self道理一样一样的。所以,调用者只需要传入第二个位置参数就可以拉。 Room.tell_info(12) # 结果 <class '__main__.Room'> --》 1 看到了把,用了类方法,类想调用类的函数属性,就不比再通过实例这个中介来调用了,而是可以直接调用。
注意:
# 通过实例也可以调用类方法 r1=Room('大House','alex',100,100,100000) r1.tell_info(13) # 结果 <class '__main__.Room'> --》 1 13 # 但是,前面说过了,@classmethod 就是专门供类使用的,所以,虽然能通过实例调用,但是千万别这么干,别给自己找不痛快
- 静态方法:@staticmethod
@staticmethod,不跟类绑定,也不跟实例绑定,只是一个类的工具包
用处:只是名义上归属类管理,不能使用类变量和实例变量,是类的工具包
特定:函数里既没有self参数,也没有cls参数,所以静态方法不能访问类属性和实例属性
class Room: tag=1 def __init__(self,name,owner,width,length,heigh): self.name=name self.owner=owner self.width=width self.length=length self.heigh=heigh @property def cal_area(self): return '%s 住的 %s 总面积是%s' % (self.owner, self.name, self.width * self.length) @classmethod def tell_info(cls,x): print(cls) print('--》',cls.tag,x) #print('--》',Room.tag) @staticmethod # static:静态 staticmethod 就是类的工具包 def wash_body(a,b,c): print('%s、 %s、 %s正在洗澡' %(a,b,c))
# 通俗的说,静态方法里没有self或者cls等位置参数,让你可以向类方法或者self那样,通过点的方式调用类变量,比如:通过cls.tag这样的方式进行调用类变量
# 在类里,这么写是没有任何实际意义的, 所以,此处这么写就是为了提示自己,这是无意义的,以后开发中别这么干 def test(x,y): print(x,y) # @staticmethod 既可以通过类调用,也可以通过实例调用 # 1. 调用方式: 类名.函数属性名 Room.wash_body('alex','yuanhao','wupeiqi') # 2. 调用方式: 实例.函数属性名 r1=Room('厕所','alex',100,100,100000) r1.wash_body('alex','yuanhao','wupeiqi') # 那不加@staticmethod,有什么区别呢? # 1. 通过类调用test()函数,可以调用 Room.test(1,2) # 1 2 # 2. 通过实例调用test()函数,不可以调用,因为实例调用需要会自动把实例自己传给函数,而test()却没有self接收。 r1.test(1,2) # TypeError: test() takes 2 positional arguments but 3 were given
问题:那静态方法到底定义到哪里去了呢?
【答】静态属性@property、类方法@classmethod、静态方法@staticmethod, 都是定义到了类的数据字典里的。
print(Room.__dict__) # {'__module__': '__main__', 'tag': 1, '__init__': <function Room.__init__ at 0x019A1C00>,
'cal_area': <property object at 0x0193CDE0>, 'tell_info': <classmethod object at 0x017993B0>,
'wash_body': <staticmethod object at 0x019A5190>,
'test': <function Room.test at 0x019A1AE0>, '__dict__': <attribute '__dict__' of 'Room' objects>,
'__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None} # 通过上面可以看到,静态属性@property、类方法@classmethod、静态方法@staticmethod, 都是定义到了类的数据字典里的。
print(r1.__dict__) # {'name': '厕所', 'owner': 'alex', 'width': 100, 'length': 100, 'heigh': 100000} #实例始终不变的就是只有自己的数据属性
小总结:
1、函数属性有参数self:表示该函数属性跟实例绑定
2、@classmethod,函数属性有参数cls:表示该函数属性跟类绑定
3、@staticmethod,函数属性既没有self参数,也没有cls参数:表示该函数属性既不跟类绑定,也不跟实例绑定,只是一个类的工具包