Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I recently encountered the same problem. That is, I needed abstract classmethods but was unable to use Python 3 because of other project constraints. The solution I came up with is the following.</p> <p>abcExtend.py:</p> <pre><code>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) </code></pre> <p>Usage:</p> <pre><code>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() </code></pre> <p>And here are some pyunit tests which give a more exhaustive demonstration.</p> <p>testAbcExtend.py:</p> <pre><code>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() </code></pre> <p>I haven't evaluated the performance cost of this solution, but it has worked for my purposes so far.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload