Python类中双下划线的私有成员(私有函数,私有变量)
一、Python中默认的成员函数、成员变量都是公开的(public),而且python中没有类似public、private等关键词来修饰成员函数,成员变量。
在python中定义私有变量只需要在变量名或函数名前加上 "__" (两个下划线),那么这个函数或变量就会成为私有的了。
在内部,python使用一种 name mangling 技术,将__membername替换成 _classname__membername,所以你在外部使用原来的私有成员的名字时,会提示找不到。
命名混淆意在给出一个在类中定义"私有"实例变量和方法的简单途径,避免派生类的实例变量定义产生问题,或者与外界代码中的变量搞混。
要注意的是混淆规则主要目的在于避免意外错误,被认作为私有的变量仍然有可能被访问或修改(使用_classname__membername),在特定的场合它也是有用的,比如调试的时候。
【示例代码】
# -*- coding: utf-8 -*-
class Test(object):
def __init__(self):
super(Test, self).__init__()
self.__member = 99
def __method(self):
print "Test.__method"
if __name__ == '__main__':
t = Test()
print dir(t)
#t.__method() # AttributeError: 'Test' object has no attribute '__method'
#print t.__member # AttributeError: 'Test' object has no attribute '__member'
t._Test__method() # 使用Python更改之后的函数名依然可以访问到"私有"函数
print t._Test__member # 使用Python更改之后的变量名依然可以访问到"私有"变量
'''
['_Test__member', '_Test__method', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
Test.__method
99
'''
可以看到在dir里面多的不是__member和__method,而是_Test__member和_Test__method。并且可以通过_Test__member和_Test__method访问原来类里面的成员变量和成员函数。
二、单下划线开头的成员变量和成员函数,其实就是public的,不等同于protected属性。
【示例代码】
# -*- coding: utf-8 -*-
class Test(object):
def __init__(self):
super(Test, self).__init__()
self.__member = 88
self._member = 99
def __method(self):
print "Test.__method"
def _method(self):
print "Test._method"
class TestDerived(Test):
def __init__(self):
super(TestDerived, self).__init__()
def test(self):
#print self.__member # AttributeError: 'TestDerived' object has no attribute '_TestDerived__member'
#self.__method() # AttributeError: 'TestDerived' object has no attribute '_TestDerived__method'
print self._member
self._method()
if __name__ == '__main__':
td = TestDerived()
print dir(td)
td.test()
#print td.__member # AttributeError: 'TestDerived' object has no attribute '__member'
#td.__method() # AttributeError: 'TestDerived' object has no attribute '__method'
print td._member # 外部可以直接访问单下划线开头的成员变量
td._method() # 外部可以直接访问单下划线开头的成员函数
'''
['_Test__member', '_Test__method', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_member','_method', 'test']
99
Test._method
99
Test._method
'''
无论是单下划线还是双下划线开头的成员,都是希望外部程序开发者不要直接使用这些成员变量和这些成员函数,只是双下划线从语法上能够更直接的避免错误的使用,但是如果按照 _类名__成员名 则依然可以访问到。单下划线的在动态调试时可能会方便一些,只要项目组的人都遵守下划线开头的成员不直接使用,那使用单下划线或许会更好。
if __name__ == "__main__":
在继续学习新东西之前, 有几点重要的观察结果。 首先, if 表达式无需使用圆括号括起来。 其次, if 语句以冒号结束, 随后跟随的是 缩进代码。
与 C 一样, Python 使用 == 做比较, 使用 = 做赋值。 与 C 不一样, Python 不支持行内赋值, 所以不会出现想要进行比较却意外地出现赋值的情况。
那么为什么说这个特殊的 if 语句是一个技巧呢?模块是对象, 并且所有的模块都有一个内置属性 __name__。一个模块的 __name__ 的值要看您如何应用模块。如果 import 模块, 那么 __name__的值通常为模块的文件名, 不带路径或者文件扩展名。但是您也可以像一个标准的程序一样直接运行模块, 在这种情况下 __name__的值将是一个特别的缺省值, __main__。
>>> import odbchelper
>>> odbchelper.__name__
'odbchelper'
一旦了解到这一点, 您可以在模块内部为您的模块设计一个测试套件, 在其中加入这个 if 语句。当您直接运行模块, __name__ 的值是 __main__, 所以测试套件执行。当您导入模块, __name__的值就是别的东西了, 所以测试套件被忽略。这样使得在将新的模块集成到一个大程序之前开发和调试容易多了.
在 MacPython 上, 需要一个额外的步聚来使得 if __name__ 技巧有效。 点击窗口右上角的黑色三角, 弹出模块的属性菜单, 确认 Run as __main__ 被选中。