2013-01-19 49 views
18

我的Python应用程序包含许多抽象类和实现。例如:确定Python类是否为Abstract Base Class或具体

import abc 
import datetime 

class MessageDisplay(object): 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractproperty 
    def display(self, message): 
     pass 

class FriendlyMessageDisplay(MessageDisplay): 
    def greet(self): 
     hour = datetime.datetime.now().timetuple().tm_hour 

     if hour < 7: 
      raise Exception("Cannot greet while asleep.") 
     elif hour < 12: 
      self.display("Good morning!") 
     elif hour < 18: 
      self.display("Good afternoon!") 
     elif hour < 20: 
      self.display("Good evening!") 
     else: 
      self.display("Good night.") 

class FriendlyMessagePrinter(FriendlyMessageDisplay): 
    def display(self, message): 
     print(message) 

FriendlyMessagePrinter是一个具体的类,我们可以使用...

FriendlyMessagePrinter().greet() 
Good night. 

...但MessageDisplayFriendlyMessageDisplay是抽象类,并试图实例化一个会导致一个错误:

TypeError: Can't instantiate abstract class MessageDisplay with abstract methods say 

如何检查是否a给定的类对象是一个(不可证实的)抽象类?

+0

Ref。对于偶然的读者,ABC:http://docs.python.org/2/library/abc.html – 2013-01-19 05:17:02

回答

19
import inspect 
print(inspect.isabstract(object))     # False 
print(inspect.isabstract(MessageDisplay))   # True 
print(inspect.isabstract(FriendlyMessageDisplay)) # True 
print(inspect.isabstract(FriendlyMessagePrinter)) # False 

这就验证了内部标志TPFLAGS_IS_ABSTRACT在类对象设置,所以它不能与您的实施一样容易上当:

class Fake: 
    __abstractmethods__ = 'bluh' 

print(is_abstract(Fake), inspect.isabstract(Fake)) # True, False 
3

抽象类及其具体实现具有__abstractmethods__属性,其中包含尚未实现的抽象方法和属性的名称。这种行为在PEP 3199描述:

Implementation: The @abstractmethod decorator sets the function attribute __isabstractmethod__ to the value True . The ABCMeta.__new__ method computes the type attribute __abstractmethods__ as the set of all method names that have an __isabstractmethod__ attribute whose value is true. It does this by combining the __abstractmethods__ attributes of the base classes, adding the names of all methods in the new class dict that have a true __isabstractmethod__ attribute, and removing the names of all methods in the new class dict that don't have a true __isabstractmethod__ attribute. If the resulting __abstractmethods__ set is non-empty, the class is considered abstract, and attempts to instantiate it will raise TypeError. (If this were implemented in CPython, an internal flag Py_TPFLAGS_ABSTRACT could be used to speed up this check.)

因此,在具体的类,此属性将要么不存在或将是一个空集。这是很容易检查:

def is_abstract(cls): 
    if not hasattr(cls, "__abstractmethods__"): 
     return False # an ordinary class 
    elif len(cls.__abstractmethods__) == 0: 
     return False # a concrete implementation of an abstract class 
    else: 
     return True # an abstract class 

或者更简洁地说:

def is_abstract(cls): 
    return bool(getattr(cls, "__abstractmethods__", False)) 
print(is_abstract(object))     # False 
print(is_abstract(MessageDisplay))   # True 
print(is_abstract(FriendlyMessageDisplay)) # True 
print(is_abstract(FriendlyMessagePrinter)) # False 
-1

您可以使用_ast模块进行此操作。例如,如果您的示例代码在foo.py中,则可以使用"foo.py""FriendlyMessagePrinter"作为参数来调用此函数。

def is_abstract(filepath, class_name): 
    astnode = compile(open(filename).read(), filename, 'exec', _ast.PyCF_ONLY_AST) 
    for node in astnode.body: 
     if isinstance(node, _ast.ClassDef) and node.name == class_name: 
      for funcdef in node.body: 
       if isinstance(funcdef, _ast.FunctionDef): 
        if any(not isinstance(n, _ast.Pass) for n in funcdef.body): 
         return False 
      return True 
    print 'class %s not found in file %s' %(class_name, filepath) 
+0

's/filepath/filename'。但是这显然是错的,对吧?它直接失败了一个没有方法的类,并且如果我简单地决定创建一个像'def bluh(self):pass'这样的函数。 – mmgp

+0

创造性的解决方案,但这也会在我的完整项目上失败,因为几个抽象方法的实现(通过'super'调用),而不仅仅是'pass'。 –

相关问题