类的访问控制
私有变量
在Python中,双下划线开始,非双下划线结尾的,都是私有的,在类的外部是无法访问的
In [26]: class Door:
...: def __init__(self, status):
...: self.__status = status # 定义一个私有的属性
...: def open(self):
...: self.__status = 'openning'
...: def close(self):
...: self.__status = 'closed'
...: def status(self):
...: return self.__status
...:
In [27]: door = Door('closed')
In [28]: door.status()
Out[28]: 'closed'
In [29]: door.__status # 当通过实例访问私有的属性,会抛出AttributeError的错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-29-d55234f04e7f> in <module>()
----> 1 door.__status
AttributeError: 'Door' object has no attribute '__status'
Python中的若是定义了私有的属性,那么就需要通过类中指定的方法来访问,不然访问到的就不是我们想要的结果
当我们使用实例对私有属性进行赋值的时候,其实就是给实例重新创建了一个属性,也证实了Python的一个惯例,赋值及创建
In [30]: door.__status = 'lanyulei'
In [31]: door.__status # 给对象新创建了属性,并没有去修改类中的__status
Out[31]: 'lanyulei'
In [32]: door.__dict__ # 在__dict__中是存在__status的方法的,说明给实例创建了一个新的属性
Out[32]: {'_Door__status': 'closed', '__status': 'lanyulei'}
尽管使用door实例给__status赋值了,但是其实在类中的__status是没有变化的,受保护的
In [34]: door.status() # 使用指定的方法,获取到的__status的值
Out[34]: 'closed'
私有方法
和私有变量是相同,双下划线开始,非双下划线结尾的方法,也是私有的方法
由此可以得出一个结论,所有双下划线开始,非双下划线结尾的成员都是私有成员
In [35]: class Door:
...: def __set_name(self, number): # 定义一个私有方法
...: self.number = number
...:
In [37]: door = Door()
In [38]: door.__set_name(5001) # 调用私有方法,报出了错误,无法调用
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-38-418365dabb86> in <module>()
----> 1 door.__set_name(5001)
AttributeError: 'Door' object has no attribute '__set_name'
在Python中是没有完全的私有属性的
当定义了私有成员的时候,其实只是修改了属性的名,因此当访问私有属性的时候,就会访问不到
Python将定义私有的属性的时候,将私有属性修改成了,_类名 + 私有属性名称
In [40]: class Door:
...: def __init__(self, status):
...: self.__status = status # 定义了一个私有的属性
...: def open(self):
...: self.__status = 'openning'
...: def close(self):
...: self.__status = 'closed'
...: def status(self):
...: return self.__status
...:
...:
In [41]: door = Door('closed')
In [42]: door.__status # 直接使用实例访问私有的属性,报出了AttributeError错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-42-d55234f04e7f> in <module>()
----> 1 door.__status
AttributeError: 'Door' object has no attribute '__status'
In [43]: door._Door__status # 使用修改后的私有属性的名称
Out[43]: 'closed'
当创建了私有属性的时候,实例化类后,会在实例中的__dict__方法中追加修改后的私有变量keyword-only数据,因此我们可以从__dict__查看实例的属性
In [44]: door.__dict__ #
Out[44]: {'_Door__status': 'closed'}
当然,如果想在外部强制的修改私有属性也是可以的
In [48]: door.status() # 获取私有属性的值
Out[48]: 'closed'
In [49]: door._Door__status = 'openning' # 在外部重新定义私有属性的值
In [50]: door.status() # 验证结果,确定私有属性已经在外部修改
Out[50]: 'openning'
这种方法可以修改私有属性的值,但是既然定义为私有属性了,就不能随意的可以更改,因此除非真的有必要,并且清楚明白的知道使用后会有什么后果,否则不用使用这个黑魔法
有一种使用单下划线 ‘_’ 的属性和方法,是一种惯用法,标记成员为私有成员,但是解释器不做任何处理
In [51]: class Door:
...: def _set_name(self, number): # 定义一个方法,单下划线的方法,就是标记说明这个是一个私有属性,但是程序不会错任何处理
...: self.number = number
property装饰器
property装饰器会把一个仅有self参数的函数,变成一个变量,属性的值,就是方法的返回值
In [52]: class Door:
...: def __init__(self, status):
...: self.__status = status
...: def open(self):
...: self.__status = 'openning'
...: def close(self):
...: self.__status = 'closed'
...: @property # 装饰status方法
...: def status(self):
...: return self.__status
...:
...:
In [53]: door = Door('openning')
In [54]: door.status # 使用访问属性的方式调用方法
Out[54]: 'openning'
In [55]: door.status() # 直接调用方法,报出了TypeError的错误
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-55-76c604e52a9b> in <module>()
----> 1 door.status()
TypeError: 'str' object is not callable
property.setter装饰器可以通过两个方法将一个方法转化为属性对此赋值,但是对于需要装饰的方法也是有要求的:
- 两个方法必须同名
- 第二个方法必须接受两个参数self和value,value为所赋的值
给类中的私有属性__number进行判断赋值,number必须是整数,大于0,小于10000
In [9]: class Door:
...: def __init__(self, number):
...: self.__number = number
...: @property
...: def number(self): # 返回属性被重新赋值后的结果集
...: return self.__number
...: @number.setter # 给number方法做逻辑处理的
...: def number(self, number):
...: if isinstance(number, int) and number > 0 and number < 10000:
...: self.__number = number
...:
In [10]: door = Door(1001)
In [12]: door.number
Out[12]: 1001
In [13]: door.number = 1002
In [14]: door.number
Out[14]: 1002