Python “;Can';t实例化抽象类…;用抽象方法”;在不应该';我没有任何抽象的方法

Python “;Can';t实例化抽象类…;用抽象方法”;在不应该';我没有任何抽象的方法,python,abstract-class,Python,Abstract Class,以下面的例子为例: 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): pr

以下面的例子为例:

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=derived_type()
行。)

但是
FooMethod
不应该是抽象的:我已经用
BarOverride
覆盖了它。那么,为什么这会引发例外呢

免责声明:是的,我可以使用显式
语法,完成完全相同的事情。(更好的是,我可以让它工作!)但这是一个最小的测试用例,更大的示例是动态创建类。:-)我很好奇为什么这不起作用

编辑:为了避免另一个明显的非答案:我不想将第三个参数中的
BarOverride
传递给
type
:在实际示例中,
BarOverride
需要将
派生类型
绑定到它。如果我可以在创建
派生类型
后定义
BarOverride
,这样做会更容易。(如果我做不到,为什么?

向类动态添加抽象方法,或尝试 在方法或类创建后修改其抽象状态, 不支持。abstractmethod()只影响子类 使用规则继承派生的;“虚拟子类”已注册 ABC的register()方法不受影响


只有在定义类时才调用元类。当
abstractmethod
将一个类标记为抽象时,该状态以后不会更改

好的,如果必须这样做,那么您可以传递一个伪dict
{'foodmethod':None}
作为要键入的第三个参数。这允许
derived_type
满足ABCMeta的要求,即覆盖所有抽象方法。稍后,您可以提供真正的
foodmethod

def main():
  derived_type = type('Derived', (FooClass,), {'FooMethod':None})
  def BarOverride(self):
    print 'Hello, world!'
  setattr(derived_type, 'FooMethod', BarOverride)
  instance = derived_type()

Jochen是对的;抽象方法是在类创建时设置的,不会因为重新分配属性而修改

您可以通过执行以下操作手动将其从抽象方法列表中删除

DerivedType.__abstractmethods__ = frozenset()


还有
setattr
,所以它仍然不认为
FooMethod
是抽象的。

我知道这个话题很老,但是。。。这真是个好问题

它不起作用,因为abc只能在类型的安装期间检查抽象方法,也就是说,当
type('Derived',(FooClass,),{}
正在运行时。在此之后完成的任何setattr都无法从abc访问

所以,setattr不起作用,buuut。。。 解决以前未声明或定义的类的名称的问题看起来是可以解决的:

我编写了一个小元类,它允许您使用占位符“clazz”来访问最终将获得您在类定义之外编写的方法的任何类

这样,您就不会再从abc获得TypeError,因为您现在可以在恢复类型之前定义方法,然后在dict参数处将其传递给type。然后abc会将其视为一个合适的方法重写

aa并且,使用新的元类,您可以在该方法期间引用类对象。 这是超级,因为现在你可以使用超级=P 我猜你也很担心

看一看:

import abc
import inspect

clazz = type('clazz', (object,), {})()

def clazzRef(func_obj):
    func_obj.__hasclazzref__ = True
    return func_obj

class MetaClazzRef(type):
    """Makes the clazz placeholder work.

    Checks which of your functions or methods use the decorator clazzRef
    and swaps its global reference so that "clazz" resolves to the
    desired class, that is, the one where the method is set or defined.

    """
    methods = {}
    def __new__(mcs, name, bases, dict):
        ret = super(MetaClazzRef, mcs).__new__(mcs, name, bases, dict)
        for (k,f) in dict.items():
            if getattr(f, '__hasclazzref__', False):
                if inspect.ismethod(f):
                    f = f.im_func
                if inspect.isfunction(f):
                    for (var,value) in f.func_globals.items():
                        if value is clazz:
                            f.func_globals[var] = ret
        return ret

class MetaMix(abc.ABCMeta, MetaClazzRef):
    pass

class FooClass(object):
    __metaclass__ = MetaMix
    @abc.abstractmethod
    def FooMethod(self):
        print 'Ooops...'
        #raise NotImplementedError()


def main():
    @clazzRef
    def BarOverride(self):
        print "Hello, world! I'm a %s but this method is from class %s!" % (type(self), clazz)
        super(clazz, self).FooMethod() # Now I have SUPER!!!

    derived_type = type('Derived', (FooClass,), {'FooMethod': BarOverride})

    instance = derived_type()
    instance.FooMethod()

    class derivedDerived(derived_type):
        def FooMethod(self):
            print 'I inherit from derived.'
            super(derivedDerived,self).FooMethod()

    instance = derivedDerived()
    instance.FooMethod()

main()
输出为:

Hello, world! I'm a <class 'clazz.Derived'> but this method is from class <class 'clazz.Derived'>!
Ooops...
I inherit from derived.
Hello, world! I'm a <class 'clazz.derivedDerived'> but this method is from class <class 'clazz.Derived'>!
Ooops...
你好,世界!我是一个学生,但是这个方法是从课堂上学来的!
哎呀。。。
我继承了他的遗产。
你好,世界!我是一个学生,但是这个方法是从课堂上学来的!
哎呀。。。

类的抽象性在类构造期间确定。为什么不在创建类时简单地在字典中包含
FooMethod
?@SvenMarnach:
BarOverride
,在实际示例中,需要将派生类绑定到它。事后创建函数是实现这一点的最简单方法。我不明白这一点。您所说的“需要将派生类绑定到它”是什么意思?但这会破坏将
FooMethod
抽象化的某些意义,因为如果类型创建和
setattr
分开,在设置方法之前,您可以实例化
派生的\u type
。类型为
ABCMeta
的类应该与任何其他类一样可以进行猴子修补。通过提供第三个参数,您确认
派生的
必须定义
方法
。这并不妨碍您以后改变对其实现的想法。接受这一点作为答案(因为它包含我的代码无法工作的原因),但这三点都很有趣。我选择了unutbu的答案作为手头问题的解决方法。我想这是错误的。我应该使用函数的闭包,而不是globals()。Globals()是,呃,全局的。。。但是,奇怪的是,代码看起来像预期的那样工作,即使有更多的类。。。
Hello, world! I'm a <class 'clazz.Derived'> but this method is from class <class 'clazz.Derived'>!
Ooops...
I inherit from derived.
Hello, world! I'm a <class 'clazz.derivedDerived'> but this method is from class <class 'clazz.Derived'>!
Ooops...