【每天1分钟】PYTHON基础之面向对象(深入类的属性)
1. 同名的类属性与实例属性
1-以实例名.属性名引用时,优先引用实例属性
2-以类名.属性引用时,只能引用类属性
>>> class A:
a = 0
def __init__(self):
self.a = 10
self.b = 100
>>> a = A()
>>> a.a # 以实例名.属性名引用时,优先引用实例属性
10
>>> a.b
100
>>> A.a # 以类名.属性引用时,只能引用类属性
0
>>> A.b # 以类名.属性引用时,只能引用类属性
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
A.b
AttributeError: type object 'A' has no attribute 'b'
>>>
2. 属性访问的特殊方法(反射)
>>> class A:
a=0
def __init__(self):
self.a=10
self.b=100
>>> a=A()
>>> getattr(a,'a')#用getattr()函数获取实例a中a属性的值
10
>>> setattr(a,'a',20)#设置实例a中a属性的值为20
>>> getattr(a,'a')#用getattr()函数获取实例a中a属性的值
20
>>> hasattr(a,'b')#测试实例a中是否包含属性b
True
3. 属性包装
将方法包装成属性,以隐藏相关实现。
1、控制属性的类型或范围
2、虚拟属性(由其它属性处理后得来)
三种属性操作:
1.可读:@property
2.可写:@.setter
3.可删:@.deleter
class Washer:
def __init__(self,water=10,scour=2):
self._water=water #不想让用户直接访问实例变量,可以标志成私有
self.scour=scour
#属性包装,将water属性包装成方法,用户使用water时实际是访问的方法
@property
def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性
return self._water
def set_water(self,water):
self.water=water
def set_scour(self,scour):
self.scour=scour
def add_water(self):
print('Add water:',self.water)
def add_scour(self):
print('Add scour:',self.scour)
def start_wash(self):
self.add_water()
self.add_scour()
print('Start wash...')
if __name__=='__main__':
w=Washer()
#w.start_wash()
print(w.water)# 可以像访问属性一样访问方法
但这时用户仍然可以通过w._water来访问实例属性,封装的不好,也不会自动检查数据是不是浮点型,不好。
解决方法:
class Washer:
def __init__(self,water=10,scour=2):
self._water=water #不想让用户直接访问实例变量,可以标志成私有
self.scour=scour
#属性包装,将water属性包装成方法,用户使用water时实际是访问的方法
@property
def water(self):#如果用户使用 实例.water相当于访问这个方法,而不是真的访问属性
return self._water
@water.setter #新添加代码
def water(self,water):
if 0<water<=500:
self._water=water
else:
print('set Failure!')
def set_water(self,water):
self.water=water
def set_scour(self,scour):
self.scour=scour
def add_water(self):
print('Add water:',self.water)
def add_scour(self):
print('Add scour:',self.scour)
def start_wash(self):
self.add_water()
self.add_scour()
print('Start wash...')
if __name__=='__main__':
w=Washer()
print(w.water)# 可以像访问属性一样访问方法
#w._water=20 #为了不让用户这样直接给实例属性赋值,用下面的语句
w.water=123 #可以像给属性赋值一样给方法赋值,并检测范围
print(w.water)# 可以像访问属性一样访问方法
另一个例子:
>>> class A:
def __init__(self):
self.__name=None
@property # 读
def name(self):
return self.__name
@name.setter # 写
def name(self,value):
self.__name=value
@name.deleter # 删除
def name(self):
del self.__name
>>> a=A()
>>> print(a.name)
None
>>> a.name = 'Python'
>>> print(a.name)
Python
>>> del a.name
>>> print(a.name)
Traceback (most recent call last):
File "<pyshell#40>", line 1, in <module>
print(a.name)
File "<pyshell#34>", line 6, in name
return self.__name
AttributeError: 'A' object has no attribute '_A__name'
>>>
4. 描述符
描述符的意义是避免重复写具有相同限定属性的实例属性的定义代码。
class NonNeg:#数据描述符
def __init__(self,default=0):#构造方法
self.default=default#一个实例属性
def __get__(self,instance,owner):#协议方法
return self.default
def __set__(self,instance,val):#协议方法
if val>0:
self.default=val
else:
print('The value must be NonNegative!')
def __delete__(self,instance):#协议方法
pass
class Movie:
rating=NonNeg()#描述符类NonNeg作另一个类Movie的属性,rating是Movie的类属性。
score=NonNeg()
if __name__=='__main__':
m=Movie()
print('rating:',m.rating)
print('score:',m.score)#输出默认值default
m.rating=80#使用__set__协议方法
print('rating:',m.rating)#使用到 __get__协议方法
m.score=-3
print('score:',m.score)
rating: 0
score: 0
rating: 80
The value must be NonNegative!
score: 0
类成员函数都是非数据描述符。
>>> class Tst:
def pr(self):
print('Tst')
>>> t = Tst()
>>> dir(t.pr)
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
># 只有 __get__ 协议
同名的实例属性和非数据描述符(以类方法为例)同时出现时,访问的优先级是什么?
>>> class Tst:
def pr(self):
self.pr = 10
print('Tst')
>>> t = Tst()
>>> t.pr
<bound method Tst.pr of <__main__.Tst object at 0x00000244EDC4FEB8>>
>>> print(t.pr)
<bound method Tst.pr of <__main__.Tst object at 0x00000244EDC4FEB8>>
>>> t.pr = 20
>>> t.pr
20
>>> del t.pr
>>> t.pr
<bound method Tst.pr of <__main__.Tst object at 0x00000244EDC4FEB8>>
>>>
>>> t1 = Tst()
>>> t1.pr()
Tst
>>> t1.pr
10
>>> t1.pr = 20
>>> t1.pr
20
>>> del t1.pr
>>> t1.pr
<bound method Tst.pr of <__main__.Tst object at 0x00000244EDAB5908>>
>>> t1.pr()
Tst
>>>
5. __call__()让类的实例和函数一样可调用
>>> class Tst:
def __init__(self,default=1):
self.water=default
def __call__(self):
print('包含call函数的类,他的实例可以直接当做函数使用。')
def info(self):
print("pass")
>>> t = Tst()
>>> t()
包含call函数的类,他的实例可以直接当做函数使用。
>>>