class A:
def __init__(self):
self.v = 0
o = A()
o.v =1
v
就是正常的对象中的成员变量,C++ 和 java 中的私有变量只有在这个类内部才可以被访问,而在这个类之外这个变量是访问不到的。在 python 中把一个类的变量定义成私有变量的方法是通过他的名字来实现的,可以把这个属性或者变量或者方法的名字前面加至少两个下划线,同时后面至多有一个下划线来把他定义成私有变量。那我们最常见的方式就是把名字前加两个下划线。
class A:
def __init__(self):
self.__v = 0
o = A()
print(o.__v)
Traceback (most recent call last):
File "c:\Users\Moe\Desktop\Note\1.py", line 6, in <module>
print(o.__v)
AttributeError: 'A' object has no attribute '__v'
在 A
这个class的初始化里面,我们做 self.__v = 0
, 那当我们在这个类外面的代码里想使用 o.__v
的时候,他就会报错。就会告诉你没有 __v
这个属性。
class A:
def __init__(self):
self.__v = 0
def print_v(self):
print(self.__v)
o = A()
o.print_v()
而如果我们给 A
写一个方法 print_v
让他打印 self.__v
,然后在这个类之外使用 print_v
他就可以正常打印出这个值了。
class A:
def __init__(self):
self.__v = 0
def __print_v(self):
print(self.__v)
o = A()
o.__print_v()
这个规则对方法同样有效,如果在 print_v
前面加两个下划线,这个 o 就也没有办法使用这个方法了。
私有变量概念的存在当然是有意义的,一个明显的好处就是他让我们的封装变得更佳严格了,我们可以通过对变量和函数的命名告诉这段代码的使用者,这个变量或者这个函数是你不应该用的,同时他还提供了一个机制,防止这些变量和函数被误用。当然除此之外还有其他的好处,尤其是当我们涉及到继承的时候。
class A:
valid_kwds = ["a"]
def __init__(self, **kwargs):
for key, val in kwargs.items():
if key in self.valid_kwds:
print(key, val)
class B(A):
valid_kwds = ["b"]
def __init__(self, **kwargs):
left_kwargs = {}
for key, val in kwargs.items():
if key in self.valid_kwds:
print(key, val)
else:
left_kwargs[key] = val
super().__init__(**left_kwargs)
o = B(a=2, b=3)
b 3
我们有两个类 A
和 B
, B
继承了 A
, 这两个类的共同特点就是都有一个类属性叫做 valid_kwds
,这个 valid_kwds
保存了在初始化的时候 kwargs
里面有哪些 keyword 是可以被接受的。当我们尝试初始化的时候,B
先从这些 keyword 里面挑他需要的,然后挑剩下的再传给 A
。为了简单起见,我们就直接把他拿到的这个keyword 和 value 给打印出来了。当你一眼看上去的时候,这段程序是没有什么问题的。类 A
需要 a
这个keyword,类 B
需要 b
这个 keyword。但当我们建立对象的时候,我们发现只有 b 3
这对 kwargs 被使用了,而本应该传到基类里的 keyword A
并没有被认为是合法的。这里的问题在第5行,当你去读取 self.valid.kwds
的时候,由于 self 是一个 b
对象,所以他的 valid_kwds
已经被覆盖掉了,第5行和第14行的 self.valid.kwds
是一样的, 所以在 类 A
的函数里面尽管他被调用了,但他不认为那个 a
是一个合法的 keyword argument。
class A:
__valid_kwds = ["a"]
def __init__(self, **kwargs):
for key, val in kwargs.items():
if key in self.__valid_kwds:
print(key, val)
class B(A):
__valid_kwds = ["b"]
def __init__(self, **kwargs):
left_kwargs = {}
for key, val in kwargs.items():
if key in self.__valid_kwds:
print(key, val)
else:
left_kwargs[key] = val
super().__init__(**left_kwargs)
o = B(a=2, b=3)
b 3
a 2
我们可以把 valid_kwds
变成私有变量来解决这个问题,由于私有变量是不会被继承和覆盖的,所以第5行的 __valid_kwds
将永远指向类 A
里定义的这个 __valid_kwds
。我们可以看到程序的运行结果,b 和 a 都被打印出来了。
当然类方法同样适用。有时候我不希望我类里面的成员被重载,我就可以把他变成私有的,这样可以保证在我自己的类里面调用这个函数的时候,他永远会调用的是我定义的这个函数,而不是继承我的类重新定义的同名的函数。