我有一个子类,我希望它 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
我该怎么做?
答案 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"
属性:
使用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
我相信这是最优雅的方法,因为代码清晰,通用且紧凑。当然,如果删除属性是他们真正想要的,那么应该真的三思而后行。
覆盖属性查找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__
的实现,跳过父级。
毋庸置疑,这仅适用于“新式类”(继承自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,但事情并非那么简单。如果你认为你已经通过这样做创建了一个子类型,这一切都归结为。 A
和B
方法表示是,但LSP表示否(许多程序员会因为你从A
继承而假定为“是”。
LSP表示如果B
是A
的子类型,那么只要我们可以使用B
,我们就可以使用A
,但是因为我们不能这样做在执行此构造时,我们可以得出结论: Platform.runLater(new Runnable() {
@Override
public void run() {
initFX(fxPanel);
}
});
实际上不是buildProcessEngine()
的子类型,因此不会违反LSP。