8.2.2 方法和私有化
Python类默认情况下,所有的方法都可以被外部访问。不过像很多其他编程语言,如Java、C#等,都提供了private关键字将方法私有化,也就是说只有类的内部方法才能访问私有化的方法,通过正常的方式是无法访问对象的私有化方法的(除非使用反射技术)。不过在Python类中并没有提供private或类似的关键字将方法私有化,但可以迂回解决。
在Python类的方法名前面加双下划线(__)可以让该方法在外部不可访问。
class person:
# method1方法在类的外部可以访问
def method1(self):
print('method1')
# __method2方法在类的外部不可访问
def __method2(self):
print('method2')
p = person()
p.method1()
# p.__method2() # 抛出异常
输出结果:
method1
其实"__method2"方法也不是绝对不可访问。Python编译器在编译Python源代码时并没有将"__method2"方法真正私有化,而是一旦遇到方法名以双下划线(__)
开头的方法,就会将方法名改成"_ClassName__methodName"
的形式。其中ClassName表示该方法所在的类名,"__methodName"
表示方法名。ClassName前面要加上单下划线(_)前缀。
对于上面的代码,Python编译器会将"__method2"
方法更名为"_person__method2"
,所以在类的外部调用"__method2"
方法会抛出异常。抛出异常的原因并不是"__method2"
方法被私有化,而是Python编译器把"__method2"
的名称改为"_person__method2"
了。当了解了这些背后的原理,就可以通过调用"_person__method2"
方法来执行"__method2"`方法。
class person:
# method1方法在类的外部可以访问
def method1(self):
print('method1')
# __method2方法在类的外部不可访问
def __method2(self):
print('method2')
p = person()
p.method1()
p._person__method2() # 正常调用"__method2"方法
输出结果:
method1
method2
[例 8.2] 本例会创建一个MyClass类,并定义两个公共的方法(getName和setName)和一个私有的方法(__outName)
。然后创建MyClass类的实例,并调用了这些方法。为了证明Python编译器在编译MyClass类时做了手脚,本例还使用了inspect模块中的getmembers函数获取MyClass类中所有的成员方法,并输出方法名。很显然"__outName"
被改成了"_MyClass__outName"
。
class MyClass:
# 公共方法
def getName(self):
return self.name
# 公共方法
def setName(self,name):
self.name = name
# 在类的内部可以直接调用私有方法
self.__outName()
# 私有方法
def __outName(self):
print('Name = {}'.format(self.name))
myClass = MyClass()
# 导入inspect模块
import inspect
# 获取MyClass类中所有的方法
methods = inspect.getmembers(myClass,predicate = inspect.ismethod)
print(methods)
# 输出类方法的名称
for method in methods:
print(method[0])
print('--------------------')
# 调用setName方法
myClass.setName('Bill')
# 调用getName方法
print(myClass.getName())
# 调用'__outName'方法,这里调用了改完名后的方法,所以可以正常运行
myClass._MyClass__outName()
# 抛出异常,因为"__outName"方法在MyCall类中不存在
# print(myclass.__outName())
输出结果:
[('_MyClass__outName', <bound method MyClass.__outName of <__main__.MyClass object at 0x106907c70>>), ('getName', <bound method MyClass.getName of <__main__.MyClass object at 0x106907c70>>), ('setName', <bound method MyClass.setName of <__main__.MyClass object at 0x106907c70>>)]
_MyClass__outName
getName
setName
--------------------
Name = Bill
Bill
Name = Bill
从getmembers函数列出的MyClass类方法的名字可以看出,"_MyClass__outName"
被绑定到了"__outName"
方法上,可以将"_MyClass__outName"
看作"__outName"
的另一个别名,一旦为某个方法起了别名,那么原来的名字在类外部就不可用了。MyClass类中的getName方法和setName方法的别名和原始方法名相同,所以在外部可以直接调用getName和setName方法。