Python:删除子类中的类属性

Ram*_*hum 40 python inheritance namespaces class

我有一个子类,我希望它包含基类上存在的类属性.

我试过这个,但它不起作用:

>>> class A(object):
...     x = 5
>>> class B(A):
...     del x
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    class B(A):
  File "<pyshell#1>", line 2, in B
    del x
NameError: name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Bhu*_*han 47

您可以使用delattr(class, field_name)它从类定义中删除它.

  • 我不是子类 - 我正在测试中删除设置类中的设置以检查错误处理 - 所以这正是我需要的! (3认同)
  • 如果有一个完整的编码示例将会很有帮助。这个可以填写吗?我不确定这在代码中的位置。 (3认同)

Kei*_*ith 17

您无需删除它.只是覆盖它.

class B(A):
   x = None
Run Code Online (Sandbox Code Playgroud)

或者根本不参考它.

或者考虑不同的设计(实例属性?).

  • **这根本不回答这个问题.**将属性设置为"无"确实_not_删除该属性,这应该是相当明显的.删除属性就是`del`和`delattr()`.[Bhushan](/sf/users/111617411/)简明扼要[回复](/sf/answers/1114409271/)似乎是这个问题的唯一实际答案. (9认同)
  • @Keith,也许基类的设计很差,用户想要修复它而不复制整个源代码.例如,Django的AuthenticationForm应该是"用于验证用户的基类",但它定义了用户名和密码表单字段,这使得它对非密码验证毫无用处.删除这些属性会保留所有基类的重用方法,同时摆脱首先不应该在基类中的垃圾. (3认同)

Kat*_*iel 12

仔细想想你为什么要这样做; 你可能没有.考虑不要让B从A继承.

子类化的想法是专门化一个对象.特别是,类的子节点应该是父类的有效实例:

>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True
Run Code Online (Sandbox Code Playgroud)

