在Python中进行名称处理
如果我们正在用Python编写类并希望遵循Python的封装OOPS概念,那么我们将如何停止对变量的外部访问,因为没有显式的访问修饰符,例如public,private,在Python中受保护并且默认情况下所有变量都是public 。在Python中,对将类成员设为私有的支持有限,该过程在Python中称为名称修改。
Python名称处理机制
在名称处理机制中,具有至少两个前导下划线的标识符(最多一个尾随下划线)在文本上均被_classname__identifier替换,其中classname是当前类名。例如,如果类中有一个变量__test,则将其替换为_classname__test。
由于名称是由解释器在内部更改的,因此我们无法使用其原始名称访问变量,这就是在Python中隐藏数据的方式。
名称修饰有助于让子类覆盖方法而又不中断类内方法调用。
Python名称修改示例
class User: def __init__(self, name, age): self.name = name self.__age = age def display_user(self): print('User Name:', self.name) print('User Age:', self.__age) user = User('Mike Dallas', 34) # calling class method user.display_user() # Accessing variables directly print(user.name) print(user.__age)
输出:
User Name: Mike Dallas User Age: 34 Mike Dallas Traceback (most recent call last): File "F:/theitroad/Python/Programs/NameMangling.py", line 16, in print(user.__age) AttributeError: 'User' object has no attribute '__age'
在类User中,使用确定的类方法访问时,存在一个__age字段(用双下划线声明),但是尝试通过名称处理机制将其名称更改为(_User__age)直接尝试访问它会导致错误。
我们可以使用dir()函数检查名称更改,该函数为传递的对象返回有效属性的列表。
class User: def __init__(self, name, age): self.name = name self.__age = age def display_user(self): print('User Name:', self.name) print('User Age:', self.__age) user = User('Mike Dallas', 34) print(dir(user))
输出:
['_User__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'display_user', 'name']
在这里,我们可以看到__age更改为_User__age。
名称与方法名称混用
由于任何具有至少两个前导下划线的类成员,在文本上最多将一个结尾的下划线替换为_classname__identifier,因此名称修饰也将应用于方法名称。
class User: def __init__(self, name, age): self.name = name self.__age = age def __display_user(self): print('User Name:', self.name) print('User Age:', self.__age) user = User('Mike Dallas', 34) user.__display_user()
输出:
Traceback (most recent call last): File "F:/theitroad/Python/Programs/NameMangling.py", line 12, in user.__display_user() AttributeError: 'User' object has no attribute '__display_user'
如何访问名称混乱的变量
在名称更改中,进程名称被_classname__membername替换,因此我们仍然可以使用更改的名称来访问成员名称。这就是为什么Python声明只有有限的支持将类成员设为私有。
class User: def __init__(self, name, age): self.name = name self.__age = age def display_user(self): print('User Name:', self.name) print('User Age:', self.__age) user = User('Mike Dallas', 34) # calling class method user.display_user() # Accessing variables directly print(user.name) # Accessing using the mangled name print(user._User__age)
输出:
User Name: Mike Dallas User Age: 34 Mike Dallas 34
使用方法重写处理Python名称
名称修饰也有助于Python中的方法覆盖。它允许子类覆盖方法而不会中断类内方法调用。考虑下面的示例,其中B类扩展了A类并覆盖了父类测试方法。在类A的init()中,还调用了test方法。
class A: def __init__(self): print('in init') self.test() def test(self): print('In test method of class A') class B(A): def test(self): print('In test method of class B') obj = B() obj.test()
输出:
in init In test method of class B In test method of class B
如我们所见,类B的test()方法被两次调用,而引用是对类B的调用。但是我们打算在做self.test()时调用类A的test()方法。为了避免在这种情况下中断类内方法调用,我们可以创建原始方法的私有副本。
class A: def __init__(self): print('in init') self.__test() def test(self): print('In test method of class A') # private copy __test = test class B(A): def test(self): print('In test method of class B') obj = B() obj.test()
输出:
in init In test method of class A In test method of class B