【问题标题】:Python subclass that doesn't inherit attributes不继承属性的 Python 子类
【发布时间】:2016-11-11 12:34:23
【问题描述】:

我想创建一个 Python 类,它表面上看起来是另一个类的子类,但实际上并没有继承它的属性。

例如,如果我的班级名为 B,我希望 isinstance(B(), A) 返回 True 以及 issubclass(B, A),但我不希望 B 具有为A. 这可能吗?

注意:我不控制A的实现。

我关心的原因:我正在使用的模块检查传递的对象是否是A 的子类。我想在B 中定义必要的属性,而不继承A 中定义的多余属性(我无法控制其实现),因为我使用__getattr__ 将一些属性调用传递给包装类,如果这些属性由 A 继承定义,__getattr__ 不会被调用。

【问题讨论】:

  • 您可能想查看抽象基类。另外,你的目标是 python 2 还是 3?
  • 以 Python 3 为目标。
  • 顺便说一句。结果是XY problem。您应该避免专门针对您的单一想法来解决您的原始问题,而是提及您的原始问题。
  • @poke 是的,我自己也意识到了这一点。对不起????

标签: python class oop inheritance


【解决方案1】:

只要您在__init__ 方法中定义属性并覆盖该方法,B 就不会运行A__init__ 中的代码,因此不会定义属性等。删除方法会更难,但似乎超出了问题的范围。

【讨论】:

  • A 似乎是一个 C 实现类的薄包装,所以覆盖 __init__ 什么都不做。
【解决方案2】:

在 Python3 中,覆盖特殊方法 __getattribute__。这使您几乎可以完全控制属性查找。有一些极端情况,因此请仔细检查文档(这是语言参考手册的第 3.3.2 节)。

【讨论】:

  • __getattr__更多? iirc, __getattr__ 如果相关属性已经定义,则不会被调用
  • __getattribute____getattr__ 不同,会为所有已定义和未定义的属性调用
  • @LukeTaylor 请参阅this question__getattribute__ 是阻止访问成员的核心方法。话虽如此,这并不是一个好的解决方案,因为您需要单独选择退出每个成员(并且还要防止子类型实际重新定义这些成员)。
  • @LukeTaylor:手头的任务特别要求类 B 继承自 A,但覆盖所有已由 A 定义的属性。因此使用 __getattribute__ 而不是 __getattr__ 是必要的。
【解决方案3】:

使用abstract base classes 使一个几乎不相关的类B 成为A 的子类,而不从它继承:

from abc import ABCMeta
class A (metaclass=ABCMeta):
    def foo (self):
        print('foo')

class B:
    def bar (self):
        print('bar')

A.register(B)

然后使用,它会给出所需的结果并显示为子类型,而实际上没有任何基本类型的成员:

>>> issubclass(B, A)
True
>>> b = B()
>>> isinstance(b, A)
True
>>> b.foo()
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    b.foo()
AttributeError: 'B' object has no attribute 'foo'

我正在使用__getattr__ 将一些属性调用传递给包装类,如果这些属性是通过从A 继承定义的,则不会调用__getattr__

__getattr__ 不会为使用正常属性解析找到的成员调用。你可以改用__getattribute__

但是,如果您正在做的是覆盖基类 A 的行为,那么我不明白为什么不能简单地覆盖方法:

class A:
    def doSomething (self);
        print('Do not do this!')

class B:
    def __init__ (self, wrapper):
        self.wrapper = wrapper

    def doSomething (self):
        print('Doing something else instead!')
        self.wrapper.doSomething()

【讨论】:

  • A 已在我无法控制的库中定义。不过还是谢谢
【解决方案4】:

您可以实现__getattribute__ 来为不在 B 中的属性引发 AttributeErrors:

class A(object):
    def __init__(self):
        self.foo = 1

    def bar(self):
        pass

class B(A):
    def __init__(self):
        self.baz = 42

    def __getattribute__(self, attr):
        if attr in ('foo', 'bar'):
            raise AttributeError()
        return super(B, self).__getattribute__(attr)

我很好奇,你为什么要这样做?

【讨论】:

  • 我正在使用一个库来检查某个东西是否是一个子类,然后它才会对它做任何事情,所以我需要诱使它认为具有所有必要方法的东西(但不是多余的) one) 不是子类。
  • @LukeTaylor 既然如此,你为什么要关心那些原始成员是否真的存在呢?如果你确定它们没有被使用(否则你不能安全地期望它在没有这些成员的情况下工作),那么它们可能不会伤害任何人只是那里。
  • @poke 因为它使用它们,但我正在实现__getattr__ 以从包装类中读取它们。如果定义了方法,则不会调用 __getattr__
【解决方案5】:

我希望这能让你满意(我认为这有点肮脏 hack):

class A:
    attribute = "Hello"
    pass

class B(A):
    def __getattribute__(self, name):
        if name == "__dict__":
            return super().__getattribute__(name)
        if name in type(self).__dict__:
            return type(self).__dict__[name]
        if name in self.__dict__:
            return self.__dict__[name]
        raise AttributeError("type object '{}' has no attribute '{}'".format(type(self).__name__, name))

现在让我们测试一下:

>>> a = A()
>>> a.attribute
'Hello'
>>> b = B()
>>> b.attribute
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "file.py", line 13, in __getattribute__
    raise AttributeError("type object '{}' has no attribute '{}'".format(type(self).__name__, name))
AttributeError: type object 'B' has no attribute 'attribute'

不幸的是B类本身继承了属性所以会发生这种情况:

>>> B.attribute
'Hello'

我希望没关系,如果确实需要使用元类(这可能会变得非常讨厌)。

【讨论】:

  • 等等,这有什么帮助?您的 __getattribute__ 实现看起来并没有改变默认行为...
  • @LukeTaylor Paul Cornelius 已经回答了它是如何工作的,我只是举例说明了如何做到这一点(不是很实用,但例如很好)。
  • 好的。非常感谢;)
猜你喜欢
  • 2013-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 2021-12-14
  • 1970-01-01
相关资源
最近更新 更多