Problem:
I'd like to generate an abstract class with a few method stubs that should be implemented by its sub-classes thereby avoiding any hard-coding.
That is, if I define a class as follows:
>>> class Sample(SomeClass):
... ACTIONS = ('run', 'sleep', 'kill')
The above class should always implement the methods run, sleep, and kill.
Attempted solution:
The snippet below works as expected and raises an error when the abstract method run is not defined in subclass FooBar.
>>> class Foo(object):
... __metaclass__ = ABCMeta
...
... #abstractmethod
... def run(self, *args, **kwargs):
... pass
>>> class FooBar(Foo):
... def __init__(self, a, b):
... self.a = a
... self.b = b
>>> FooBar(1, 2)
TypeError: Can't instantiate abstract class FooBar with abstract methods run
I was trying to achieve the same by generating the Foo class using type:
>>> Foo2 = type('Foo', (), {'__metaclass__': ABCMeta,
... 'run' : abstractmethod(lambda self, *args, **kwargs: None)})
>>> class FooBar(Foo2):
... def __init__(self, a, b):
... self.a = a
... self.b = b
However, the below call does not raise any error:
>>> FooBar(1, 2)
<__main__.FooBar at ...>
Noticed that __abstractmethods__ doesn't get defined in Foo2 (comparing vars(Foo) and vars(Foo2)).
I did find a similar attempt here, but wasn't able to follow why the above wouldn't work and why I can't make use of ABCMeta directly (which defines __new__ in a generic manner).
Realized I could just do this :)
>>> Foo2 = ABCMeta('Foo', (), {'run': abstractmethod(lambda self, *args, **kwargs: None)})
>>> FooBar(1, 2)
TypeError: Can't instantiate abstract class FooBar with abstract methods run
Related
I can see code below
class MetaStrategy(StrategyBase.__class__): pass
I am not sure why not just write code like below
class MetaStrategy(StrategyBase): pass
Definition schematic
class StrategyBase(DataAccessor):
pass
class DataAccessor(LineIterator):
pass
class LineIterator(with_metaclass(MetaLineIterator, LineSeries)):
pass
def with_metaclass(meta, *bases):
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, str('temporary_class'), (), {})
If you call self.__class__ from a subclass instance, self.__class__ will use that type of the subclass.
Any class that is expressly specified while using the class will be used naturally.
Take the example below:
class Foo(object):
def create_new(self):
return self.__class__()
def create_new2(self):
return Foo()
class Bar(Foo):
pass
b = Bar()
c = b.create_new()
print type(c) # We got an instance of Bar
d = b.create_new2()
print type(d) # we got an instance of Foo
How do I create a decorator for an abstract class method in Python 2.7?
Yes, this is similar to this question, except I would like to combine abc.abstractmethod and classmethod, instead of staticmethod. Also, it looks like abc.abstractclassmethod was added in Python 3 (I think?), but I'm using Google App Engine, so I'm currently limited to Python 2.7
Thanks in advance.
Here's a working example derived from the source code in Python 3.3's abc module:
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
Here's what it looks like when running:
>>> 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
Note that the final example fails because cls() won't instantiate. ABCMeta prevents premature instantiation of classes that haven't defined all of the required abstract methods.
Another way to trigger a failure when the from_int() abstract class method is called is to have it raise an exception:
class DemoABC:
__metaclass__ = ABCMeta
#abstractclassmethod
def from_int(cls, n):
raise NotImplementedError
The design ABCMeta makes no effort to prevent any abstract method from being called on an uninstantiated class, so it is up to you to trigger a failure by invoking cls() as classmethods usually do or by raising a NotImplementedError. Either way, you get a nice, clean failure.
It is probably tempting to write a descriptor to intercept a direct call to an abstract class method, but that would be at odds with the overall design of ABCMeta (which is all about checking for required methods prior to instantiation rather than when methods are called).
Another possible workaround:
class A:
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def some_classmethod(cls):
"""IMPORTANT: this is a class method, override it with #classmethod!"""
class B(A):
#classmethod
def some_classmethod(cls):
print cls
Now, one still can't instantiate from A until some_classmethod is implemented, and it works if you implement it with a #classmethod.
You could upgrade to Python 3.
Starting with Python 3.3, it is possible to combine #classmethod and #abstractmethod:
import abc
class Foo(abc.ABC):
#classmethod
#abc.abstractmethod
def my_abstract_classmethod(...):
pass
Thanks to #gerrit for pointing this out to me.
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.
abc-extend.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)
Usage:
from abc-extend 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()
And here are some pyunit tests which give a more exhaustive demonstration.
test-abc-extend.py:
import unittest
import abc
oldclassmethod = classmethod
from abc-extend 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()
I haven't evaluated the performance cost of this solution, but it has worked for my purposes so far.
I've written the following code to help enforce the definition of certain methods for a class:
def createClassTemplate(name, requiredMethods=[], inherits=object):
def require(name):
def errorRaiser(self, *args, **kwargs):
raise RequireException("method '{}' must be defined.".format(name))
setattr(wrapper, "__name__", name)
return errorRaiser
class Custom(inherits): pass
setattr(Custom, "__name__", name)
for methodName in requiredMethods:
setattr(Custom, methodName, require(methodName))
return Custom
Which is implemented like this:
Model = createClassTemplate("Model", ["foo", "bar", "baz"])
class MyClass(Model):
pass
This way, when I'm missing a method, the calling class will generate a meaningful error indicating that I've failed to define a required method.
The problem is, the above code seems uncomfortably hacky and unpythonic. Am I going about this right way? Should I even be forcing class templates like this? And is there a better way to accomplish the same thing?
You can use the ABCMeta metaclass:
from abc import ABCMeta, abstractmethod
class MyClass:
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
Subclasses of MyClass must override foo to be allowed to be instantiated.
You should use a metaclass instead.
The standard library comes with a ready-made metaclass for just this task, the ABCMeta metaclass:
import abc
class Model(object):
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def foo(self):
pass
#abc.abstractmethod
def bar(self):
pass
#abc.abstractmethod
def baz(self):
pass
class MyClass(Model):
pass
Demonstration:
>>> import abc
>>> class Model(object):
... __metaclass__ = abc.ABCMeta
... #abc.abstractmethod
... def foo(self):
... pass
... #abc.abstractmethod
... def bar(self):
... pass
... #abc.abstractmethod
... def baz(self):
... pass
...
>>> class MyClass(Model):
... pass
...
>>> myclass = MyClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods bar, baz, foo
Once MyClass does provide implementations of the abstract methods, instantiation succeeds:
>>> class MyClass(Model):
... def foo(self): pass
... def bar(self): pass
... def baz(self): pass
...
>>> myclass = MyClass()
If you're using Python 2.6+, there's the concept of an ABC - http://docs.python.org/2/library/abc.html
It seems to me that you are trying to reinvent the Abstract Base Classes
I think you want something like this:
import abc
class MyBaseClass:
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def foo(self):
pass
#abc.abstractmethod
def bar(self):
pass
#abc.abstractmethod
def baz(self):
pas
I have a "partial" class that requires some mixin for its functionality (I want to do it with inheritance for performance and simplicity reasons). Can I declare that my class is going to need new methods?
Apparently the following guess does not work ("Can't instantiate abstract class"):
from abc import abstractmethod, ABCMeta
class A(metaclass=ABCMeta):
#abstractmethod
def a(self):
pass
class B:
def a(self):
return 12
class C(A, B):
pass
c = C()
Here A tries to declare that its other methods need a() to work.
(Python 3)
Any suggestions to the declare that?
You need to put B in front of A in the inheritance order for that to work:
>>> from abc import abstractmethod, ABCMeta
>>> class A:
... __metaclass__=ABCMeta
... #abstractmethod
... def a(self):
... pass
>>> class B:
... def a(self):
... return 12
>>> class C(B,A):
... pass
>>> c = C()
>>> c
<__main__.C object at 0x10e9c40d0>
>>> class D:
... pass
>>> class E(D, A):
... pass
>>> e = E()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: Can't instantiate abstract class E with abstract methods a
How do I create a decorator for an abstract class method in Python 2.7?
Yes, this is similar to this question, except I would like to combine abc.abstractmethod and classmethod, instead of staticmethod. Also, it looks like abc.abstractclassmethod was added in Python 3 (I think?), but I'm using Google App Engine, so I'm currently limited to Python 2.7
Thanks in advance.
Here's a working example derived from the source code in Python 3.3's abc module:
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
Here's what it looks like when running:
>>> 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
Note that the final example fails because cls() won't instantiate. ABCMeta prevents premature instantiation of classes that haven't defined all of the required abstract methods.
Another way to trigger a failure when the from_int() abstract class method is called is to have it raise an exception:
class DemoABC:
__metaclass__ = ABCMeta
#abstractclassmethod
def from_int(cls, n):
raise NotImplementedError
The design ABCMeta makes no effort to prevent any abstract method from being called on an uninstantiated class, so it is up to you to trigger a failure by invoking cls() as classmethods usually do or by raising a NotImplementedError. Either way, you get a nice, clean failure.
It is probably tempting to write a descriptor to intercept a direct call to an abstract class method, but that would be at odds with the overall design of ABCMeta (which is all about checking for required methods prior to instantiation rather than when methods are called).
Another possible workaround:
class A:
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def some_classmethod(cls):
"""IMPORTANT: this is a class method, override it with #classmethod!"""
class B(A):
#classmethod
def some_classmethod(cls):
print cls
Now, one still can't instantiate from A until some_classmethod is implemented, and it works if you implement it with a #classmethod.
You could upgrade to Python 3.
Starting with Python 3.3, it is possible to combine #classmethod and #abstractmethod:
import abc
class Foo(abc.ABC):
#classmethod
#abc.abstractmethod
def my_abstract_classmethod(...):
pass
Thanks to #gerrit for pointing this out to me.
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.
abc-extend.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)
Usage:
from abc-extend 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()
And here are some pyunit tests which give a more exhaustive demonstration.
test-abc-extend.py:
import unittest
import abc
oldclassmethod = classmethod
from abc-extend 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()
I haven't evaluated the performance cost of this solution, but it has worked for my purposes so far.