【问题标题】:How to define a superclass-only method?如何定义仅超类的方法?
【发布时间】:2021-11-01 15:52:54
【问题描述】:

sn-p:

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'

如果我只想定义一个只包含超类的方法,我该怎么做?

【问题讨论】:

  • 好的,但是你希望什么时候拨打superclass_only()是合法的?
  • 没有这样的东西。任何子类都是超类,这意味着您应该能够在任何可以使用超类实例的地方使用子类的实例。这种行为会打破这个基本假设。如果它是超类中的 internal 调用,则可以使用 __ 名称修饰来执行此操作,但在您的示例中,该方法是一个公共方法,预计将成为任何 Base 的公共接口的一部分及其所有子类。
  • @quamrana 我只希望超类调用方法,但子类
  • 你破坏了Liskov substitution principle。当然有时你必须打破它,但要小心。

标签: python oop methods superclass


【解决方案1】:

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

回答:

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

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

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

也许,更好的设计是:

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__ 方法执行的(不要将其与 @987654334 混淆@ 更简单,设计为仅在未找到属性时才被命中)。

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

【讨论】:

    【解决方案2】:

    您可以使用装饰器函数包装仅超类方法,该装饰器函数根据方法的类名验证当前实例的类名,该类名可以派生自方法的__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() 返回'yes',而调用Foo().method() 引发NotImplementedError

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

    【讨论】:

      猜你喜欢
      • 2023-03-08
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      • 2014-11-17
      • 1970-01-01
      • 2021-10-31
      相关资源
      最近更新 更多