2015-09-17 49 views
7

我编写了一个装饰(出于好奇的缘故)在python中做了一个类抽象。到目前为止,它看起来会起作用,但我得到了意想不到的行为。Python抽象装饰不起作用

用于装修的想法是这样的:

from abc import ABCMeta, abstractmethod 

def abstract(cls): 
    cls.__metaclass__ = ABCMeta 
    return cls 

然后,使用该装饰时,它只是需要定义一个抽象的方法

@abstract 
class Dog(object): 
    @abstractmethod 
    def bark(self): 
     pass 

但是,当我考,我能实例化Dog对象:

d = Dog() 
d.bark() //no errors 
Dog.__metaclass__ //returned "<class 'abc.ABCMeta'>" 

当测试指定__metaclass__直接,它像预期的那样:

​​

测试:

d = Dog() 

"Traceback (most recent call last): 
    File "<pyshell#98>", line 1, in <module> 
    d = Dog() 
TypeError: Can't instantiate abstract class Dog with abstract methods bark" 

这究竟是为什么?

+0

检查我的答案,这解决了你的问题。感谢这个问题。 –

回答

2

我已经能够重现您的问题。

ABCMeta .__ new__永远不会使用您的装饰器调用。当你真的把它定义为Dog的元类时,它就会被调用。这是因为Dog类已经被定义了,所以之后添加ABCMeta实际上并没有什么区别,因为它的没有在定义时被调用,所以不会将Dog类注册为摘要,并且不会发现其抽象方法。我邀请你阅读ABCMeta。 新的代码,你会看到它的作用。

然而,我发现这个解决方案,还是让你的装饰工作:

def abstract(cls): 
    return ABCMeta(cls.__name__, cls.__bases__, dict(cls.__dict__)) 

这现在将按预期。意思是您必须继承Dog才能拨打bark

+0

工作,整洁! – Hydren

4

Python参考states

当类定义被读出,如果__metaclass__定义然后分配给它的调用将被调用,而不是类型()。这允许类或函数将被写入其中监视器或修改类创建过程

的重要组成部分,是当类定义为,这意味着你不能事后改变元类,这是只是装饰者试图做什么,因为它只是语法糖。

@some_decorator 
class SomeClass(object): 
    pass 

是一样的:

class SomeClass(object): 
    pass 

SomeClass = some_decorator(SomeClass) 

所以当装饰被调用时,类定义已经被阅读。

+0

非常简洁的答案。尽管Apero的答案给了我一个解决方案,但如果可以的话,我也会将你标记为“已接受”。 – Hydren