“无法实例化抽象类”使用抽象方法ÉD;在不应该有任何抽象方法的类上
采取以下最小示例:
import abc
class FooClass(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def FooMethod(self):
raise NotImplementedError()
def main():
derived_type = type('Derived', (FooClass,), {})
def BarOverride(self):
print 'Hello, world!'
derived_type.FooMethod = BarOverride
instance = derived_type()
运行 main()
会得到:(
TypeError: Can't instantiate abstract class Derived with abstract methods FooMethod
异常发生在 instance = obliged_type()
行上。)
但是 FooMethod
不应该是抽象的:我已经用 BarOverride
覆盖了它。那么,为什么会引发异常呢?
免责声明:是的,我可以使用显式的class
语法,并完成完全相同的事情。 (更好的是,我可以让它工作!)但这只是一个最小的测试用例,更大的示例是动态创建类。 :-) 我很好奇为什么这不起作用。
编辑:并且为了防止其他明显的非答案:我不想将第三个参数中的 BarOverride
传递给 type
:真实的例子,BarOverride
需要绑定衍生类型
。如果我可以在创建衍生类型
之后定义BarOverride
,那么这样做会更容易。 (如果我做不到,那为什么?)
Take the following minimal example:
import abc
class FooClass(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def FooMethod(self):
raise NotImplementedError()
def main():
derived_type = type('Derived', (FooClass,), {})
def BarOverride(self):
print 'Hello, world!'
derived_type.FooMethod = BarOverride
instance = derived_type()
Running main()
gets you:
TypeError: Can't instantiate abstract class Derived with abstract methods FooMethod
(The exception occurs on the instance = derived_type()
line.)
But FooMethod
shouldn't be abstract: I've overridden it with BarOverride
. So, why is this raising exceptions?
Disclaimer: Yes, I could use the explicit class
syntax, and accomplish the exact same thing. (And even better, I can make it work!) But this is a minimal test case, and the larger example is dynamically creating classes. :-) And I'm curious as to why this doesn't work.
Edit: And to prevent the other obvious non-answer: I don't want to pass BarOverride
in the third argument to type
: In the real example, BarOverride
needs to have derived_type
bound to it. It is easier to do this if I can define BarOverride
after the creation of derived_type
. (If I can't do this, then why?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
因为文档是这么说的:
仅当定义类时才调用元类。当
abstractmethod
将一个类标记为抽象时,该状态以后不会改变。Because the docs say so:
A metaclass is only called when a class is defined. When
abstractmethod
has marked a class as abstract that status won't change later.约亨是对的;抽象方法是在类创建时设置的,我不会仅仅因为重新分配属性而对其进行修改。
手动将其从抽象方法列表中删除,因此它仍然不会认为
FooMethod
是抽象的。您可以通过执行或
以及
setattr
Jochen is right; the abstract methods are set at class creation and won't me modified just because you reassign an attribute.
You can manually remove it from the list of abstract methods by doing
or
as well as
setattr
, so it doesn't still think thatFooMethod
is abstract.我知道这个话题确实很老了但是......这确实是一个很好的问题。
它不起作用,因为 abc 只能在类型实例化期间(即
type('Derived', (FooClass,), {})
运行时)检查抽象方法。此后完成的任何 setattr 都无法从 abc 访问。所以,setattr 不起作用,但是......
您解决先前未声明或定义的类名称的问题看起来是可以解决的:
我编写了一个小元类,它允许您使用占位符“clazz”来访问任何类,该类最终将获得您在类外部编写的方法定义。
这样你就不会再从 abc 得到 TypeError 了,因为你现在可以在实例化你的类型之前定义你的方法,然后将它传递给 dict 参数的 type 。然后 abc 会将其视为正确的方法覆盖。
Aaand,使用新的元类,您可以在该方法中引用类对象。
这就是 super,因为现在你可以使用 super! =P
我猜你也担心这个……
看一下:
输出是:
I know this topic is really old but... That is really a nice question.
It doesn't work because abc can only check for abstract methods during instatiation of types, that is, when
type('Derived', (FooClass,), {})
is running. Any setattr done after that is not accessible from abc.So, setattr wont work, buuut...
Your problem of addressing the name of a class that wasn't previously declared or defined looks solvable:
I wrote a little metaclass that lets you use a placeholder "clazz" for accessing any class that will eventually get the method you are writing outside a class definition.
That way you won't get TypeError from abc anymore, since you can now define your method BEFORE instatiating your type, and then pass it to type at the dict argument. Then abc will see it as a proper method override.
Aaand, with the new metaclass you can refer to the class object during that method.
And this is super, because now you can use super! =P
I can guess you were worried about that too...
Take a look:
The output is:
好吧,如果你必须这样做,那么你可以只传递一个虚拟字典
{'FooMethod':None}
作为 type 的第三个参数。这允许衍生类型
满足ABCMeta的要求,即覆盖所有抽象方法。稍后您可以提供真正的FooMethod
:Well, if you must do it this way, then you could just pass a dummy dict
{'FooMethod':None}
as the third argument to type. This allowsderived_type
to satisfy ABCMeta's requirement that all abstract methods be overridden. Later on you can supply the realFooMethod
: