首页 > 解决方案 > 如何定义仅超类的方法?

问题描述

片段:

class Base:                                                                     
    def superclass_only(self):
        return 'yes'


class Foo(Base):
    pass


foo = Foo()
>>> foo.superclass_only()
yes

# Expection is to raise error
>>> foo.superclass_only()
traceback 
...
AttributeError: 'Foo' object has no attribute 'superclass_only'

如果我只想定义一个仅限超类的方法,我该怎么办?

标签: pythonoopmethodssuperclass

解决方案


TL;DR : 为方法名称添加前缀__以触发 Python 的名称修改机制。

回答:

你通常不能这样做:继承不应该是这样工作的。如果您需要在“子类”中“隐藏”方法,您应该重新调整您的方法。

第一件事是使用命名约定来指示该方法是私有的,在 Python 中,我们通过在方法名称中添加“_”前缀来做到这一点:这应该向您的Foo类的用户指示应该使用保留的方法只有编写代码的人Base才能放心。

另一件事是考虑在这种情况下,如果你的组合不会比继承更好:如果你的Base类知道do thingsFoo 不能对自己做,你真的可以说“Foo 对象也是 Base 对象”吗?(这就是继承的意义)。

也许,更好的设计是:

class Base:
   ...

class Bar:
   def method_foo_cant_do(...):
       ...

class Foo(Base):
   def __init__(self, ...):
       self.bar = Bar()
       ...

最后,虽然不是为此而设计的,而是为了避免复杂层次结构中的方法名冲突,Python 有一种“名称修改”机制,它可以透明地将方法名更改为包含类名作为前缀的方法名。这将避免在子类中随意使用该方法,并且是一个更强有力的指标,表明它应该在“Base”中一起使用 - 但不会“不惜一切代价阻止”它被调用。

要走的路就是在方法前面加上两个下划线。在编译时,Python 将方法转换为 f"_{class_name}__{method_name}",在方法声明处以及在声明它的类中对它的所有引用中。所以Foo.__superclass_only不会到达Base.__superclass_only,因为后者的名字已经被修改为Base._Base__superclass_only

class Base:                                                                     
    def __superclass_only(self):
        return 'yes'

class Foo(Base):
    pass

在交互式解释器上:

In [3]: f= Foo()                                                                                  

In [4]: f.__superclass_only()                                                                     
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-19c8185aa9ad> in <module>
----> 1 f.__superclass_only()

但它仍然可以通过使用转换后的名称来访问:f._Base__superclass_only()会起作用。

Python 允许的另一件事是自定义为给定类检索属性的方式:对类中的属性和方法的某种搜索是由__getattribute__每个类中的方法执行的(不要误认为__getattr__哪个更简单且旨在被击中仅当未找到属性时)。

重新实现__getattribute__很容易出错,并且可能会让您比开始的方式更糟,并且给定一个对象,仍然可以通过做事 foo来调用(即:从基类本身检索该方法作为未绑定的方法(函数)并手动传入实例以成为“self”参数),并减轻这种情况需要您在元类上实现正确的 - (并且最终仍然可以被可以阅读源代码的人绕过)superclass_onlyBase.superclass_only(foo, ...)foo__get_attribute__


推荐阅读