Python 中私有方法和受保护方法的继承
- 2025-03-13 09:13:00
- admin 原创
- 69
问题描述:
我知道,Python 中没有“真正的”私有/受保护方法。这种方法不是为了隐藏任何东西;我只是想了解 Python 的作用。
class Parent(object):
def _protected(self):
pass
def __private(self):
pass
class Child(Parent):
def foo(self):
self._protected() # This works
def bar(self):
self.__private() # This doesn't work, I get a AttributeError:
# 'Child' object has no attribute '_Child__private'
那么,这种行为是否意味着“受保护”的方法将被继承,而“私有”的方法则根本不会继承?
还是我遗漏了什么?
解决方案 1:
Python 没有隐私模型,没有像 C++、C# 或 Java 那样的访问修饰符。没有真正“受保护”或“私有”的属性。
以双下划线开头且不以双下划线结尾的名称会被修饰,以防止在继承时发生冲突。子类可以定义自己的__private()
方法,这些方法不会干扰父类中的相同名称。此类名称被视为类私有的;它们仍然可以从类外部访问,但意外冲突的可能性要小得多。
混淆是通过在任何此类名称前面添加一个额外的下划线和类名(无论名称如何使用或是否存在)来完成的,从而有效地为它们提供了一个命名空间。在Parent
类中,任何__private
标识符(在编译时)都被名称替换_Parent__private
,而在Child
类中,标识符_Child__private
在类定义中的所有位置都被替换为。
以下操作有效:
class Child(Parent):
def foo(self):
self._protected()
def bar(self):
self._Parent__private()
请参阅词法分析文档中的标识符保留类:
__*
类私有名称。此类名称在类定义上下文中使用时,将被重写为使用混乱形式,以帮助避免基类和派生类的“私有”属性之间发生名称冲突。
以及关于名称的参考文献:
私有名称改编:当类定义中文本出现的标识符以两个或多个下划线字符开头,但不以两个或多个下划线结尾时,它被视为该类的私有名称。私有名称在生成代码之前会转换为更长的形式。转换会将类名插入名称前面,删除前导下划线并插入单个下划线。例如,
__spam
名为 Ham 的类中出现的标识符将转换为_Ham__spam
。此转换与标识符使用的语法上下文无关。
不要使用类私有名称,除非您明确希望避免必须告知想要对您的类进行子类化的开发人员他们不能使用某些名称,否则可能会破坏您的类。除了已发布的框架和库之外,此功能几乎没有用处。
PEP 8 Python 风格指南对私有名称修改有如下说明:
如果您的类打算被子类化,并且您有不想让子类使用的属性,请考虑使用双前下划线命名它们,而不使用尾部下划线。这会调用 Python 的名称改编算法,其中类的名称被改编为属性名称。这有助于避免在子类无意中包含同名属性时发生属性名称冲突。
注 1:请注意,在混乱的名称中仅使用简单类名,因此如果子类选择相同的类名和属性名,仍然会发生名称冲突。
注 2:名称修改可能会使某些用途(例如调试和
__getattr__()
)变得不太方便。但是,名称修改算法有据可查,并且易于手动执行。注 3:并非所有人都喜欢名称混淆。尝试在避免意外名称冲突的需求与高级调用者的潜在使用之间取得平衡。
解决方案 2:
双重__
属性被更改为,_ClassName__method_name
这使得它比 所暗示的语义隐私更加私密_method_name
。
从技术上来说,如果您真的愿意,您仍然可以得到它,但大概没有人会这样做,所以出于维护代码抽象的原因,该方法在这一点上可能是私有的。
class Parent(object):
def _protected(self):
pass
def __private(self):
print("Is it really private?")
class Child(Parent):
def foo(self):
self._protected()
def bar(self):
self.__private()
c = Child()
c._Parent__private()
这有额外的好处(或者有人会说是主要的好处),就是允许方法不与子类方法名称冲突。
解决方案 3:
通过声明数据成员为私有的:
__private()
你根本无法从课堂外部访问它
Python 支持一项称为名称修改的技术。
该特性将以两个下划线为前缀的类成员变为:
_className.memberName
如果你想访问它,Child()
你可以使用:self._Parent__private()
解决方案 4:
PEP8还表示
仅对非公共方法和实例变量使用一个前导下划线。
为了避免与子类发生名称冲突,请使用两个前导下划线来调用 Python 的名称修改规则。
Python 将这些名称与类名混为一谈:如果
class Foo
有一个名为 的属性__a
,则 无法访问它Foo.__a
。(坚持的用户仍可以通过调用 获得访问权限Foo._Foo__a
。)通常,仅应使用双前导下划线来避免与旨在子类化的类中的属性发生名称冲突。
按照惯例,你也应该远离_such_methods
。我的意思是你应该把它们当作private
解决方案 5:
虽然这是一个老问题,但我遇到了它并找到了一个很好的解决方法。
在这种情况下,您在父类上命名了混乱,因为您想要模仿受保护的函数,但仍然希望在子类上以简单的方式访问该函数。
parent_class_private_func_list = [func for func in dir(Child) if func.startswith ('_Parent__')]
for parent_private_func in parent_class_private_func_list:
setattr(self, parent_private_func.replace("_Parent__", "_Child"), getattr(self, parent_private_func))
这个想法是手动将父类函数名替换为适合当前命名空间的函数名。在子类的 init 函数中添加此函数后,您可以轻松调用该函数。
self.__private()
解决方案 6:
据我所知,在第二种情况下,Python 执行“名称改编”,因此父类的 __private 方法的名称实际上是:
_Parent__private
而且你也不能以这种形式在孩子身上使用它
扫码咨询,免费领取项目管理大礼包!