Python:删除子类中的类属性
我有一个子类,我想它而不是包括一个类属性存在于基类。Python:删除子类中的类属性
我尝试这样做,但它不工作:
>>> 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
我怎样才能做到这一点?
仔细想想你为什么要这么做;你可能不会。考虑不让B从A继承A.
子类化的想法是专门化一个对象。具体而言,一类的儿童应在父类的有效实例:
>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True
如果实施该行为(例如带x = property(lambda: AttributeError)
),你打破了子类的概念,这是不好的。
我通常讨厌非答案减少到“不要做这个真棒和有趣的事情,因为我是一个负责任的成年人......
我一般同意@CecilCurry,因为我之前遇到过类似的答案,我讨厌他们。然而,在这种特殊情况下,我会说删除一个父属性首先不是真棒和有趣的,所以提醒[LSP](https://en.wikipedia.org/wiki/Liskov_substitution_principle)不是无稽之谈。我最终提出了这个答案和CecilCurry的评论。 – RayLuo 2016-10-22 01:41:44
我找到答案指出广泛接受的编程原理非常有益,因为我正在学习。我也知道每条规则都有一定的灵活性,所以要把它们作为指导方针。 – mehmet 2016-11-04 14:20:45
您不需要删除它。只需重写它。
class B(A):
x = None
或根本不参考它。
或考虑一个不同的设计(实例属性?)。
+1,因为它回答了问题,并建议设计更改。 – FlipMcF 2013-10-04 15:36:19
**这根本没有回答这个问题。**将一个属性设置为'None'并不会删除该属性,这应该是相当明显的。删除属性是'del'和'delattr()'做的。 [Bhushan](https://*.com/users/1594534/bhushan)的简洁[回复](https://*.com/a/15920132/2809027)似乎是此问题的唯一实际答案。 – 2016-02-02 06:02:09
@CecilCurry更深层的问题是为什么OP认为他需要删除它?无论如何,这是一个糟糕的设计。 – Keith 2016-02-06 19:17:27
也许你可以将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
我也有同样的问题,我想我有一个合法的理由来删除子类中的类属性:我的超类(称为A)有一个只读属性,提供了属性,但在我的子类(称为B)中,该属性是一个读/写实例变量。我发现Python调用了属性函数,即使我认为实例变量应该重写它。我可以创建一个单独的getter函数来访问底层属性,但这看起来像是一个不必要和不雅的界面名称空间混乱(如果真的很重要)。
事实证明,答案是用A的原始公共属性创建一个新的抽象超类(称为S),并且A和B从S派生出来。由于Python有鸭子打字,它并不真的因为B不扩展A,我仍然可以在相同的地方使用它们,因为它们隐含地实现了相同的接口。
您可以使用delattr(class, field_name)
从类定义中删除它。
我不是继承 - 我从我的测试中的设置类中删除一个设置,以检查正确的错误处理 - 所以这正是我需要的! – sage 2013-12-27 21:12:06
有一个完整的代码示例会很有帮助。这可以填写吗?我不确定代码中的内容。 – broccoli2000 2016-09-07 19:34:26
试图做到这一点可能是一个坏主意,但......
这似乎并没有被通过,因为如何查找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
设置为6,B.x
将存在,它可以通过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)
现在,当然纯粹主义者可能会嚷嚷,这打破了LSP,但它不是那么简单。这一切都归结为如果你认为你通过这样做创建了一个子类型。 issubclass
和isinstance
方法表示是,但LSP表示否(因为您从A
继承,许多程序员会认为“是”)。
的LSP意味着如果B
是A
子类型,然后我们可以使用B
每当我们可以使用A
,但由于我们不能做到这一点,而这样做的构建,我们可以得出这样的结论B
其实不是的亚型A
,因此不违反LSP。
没有答案为我工作。
例如delattr(SubClass, "attrname")
(或其确切的等价物,del SubClass.attrname
)不会“隐藏”父方法,因为这不是方法解析的工作方式。由于子类不具有attrname
,所以它会因AttributeError('attrname',)
而失败。当然,用None
替换属性实际上并不会将其删除。
让我们考虑这个基类:
class Spam(object):
# Also try with `expect = True` and with a `@property` decorator
def expect(self):
return "This is pretty much expected"
我知道只有两个唯一途径继承它,隐藏expect
属性:
-
使用描述符类raises
AttributeError
from__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
然而,这仅适用于实例,而不是类(在
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
我相信这是最优雅的方式,因为代码是明确的,通用的,紧凑。当然,真的如果删除属性是他们真正想要的,请三思。
-
覆盖属性查找with
__getattribute__
magic method。你可以在一个子类中(或者像下面这个例子中的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
这看起来比上述方法更差(更详细的少通用),但可以考虑这种方式为好。
注意,这并不特殊( “魔术”)的方法(例如
__len__
)不工作,因为这些旁路__getproperty__
。有关更多详细信息,请参阅Python文档的Special Method Lookup部分。如果这是你需要撤消的,只需重写它并调用object
的实现,即可跳过父项。
不用说,这仅适用于“新样式类”(即从object
继承者),因为魔术方法和描述符的协议不受支持那里。希望这些都是过去的事情。
这打破了[LSP](http://en.wikipedia.org/wiki/Liskov_substitution_principle)。你为什么想这样做? – 2011-05-19 10:22:50
您想要删除的'B'类'x'类变量确实存储在'A .__ dict__'中,所以如果您设法删除它,您也会删除'A.x'。因此,最接近的是_hiding_'A.x',通过给'B'一个'x'类变量,就像@Keith在他的回答中所表明的那样。 – 2011-05-19 11:08:06
@JBernardo:LSP和静态类型之间没有太多联系。 LSP是一个合理的原则,因为它使得类层次结构以可预测和一致的方式运行。这同样适用于静态和动态类型的语言,不管它们是否提供违反LSP的手段。 – 2011-05-19 16:29:46