Python:删除子类中的类属性

时间:2011-05-19 10:19:21

标签: python class inheritance namespaces

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

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

>>> 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

我该怎么做?

7 个答案:

答案 0 :(得分:40)

您可以使用delattr(class, field_name)将其从类定义中删除。

完整示例:

# python 3
class A:  
    def __init__(self):
        self.field_to_keep = "keep this in B"
        self.field_to_delete = "don't want this in B"

class B(A):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        delattr(self, 'field_to_delete')

答案 1 :(得分:18)

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

class B(A):
   x = None

或者根本不参考它。

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

答案 2 :(得分:9)

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

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

>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True

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

答案 3 :(得分:7)

也许你可以将x设置为property,并在有人试图访问它时引发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

答案 4 :(得分:6)

没有一个答案对我有用。

例如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" 属性:

  1. 使用raises AttributeError from __get__的描述符类。在属性查找时,会出现异常,通常与查找失败无法区分。

    最简单的方法是声明一个提升expect的属性。这基本上就是@JBernardo所建议的。

    AttributeError

    但是,这仅适用于实例,而不适用于类(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 断言会被破坏)。

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

    hasattr(SpanishInquisition, "expect") == True

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

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

    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
    

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

    注意,处理特殊(“魔术”)方法(例如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 ),因为这些方法绕过__len__。查看Python文档的Special Method Lookup部分以获取更多详细信息。如果这是您需要撤消的内容,只需覆盖它并调用__getproperty__的实现,跳过父级。

  3. 毋庸置疑,这仅适用于“新式类”(继承自object的类),因为那里不支持魔术方法和描述符协议。希望这些已成为过去。

答案 5 :(得分:5)

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

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

答案 6 :(得分:1)

尝试这样做可能是一个坏主意,但是......

似乎没有通过“正确”继承来实现这一点,因为默认情况下查找B.x的方式如何。获取B.x后,x首先会在B中查找,如果找不到A,则会在B.x中搜索,但另一方面在设置或删除时B只会搜索>>> class A: >>> x = 5 >>> class B(A): >>> pass >>> B.x 5 >>> del B.x Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: class B has no attribute 'x' >>> B.x = 6 >>> B.x 6 >>> del B.x >>> B.x 5 。例如,

B.x

我们首先看到,我们似乎无法删除A.x,因为它不存在(B.x存在,并且是您评估B.x时提供的内容。但是,通过将B.x设置为6,B.x将存在,del B.x可以检索它,并由A.x删除,它将不再存在,所以在此之后B.x B.x }}将作为对AttributeError的回复。

另一方面,可能做的是使用元类使class NoX(type): @property def x(self): raise AttributeError("We don't like X") class A(object): x = [42] class B(A, metaclass=NoX): pass print(A.x) print(B.x) 加注issubclass

isinstance

现在当然纯粹主义者可能会大喊这打破了LSP,但事情并非那么简单。如果你认为你已经通过这样做创建了一个子类型,这一切都归结为。 AB方法表示是,但LSP表示否(许多程序员会因为你从A继承而假定为“是”。

LSP表示如果BA的子类型,那么只要我们可以使用B,我们就可以使用A,但是因为我们不能这样做在执行此构造时,我们可以得出结论: Platform.runLater(new Runnable() { @Override public void run() { initFX(fxPanel); } }); 实际上不是buildProcessEngine()的子类型,因此不会违反LSP。