2012-06-27 24 views
25

मैं पायथन 2.7 में एक अमूर्त वर्ग विधि के लिए सजावट कैसे बना सकता हूं?पायथन 2.7 abc.abstractmethod और classmethod को मिलाएं

हाँ, यह this question के समान है, सिवाय इसके कि मैं staticmethod के बजाय, abc.abstractmethod और classmethod गठबंधन करना चाहते हैं। इसके अलावा, ऐसा लगता है कि abc.abstractclassmethodadded in Python 3 (I think?) था, लेकिन मैं Google App Engine का उपयोग कर रहा हूं, इसलिए मैं वर्तमान में पाइथन 2.7

अग्रिम धन्यवाद।

+0

यदि आप abc.abstractclassmethod का उपयोग नहीं कर सकते हैं, तो यह कैसे एक और सजावट के साथ गठबंधन करने का मुद्दा उठता है? – abarnert

+3

सोचें कि आप गलत पढ़ते हैं: abc.abstractclassmethod = abc.abstractmethod + classmethod। पायथन 2.7 में abstractmethod और classmethod है, लेकिन abstractclassmethod नहीं – jchu

+1

मुझे बिल्कुल इसी तरह की समस्या का सामना करना पड़ रहा है! अच्छा प्रश्न! –

उत्तर

24

यहाँ एक काम कर उदाहरण पायथन में स्रोत कोड से ली गई है 3.3 के एबीसी मॉड्यूल:

>>> d = DemoConcrete(5)    # Succeeds by calling a concrete __init__() 
Initializing with 5 

>>> d = DemoConcrete.from_int(5) # Succeeds by calling a concrete from_int() 
Initializing with 10 

>>> DemoABC()      # Fails because from_int() is abstract  
Traceback (most recent call last): 
    ... 
TypeError: Can't instantiate abstract class DemoABC with abstract methods from_int 

>>> DemoABC.from_int(5)    # Fails because from_int() is not implemented 
Traceback (most recent call last): 
    ... 
TypeError: Can't instantiate abstract class DemoABC with abstract methods from_int 

ध्यान दें कि अंतिम उदाहरण:

from abc import ABCMeta 

class abstractclassmethod(classmethod): 

    __isabstractmethod__ = True 

    def __init__(self, callable): 
     callable.__isabstractmethod__ = True 
     super(abstractclassmethod, self).__init__(callable) 

class DemoABC: 

    __metaclass__ = ABCMeta 

    @abstractclassmethod 
    def from_int(cls, n): 
     return cls() 

class DemoConcrete(DemoABC): 

    @classmethod 
    def from_int(cls, n): 
     return cls(2*n) 

    def __init__(self, n): 
     print 'Initializing with', n 

यहाँ क्या यह जब चल रहा है की तरह लग रहा है विफल रहता है क्योंकि cls() तत्काल नहीं होगा। एबीसीएमटा उन वर्गों के समयपूर्व तत्कालता को रोकता है जिन्होंने सभी आवश्यक सार विधियों को परिभाषित नहीं किया है।

एक और तरीका है जब from_int() सार वर्ग विधि कहा जाता है यह एक अपवाद बढ़ा है एक विफलता को गति प्रदान करने:

class DemoABC: 

    __metaclass__ = ABCMeta 

    @abstractclassmethod 
    def from_int(cls, n): 
     raise NotImplementedError 

डिजाइन ABCMeta किसी भी सार विधि को रोकने के लिए कोई प्रयास नहीं करता एक uninstantiated कक्षा पर बुलाया जा रहा है, तो यह cls() का आह्वान करके विफलता को ट्रिगर करने के लिए आप पर निर्भर करता है क्योंकि क्लासमेड आमतौर पर करते हैं या नहीं बढ़ाया गया है। किसी भी तरह से, आपको एक अच्छी, साफ विफलता मिलती है।

यह शायद एक अमूर्त वर्ग विधि के लिए एक सीधा कॉल अवरोधन करने की जानकारी देता लिखने के लिए आकर्षक है, लेकिन वह ABCMeta (सब के बारे में आवश्यक तरीकों इन्स्टेन्शियशन से पहले के लिए जाँच है, जिनमें से समग्र डिजाइन के साथ अंतर पर होगा बल्कि जब विधियों को बुलाया जाता है)।

+2

पाइथन स्रोत कोड को जांचने का अच्छा विचार, लेकिन दुर्भाग्यवश, जब मैं 'classmethod' लिखता हूं, उसके बाद' abc.abstractmethod' के बाद, यह 'abstractmethod' को लागू नहीं करता है। यानी मैं अभी भी विधि को कॉल कर सकता हूं। – jchu

+1

@jchu एबीसीएमटा क्लास इंस्टेंटेशन को रोकने के बारे में है जब अमूर्त विधियां गायब हैं। एक uninstantiated कक्षा पर अमूर्त तरीकों के लिए कॉल को रोकने के लिए इसका कोई कोड नहीं है। यदि आप एक अमूर्त वर्ग विधि के लिए कॉल पर एक साफ विफलता चाहते हैं, तो इसे * NotImplementedError * बढ़ाएं या इसे '' cls() '' के साथ तत्काल करने का प्रयास करें। –

+0

तो यह किसी ऑब्जेक्ट को क्लासमेड से तत्काल करने के लिए एक सामान्य पायथन पैटर्न है? मेरे विशेष उपयोग के मामले के लिए, किसी ऑब्जेक्ट को क्लासमेड के भीतर से तुरंत चालू करने का अर्थ नहीं है, जो शायद मेरे डिस्कनेक्ट को बताता है। जैसा कि आपने अपने वैकल्पिक विकल्प में उल्लेख किया है, मैंने अमूर्त बेस क्लास में NotImplementedError को बढ़ाया है। लेकिन उस स्थिति में, क्या व्यवहार में कोई अंतर है यदि आपने अमूर्त क्लासमेड सजावट या क्लासमेड सजावट (इरादे में स्पष्टता के अलावा) का उपयोग किया है? – jchu