如果你实现了这种行为(例如x = property(lambda: AttributeError)),你就破坏了子类化概念,这就是坏事.

  • 我一般都讨厌非答案减少到"不要做这个令人敬畏和有趣的事情,因为我是一个负责任的成年人...... <挥手>!" 这也不例外.虽然保留[Liskov替代性](https://en.wikipedia.org/wiki/Liskov_substitution_principle)对于理智的开发至关重要,但每条规则都打算被打破.我们都是大人,在这里.**谢谢.** (42认同)
  • 我总体上同意@CecilCurry,因为我之前遇到过类似的其他问题的答案,而且我讨厌它们。然而,在这种特殊情况下,我想说删除父属性首先并不令人敬畏和有趣,因此善意地提醒[LSP](https://en.wikipedia.org/wiki/Liskov_substitution_principle)并不是废话。我最终对这个答案和 CecilCurry 的评论都投了赞成票。 (2认同)
  • 我发现指出被广泛接受的编程原理的答案非常有益,因为我正在学习。我也知道每条规则都有一定的灵活性,因此请以它们为指导。 (2认同)

JBe*_*rdo 7

也许你可以在有人试图访问它时设置xproperty并引发AttributeError.

>>> class C:
        x = 5

>>> class D(C):
        def foo(self):
             raise AttributeError
        x = property(foo)

>>> d = D()
>>> print(d.x)
File "<pyshell#17>", line 3, in foo
raise AttributeError
AttributeError
Run Code Online (Sandbox Code Playgroud)

  • 这是目前*唯一*实际有效的解决方案。当然,除了它仅“删除”子类*实例*上的属性,而不是子类本身。 (2认同)

drd*_*man 7

没有一个答案对我有用.

例如delattr(SubClass, "attrname")(或其精确等价物del SubClass.attrname)不会"隐藏"父方法,因为这不是方法解析的工作方式.AttributeError('attrname',)相反,它将失败,因为子类没有attrname.当然,替换属性None并不会实际删除它.

让我们考虑一下这个基类:

class Spam(object):
    # Also try with `expect = True` and with a `@property` decorator
    def expect(self):
        return "This is pretty much expected"
Run Code Online (Sandbox Code Playgroud)

我知道只有两种方法可以将它子类化,隐藏expect属性:

  1. 使用一个描述符类引起AttributeError__get__.在属性查找时,会出现异常,通常与查找失败无法区分.

    最简单的方法就是宣布一个提升的财产AttributeError.这基本上是@JBernardo所建议的.

    class SpanishInquisition(Spam):
        @property
        def expect(self):
            raise AttributeError("Nobody expects the Spanish Inquisition!")
    
    assert hasattr(Spam, "expect") == True
    # assert hasattr(SpanishInquisition, "expect") == False  # Fails!
    assert hasattr(SpanishInquisition(), "expect") == False
    
    Run Code Online (Sandbox Code Playgroud)

    但是,这仅适用于实例,而不适用于类(hasattr(SpanishInquisition, "expect") == True断言会被破坏).

    如果您希望上面的所有断言都成立,请使用:

    class AttributeHider(object):
        def __get__(self, instance, owner):
            raise AttributeError("This is not the attribute you're looking for")
    
    class SpanishInquisition(Spam):
        expect = AttributeHider()
    
    assert hasattr(Spam, "expect") == True
    assert hasattr(SpanishInquisition, "expect") == False  # Works!
    assert hasattr(SpanishInquisition(), "expect") == False
    
    Run Code Online (Sandbox Code Playgroud)

    我相信这是最优雅的方法,因为代码清晰,通用且紧凑.当然,应该真的三思,如果除去该属性是他们真正想要的.

  2. 使用__getattribute__魔术方法覆盖属性查找.您可以在子类中执行此操作(或者在下面的示例中使用mixin,因为我只想编写一次),这会隐藏子类实例上的属性.如果要隐藏子类中的方法,则需要使用元类.

    class ExpectMethodHider(object):
        def __getattribute__(self, name):
            if name == "expect":
                raise AttributeError("Nobody expects the Spanish Inquisition!")
            return super().__getattribute__(name)
    
    class ExpectMethodHidingMetaclass(ExpectMethodHider, type):
        pass
    
    # I've used Python 3.x here, thus the syntax.
    # For Python 2.x use __metaclass__ = ExpectMethodHidingMetaclass
    class SpanishInquisition(ExpectMethodHider, Spam,
                             metaclass=ExpectMethodHidingMetaclass):
        pass
    
    assert hasattr(Spam, "expect") == True
    assert hasattr(SpanishInquisition, "expect") == False
    assert hasattr(SpanishInquisition(), "expect") == False
    
    Run Code Online (Sandbox Code Playgroud)

    这看起来比上面的方法更糟糕(更冗长,更通用),但也可以考虑这种方法.

    请注意,这并没有特殊("魔术")方法(如工作__len__),因为这些绕行__getproperty__.有关更多详细信息,请查看Python文档的特殊方法查找部分.如果这是你需要撤消的,只需覆盖它并调用object实现,跳过父节点.

毋庸置疑,这仅适用于"新式类"(继承自的类object),因为那里不支持魔术方法和描述符协议.希望这些都是过去的事情.


acj*_*jay 5

我也有同样的问题,我认为我有一个有效的理由删除子类中的class属性:我的超类(称之为A)有一个只读属性提供了属性的值,但是我的子类(称之为B),该属性是一个读/写实例变量.我发现Python正在调用属性函数,即使我认为实例变量应该覆盖它.我可以使用一个单独的getter函数来访问底层属性,但这似乎是一个不必要的,不优雅的接口命名空间的混乱(好像这真的很重要).

事实证明,答案是用A的原始公共属性创建一个新的抽象超类(称之为S),并且A和B派生自S.因为Python有鸭子打字,所以B确实不重要不扩展A,我仍然可以在相同的地方使用它们,因为它们隐含地实现了相同的接口.