Python 类的 public protected private 属性
class A:
def __init__(self):
self.public_field = 5
self._protect_filed = 8
self.__private_field = 10
def get_private_filed(self):
return self.__private_field
obj = A()
print(obj.public_field) # 访问 public 属性
print(obj._protect_filed) # 访问 protected 属性
print(obj.__private_field) # 访问 private 属性会报错
# AttributeError: 'A' object has no attribute '__private_field'
public
在 python 中数据和方法默认都是 pubic,此时方法和变量名都没有下划线。
1、 xx 没有以下划线开头的变量或者方法是 public。
2、public 可以被子类、类内以及类外被访问。
protected
1、 _xx 以单下划线开头表示的是 protected 变量或者方法。
2、保护类型只能允许其本身与子类进行访问。
以单个下划线开头的属性,这是为了尽量减少无意间访问内部属性所带来的意外,用一种习惯性的命名方式来表示该字段受保护,本类之外的代码使用该字段时要小心。
它本质上与 public 属性使用相同,但命名上体现了保护目的。
应该多用 protected 属性,并在文档里把这些字段的合理用法告诉子类开发者,而不要试图用 private 属性来限制子类访问这些字段。
class B(A):
def __init__(self):
super().__init__()
self._value = 10
childObj = B()
print(childObj._value)
print(childObj.public_field)
print(childObj._protect_filed)
应该主观上避免对 protected 属性的访问,但访问它也不会导致报错
private
1、 __xx 双下划线表示的是私有类型的变量或者方法。
2、private 类型只能允许类内进行访问。
# 访问 private 属性会报错
# AttributeError: 'A' object has no attribute '__private_field'
obj.__private_field
但本类的方法可以直接访问它们:
obj.get_private_filed()
子类无法访问父类的 private 字段:
print(childObj.__private_filed)
#AttributeError: 'B' object has no attribute '__private_filed'
其原理是 Python 对 private 属性的名称做了一些变换:比如 A 的 __private_field 字段,实际上被变换成 _A__private_filed 字段,通过变换后属性名与被访问属性名不相符达到类之外或子类无法访问 private 属性目的。
print(obj._A__private_field)
print(childObj._A__private_field)
换句话说,Python 编译器无法严格保证 private 字段的私密性。
Python 为什么不从语法上严格保证 private 字段的私密性呢?用最简单的话讲,We are all consenting adults here(我们都是成年人了)。这也是很多 Python 程序员的观点,大家都认为开放要比封闭好。
另外一个原因在于 Python 语言本身就已经提供了一些属性挂钩(getattr 等),使得开发者能够按照自己的需要来操作对象内部的数据。既然如此,那为什么还要阻止访问 private 属性呢?
最后,不要盲目地将属性设为 private,而是应该从一开始就做好规划,并允许子类更多地访问超类的内部 API;只有当子类不受自己控制时,才考虑用 private 属性来避免命名冲突。
特列方法
xx 定义的是特列方法。用户控制的命名空间内的变量或是属性,只有当文档有说明时使用,不要自己定义这类变量。 (就是说这些是 python 内部定义的变量名)
需要特殊说明的是,在 python 中没有像其他类似语言的 public, private 等关键字修饰。同时若方法或者变量是 private 类型时,其方法或者变量实际上是被转换了,转换规则是 在变量的前端插入类名,再类名前添加一个下划线"_",形成"_ClassName__变量名".
如果我们使用转换后的变量或者方法,我们仍然可以进行访问,即 python 没有严格意义上的 private 类型。
class C:
def __init__(self):
# public
self.name = "class_def"
# private
self.__age = 29
# protected
self._sex = "man"
def fun1(self):
print("call public function")
def __fun2(self):
print("call private function")
def _fun3(self):
print("call protected function")
if __name__ == "__main__":
# 实例化类对象
obj = C()
# 调用方法
# ok
obj.fun1()
obj._C__fun2()
obj._fun3()
# 访问数据
print(obj._C__age)
print(obj._sex)
print(obj.name)
# error
# obj.__fun2()
# print(obj.__age)
输出结果:
call public function
call private function
call protected function
29
man
class_def