2

मुझे हाल ही में एक ही समस्या का सामना करना पड़ा। यही है, मुझे अमूर्त कक्षाओं की आवश्यकता थी लेकिन अन्य परियोजना बाधाओं के कारण पाइथन 3 का उपयोग करने में असमर्थ था। जिस समाधान के साथ मैं आया था वह निम्नलिखित है।

abcExtend.py:

import abc 

class instancemethodwrapper(object): 
    def __init__(self, callable): 
     self.callable = callable 
     self.__dontcall__ = False 

    def __getattr__(self, key): 
     return getattr(self.callable, key) 

    def __call__(self, *args, **kwargs): 
     if self.__dontcall__: 
      raise TypeError('Attempted to call abstract method.') 
     return self.callable(*args,**kwargs) 

class newclassmethod(classmethod): 
    def __init__(self, func): 
     super(newclassmethod, self).__init__(func) 
     isabstractmethod = getattr(func,'__isabstractmethod__',False) 
     if isabstractmethod: 
      self.__isabstractmethod__ = isabstractmethod 

    def __get__(self, instance, owner): 
     result = instancemethodwrapper(super(newclassmethod, self).__get__(instance, owner)) 
     isabstractmethod = getattr(self,'__isabstractmethod__',False) 
     if isabstractmethod: 
      result.__isabstractmethod__ = isabstractmethod 
      abstractmethods = getattr(owner,'__abstractmethods__',None) 
      if abstractmethods and result.__name__ in abstractmethods: 
       result.__dontcall__ = True 
     return result 

class abstractclassmethod(newclassmethod): 
    def __init__(self, func): 
     func = abc.abstractmethod(func) 
     super(abstractclassmethod,self).__init__(func) 

उपयोग:

from abcExtend import abstractclassmethod 

class A(object): 
    __metaclass__ = abc.ABCMeta  
    @abstractclassmethod 
    def foo(cls): 
     return 6 

class B(A): 
    pass 

class C(B): 
    @classmethod 
    def foo(cls): 
     return super(C,cls).foo() + 1 

try: 
    a = A() 
except TypeError: 
    print 'Instantiating A raises a TypeError.' 

try: 
    A.foo() 
except TypeError: 
    print 'Calling A.foo raises a TypeError.' 

try: 
    b = B() 
except TypeError: 
    print 'Instantiating B also raises a TypeError because foo was not overridden.' 

try: 
    B.foo() 
except TypeError: 
    print 'As does calling B.foo.' 

#But C can be instantiated because C overrides foo 
c = C() 

#And C.foo can be called 
print C.foo() 

और यहाँ कुछ pyunit परीक्षण जो एक और अधिक संपूर्ण प्रदर्शन दे रहे हैं।

testAbcExtend.py:

import unittest 
import abc 
oldclassmethod = classmethod 
from abcExtend import newclassmethod as classmethod, abstractclassmethod 

class Test(unittest.TestCase): 
    def setUp(self): 
     pass 

    def tearDown(self): 
     pass 

    def testClassmethod(self): 
     class A(object): 
      __metaclass__ = abc.ABCMeta    
      @classmethod 
      @abc.abstractmethod 
      def foo(cls): 
       return 6 

     class B(A): 
      @classmethod 
      def bar(cls): 
       return 5 

     class C(B): 
      @classmethod 
      def foo(cls): 
       return super(C,cls).foo() + 1 

     self.assertRaises(TypeError,A.foo) 
     self.assertRaises(TypeError,A) 
     self.assertRaises(TypeError,B) 
     self.assertRaises(TypeError,B.foo) 
     self.assertEqual(B.bar(),5) 
     self.assertEqual(C.bar(),5) 
     self.assertEqual(C.foo(),7) 

    def testAbstractclassmethod(self): 
     class A(object): 
      __metaclass__ = abc.ABCMeta  
      @abstractclassmethod 
      def foo(cls): 
       return 6 

     class B(A): 
      pass 

     class C(B): 
      @oldclassmethod 
      def foo(cls): 
       return super(C,cls).foo() + 1 

     self.assertRaises(TypeError,A.foo) 
     self.assertRaises(TypeError,A) 
     self.assertRaises(TypeError,B) 
     self.assertRaises(TypeError,B.foo) 
     self.assertEqual(C.foo(),7) 
     c = C() 
     self.assertEqual(c.foo(),7) 

if __name__ == "__main__": 
    #import sys;sys.argv = ['', 'Test.testName'] 
    unittest.main() 

मैं मूल्यांकन नहीं किया इस समाधान का प्रदर्शन लागत, लेकिन यह मेरी प्रयोजनों के लिए अब तक काम किया है।

14

एक अन्य संभावित वैकल्पिक हल:

class A: 
    __metaclass__ = abc.ABCMeta 

    @abc.abstractmethod 
    def some_classmethod(cls): 
     """IMPORTANT: this is class method, override it with @classmethod!""" 
     pass 

class B(A): 
    @classmethod 
    def some_classmethod(cls): 
     print cls 

अब, एक अभी भी 'some_classmethod' जब तक एक से दृष्टांत नहीं कर सकते हैं कार्यान्वित किया जाता है, और यदि आप इसे एक classmethod साथ लागू यह काम करता है।

+0

यह शीर्ष उत्तर होना चाहिए –

संबंधित मुद्दे