2013-01-19 2 views
18

My Python 애플리케이션에는 많은 추상 클래스와 구현이 포함되어 있습니다. 예를 들어 :Python 클래스가 추상 기본 클래스인지 콘크리트인지 확인

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 

주어진 클래스 객체는 (인스턴스화 할 수없는) 추상 클래스인가?

+0

Ref. 일반 독자에게 ABC : http://docs.python.org/2/library/abc.html –

답변

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

크리에이티브 솔루션이지만, 일부 추상 메소드에는'pass'뿐만 아니라 ('super'를 통해 호출되는) 구현이 있기 때문에 전체 프로젝트에서 실패합니다. –

관련 문제