2013-01-19 35 views
18

है मेरे पायथन एप्लिकेशन में कई सार कक्षाएं और कार्यान्वयन शामिल हैं। उदाहरण के लिए:निर्धारित करें कि कोई पाइथन क्लास एक सार बेस क्लास या कंक्रीट

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. 

... लेकिन MessageDisplay और FriendlyMessageDisplay सार वर्ग हैं और दृष्टांत के लिए एक में परिणाम होगा प्रयास कर रहा है एक त्रुटि:

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

मैं कैसे जांच सकता हूं कि कोई दिया गया वर्ग वस्तु एक (uninstantiatable) अमूर्त वर्ग है?

+0

रेफरी। आकस्मिक पाठकों के लिए, एबीसी: 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

क्रिएटिव समाधान, लेकिन यह मेरी पूरी परियोजना पर भी असफल होगा क्योंकि कई अमूर्त तरीकों में 'सुपर' के माध्यम से लागू किया गया है), केवल 'पास' नहीं। –