tags: python知识
在看python代码的时候,有时候会发现有的变量或者函数前面会加入“_”(一个杠)或者“__”(两个杠)来表示变量的私有,出于好奇,在网上找了很多说法并结合自己实际操作,简单记录一下这方面的内容。
1. 与类无关的命名
这部分标题实在不知道起啥好,因为网上很多人在讲解这部分内容的时候,上来就说是类变量怎么样,类成员怎么样,比如下面的:
_xxx "单下划线 " 开始的成员变量叫做保护变量,意思是只有类对象(即类实例)和子类对象自己能访问到这些变量,需通过类提供的接口进行访问;不能用’from module import *'导入####
结果我试了下,发现不是这样的啊。到底想说啥,真的相当的模糊,很让人费解。我自己梳理了下,把我的理解先记下来。
这里就直接看示例吧。
首先,强调一下,这里是给任意变量命名,不是给类成员变量命名
现在新建一个文件,名字为 test1.py
# test1.py
a = 1
_b = 2
__c = 3
在上面的代码中,就只是简单定义了两个变量,一个为常规我们定义的变量a,另一个b被定义为私有变量。这两个变量在本文件内访问,没什么大的区别。但是在多个文件中经常会相互导入模块,那么这时就有了说法。比如在test2.py中要使用test1中的两个变量,那么就可以通过import导入,但是如何导入是一个问题。
如果采用的是from test1 import * 的方式导入
from test1 import *
print(a)
print(_b)
print(__c)
那么执行结果会是:
如果采用的是from test1 import a, _b, __c,那么执行结果会是:
如果采用的是 import test1,然后调用的时候使用的方式是:
import test1
print(test1.a)
print(test1._b)
print(test1.__c)
输出是:
第一种情况之所以会出现上面的情况,原因就在于在test1文件内,_b和__c私有的。类似于c++,是static的。而如果采用后面两种方式导入,则明确指定要导入的内容,或者指定是命名空间test1下的_b和__c,这种方式是允许的。
上面的调用方式不涉及类的任何内容。
2. 与类有关的命名
现在针对类的成员变量的命名方式进行说明
首先说明一下,在类中,以普通方式命名的变量类似于c++中的public变量,而加“_”前缀的变量类似于c++中的protected变量,加“__”前缀的变量,类似于c++中的private变量。
这里我们可以这样理解,但并不是真实意义上的等同,因为python不具有严格的私有化机制,下面会说明
还是刚刚的文件test1.py,我们写三个类,这三个类只是成员变量的命名方式不一样,其他的没有区别:
# coding:utf-8
class P1():
# 普通方式命名
def __init__(self, name, age):
self.name = name
self.age = age
class P2():
# 加_
def __init__(self, name, age):
self._name = name
self._age = age
class P3():
# 加__
def __init__(self, name, age):
self.__name = name
self.__age = age
然后在test2.py中去导入并继承,还是采用两种方式导入,一种是from test1 import * , 另一种方式是import test1(from test1 import P1, P2, P3和这个差不多,引用方式不一样而已,就省略了)。看下这两种的区别。
首先是from test1 import * :
from test1 import *
#import test1
#P1, P2, P3 = test1.P1, test1.P2, test1.P3
class A(P1):
def __init__(self):
super(A, self).__init__("张三", 3)
class B(P2):
def __init__(self):
super(B, self).__init__("李四", 4)
class C(P3):
def __init__(self):
super(C, self).__init__("王五", 5)
a = A()
print(a.name)
print(a.age)
b = B()
print(b._name)
print(b._age)
c = B()
print(c.__name)
print(c.__age)
打印结果是:
采用import test1 的方式导入结果如下:
在这里可以看出,两种导入方式的结果是一样的,并没有很多教程上面说的,不能通过from ### import *导入啊,难道是我理解的有问题?也就是说,使用以上方式导入的话,对于普通的成员变量和加“_”的成员变量来说,不受影响,而对于“__”来说,则无法访问。
之所以前面说其本身不能与c++中的私有化机制或者保护机制等同,其实在于下面的原因:
import test1
P1, P2, P3 = test1.P1, test1.P2, test1.P3
class A(P1):
def __init__(self):
super(A, self).__init__("张三", 3)
class B(P2):
def __init__(self):
super(B, self).__init__("李四", 4)
class C(P3):
def __init__(self):
super(C, self).__init__("王五", 5)
a = A()
print(dir(a))
b = B()
print(dir(b))
c = C()
print(dir(c))
结果输出:
['__class__', '__delattr__', ... 'age', 'name']
['__class__', '__delattr__', ... '_age', '_name']
['_P3__age', '_P3__name', '__class__', ...]
为了简单,省略了大部分中间一样的内容,就看一下区别。
可以发现,a,b两者都是继承各自父类的成员变量。在c++中,protected中的变量被子类继承之后,是要私有化的,自动变成private的;但是在python中,我们可以发现,继承之后并没有发生属性的改变,也就是说,这种加’_'的变量,并没有变成"__",在本类中或者子类中,感觉只是为了在类中区分私有变量还是共有变量,更多的是一种标志性的东西,在继承里面没有什么实际的区分,也不限制访问,只不过在访问方式上面,得通过XX._name的方式,而不是XX.name的方式访问或者修改。
而c则不一样,因为在其父类中,成员变量“__name”是私有的,子类中是不能直接继承的,而且在子类中已经偷偷改变了名字,变成了“_P3__age和_P3__name”了,再通过c.__name或者c.__age访问是要出错的。如果要访问,那么只能通过c._P3__age和c._P3__name访问。这就防止了在写程序中,通过c.__name或者c.__age改变类成员变量的情况出现。
说句题外话,其实,即使在子类中对c.__name或者c.__age赋值了,那其实是简单的为该子类增加了一个私有变量,并不能改变父类中私有变量的内容。比如下例:
c = C()
c.__name = "赵六"
c.__age = 6
print(dir(c))
print("子类私有变量")
print(c.__name)
print(c.__age)
print("父类私有变量")
print(c._P3__age)
print(c._P3__age)
程序输出为:
['_P3__age', '_P3__name', '__age', '__name',...']
可以看到,子类的成员变量中,增加了两个变量,是我们动态创建的。
如果非要改父类的成员变量,那么就得采用下面的方式:
c = C()
c._P3__name = "赵六"
c._P3__age = 6
print("修改父类私有变量")
print(c._P3__name)
print(c._P3__age)
打印结果如下:
非常不建议这样做,只是供理解
总结
实际上,上面所说的只是帮助理解的东西,在实际操作中,通常私有变量要改的话,也不会采用这种方式去改变,因为不方便,也不安全。通常是通过函数的形式加上@property这种修饰符的使用的,比如下面例子:
class A(object):
def __init__(self, name):
self._name = name
self._classes = []
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
self._name=new_name
@property
def classes(self):
return self._classes
@property
def num_classes(self):
return len(self._classes)
这样的话,想要修改就不会通过XX._name这种形式,而是直接使用XX.name改就可以了。
不过这部分内容也挺多的,这里不再写了。
上面的内容不能保证完全正确,只是自己做个记录,希望有见解的朋友给评判下!