如何定义超类专用方法?

How to define a superclass-only method?

片段:

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'

如果我只想定义一个超类专用方法,我该怎么办?

TL;DR: 在方法名前加上__前缀来触发Python的名字修饰机制。

答案:

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

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

另一件事是想想在这种情况下你是否会比继承更好地使用组合:如果你的 Base class 知道 do things Foo 做不到就其本身而言,您真的可以说“Foo 对象也是 Base 对象”吗? (这就是继承的意义所在)。

也许,更好的设计是:

class Base:
   ...

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

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

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

方法就是在方法前加上两个下划线。在编译时,Python 将方法转换为 f"_{class_name}__{method_name}",在方法声明中以及 [=65= 中对它的所有引用中] 声明的地方。所以 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 允许的另一件事是自定义为给定的 class 检索属性的方式:class 中对属性和方法的某种搜索是由 [每个 class 中的 =22=] 方法(不要将其与 __getattr__ 混淆,后者更简单,仅在未找到属性时才命中)。

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

您可以使用装饰器函数包装 superclass-only 方法,该函数根据方法的 class 名称验证当前实例的 class 名称,该名称可以从方法的__qualname__ 属性:

def superclass_only(method):
    def wrapper(self, *args, **kwargs):
        if self.__class__.__name__ != method.__qualname__.split('.')[-2]:
            raise NotImplementedError
        return method(self, *args, **kwargs)
    return wrapper

因此:

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

class Foo(Base):
    pass

调用 Base().method() returns 'yes',同时调用 Foo().method() 引发 NotImplementedError.

演示:https://replit.com/@blhsing/SpringgreenHonorableCharacters