python中的private、下划线、类变量
1. python中没有private、protected,但是有个惯例
官方文档是这么写的:
9.6. Private Variables and Class-local References
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
也就是说外部不可访问的实例变量在python中是不存在的。但是有个约定惯例,用一个下划线开头的变量是一个内部的细节实现,不作为公开的接口的一部分。
它的原理是什么呢?
- 以单下划线开头的变量是一个君子协议,这个变量是内部细节,你最好别用,真要用,直接也能用。就是这样而矣。用了别人说你编码不规范没有遵守约定之类而矣。。就这样。
- 以双下划线开头的变量是一个名字会变的变量,你直接用变量名是访问不到的。如果我知道名字变化的规则,还是可以修改获取其值的。。。。仅此而矣。
2. 双下划线变量命名的一个trick
还是之前那个链接的官方文档。https://docs.python.org/3.6/tutorial/classes.html
Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.
当类中出现双下划线开始的变量时,纵然定义为实例变量,也即以self.__param_name的方式定义,当你在pycharm断点时,看不到self.__param_name变量的。你将会看到一个_classname__param_name的变量。也即一个类似类变量的东西,但是只存在于实例中,不出现在类变量中(类变量是即在出现在既中也出现在实例中)。
由于名字被混杂了,直接self.__param_name是访问不到的,混杂后的名字形如self._classname__param_name。用这种混杂的方式可以在一定程度上防止直接对这些想要隐藏的变量的访问。但想要访问还是可以访问得到的。
用一点代码描述下这些现象。
class Test:
__cls_glb = "hehe"
_cls_glb = "hahaggg"
cls_glb = "......."
def __init__(self):
self.__inst_pri = "haha"
if __name__ == '__main__':
t1 = Test()
print(t1.cls_glb) # 等于类变量
t1.cls_glb = "ffffff" # 导致新加一个实例变量,
Test.cls_glb = "ggggggg" #不会修改t1中的实例变量,即上一行的变量
# print(t1.__inst_pri) # AttributeError: 'Test' object has no attribute '__inst_pri'
# print(t1.__cls_glb) # AttributeError: 'Test' object has no attribute '__cls_glb'
print(Test._Test__cls_glb)# 如果你那么想要这个变量的值的话,打印hehe
Test._Test__cls_glb = "*******"
print(Test._Test__cls_glb) # 打印*******
。。。
有点坑吧,本想定义一个私有的实例变量,然而却成了一个类似类变量的东西。
3. 静态类变量的一个trick
c++中静态类变量只有一个副本,一处实例修改,所有都变。
python中不完全是这样的。。。。。。。得有多少坑。。。。
自己用pycharm断点试验,总结如下:
- classname.变量、inst.变量、self.变量开始是相同的变量。。。。。
也即只读操作,三者都是一样的。- 出现以下情况时将变成不同变量.
- self.变量=xx
- inst.变量=xx
也即任何实例赋值都会导致产生一个新的副本,此时实例变量是实例变量,类变量是类变量,inst.变量读出来的是实例变量的值,不再是类变量的值。
总结起来就是,读取变量可以用三种方式中任意一种,赋值时类变量必须以类名.变量=xx的形式。此是唯一正确的形式(当然不考虑设置类属性值之类的偏门方法)。
网上一篇文章也提到这个trick。
https://www.python-course.eu/python3_class_and_instance_attributes.php