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
Related
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
I have the following snippet:
from abc import abstractproperty
class Base(object):
#abstractproperty
def foo(self):
print 'wat'
class C(Base):
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
c = C()
When I execute it, it gives the stacktrace:
in __init__ AttributeError: can't set attribute
on the line self.foo = 'test'.
Anyone know why this is happening?
First of all, note that you forgot to use ABCMeta as your metaclass. Change your code to:
For Python 2.x:
from abc import ABCMeta
class Base(object):
__metaclass__ = ABCMeta
...
For Python 3.x, any of the following:
from abc import ABC, ABCMeta
class Base(ABC):
...
class Base(metaclass=ABCMeta):
...
And you'll see this nice error:
Traceback (most recent call last):
File "a.py", line 14, in <module>
c = C()
TypeError: Can't instantiate abstract class C with abstract methods foo
It's telling you that you need a concrete implementation for your abstract property.
But that's not enough: to properly have self.foo = 'test' working, you need to implement a concrete setter for your property.
In the end, your code should look like this:
from abc import ABCMeta, abstractproperty
class Base(object):
__metaclass__ = ABCMeta
#abstractproperty
def foo(self):
print 'wat'
class C(Base):
#property
def foo(self):
# do something
#foo.setter
def foo(self, value):
# do something else
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
c = C()
Remember that you can use super() in your concrete property code to use the code from the abstract property.
Note: that since Python 3.3, #abstractproperty has been deprecated. Instead, you should use a combination of #property and #abstractmethod:
class Base(ABC):
#property
#abstractmethod
def foo(self):
...
Your code defines a read-only abstractproperty. See docs on ABC. In addition, you did not set ABCMeta as meta class, which is obligatory. Furthermore, an abstractproperty is abstract which means that it has to be overwritten in the child class.
If you want to create a read-write abstractproperty, go with something like this:
from abc import ABCMeta, abstractproperty
class Base(object):
__metaclass__ = ABCMeta # This is required.
def getfoo(self):
pass # Getter for preprocessing when property 'foo' is requested
def setfoo(self, thefoo):
pass # Setter for preprocessing when property 'foo' is set
foo = abstractproperty(getfoo, setfoo)
class C(Base):
foo = '' # Overwrite abstract property
def __init__(self):
self.foo = 'test'
self.bar = 'test2'
Using the code above, you can instantiate your class C and set its property foo:
c = C()
print(c.foo)
>>> test
What is wrong with the following code?
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): super(B).A_M()
error (Python 2.7.3):
>>> a = A()
>>> a.B.C()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "..x.py", line 36, in C
def C(): super(B).A_M()
NameError: global name 'B' is not defined
Edit:
the solution was simple as this:
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): A().A_M() #use of A() instead of supper, etc.
Important Note that there is an issue with this solution. If you change the name of super class (i.e. A) then you will have to update all uses inside itself as A :)).
class A(object):
def foo(self):
print('foo')
#staticmethod
def bar():
print('bar')
class B(object):
#staticmethod
def bar(obj):
# A.foo is not staticmethod, you can't use A.foo(),
# you need an instance.
# You also can't use super here to get A,
# because B is not subclass of A.
obj.foo()
A.foo(obj) # the same as obj.foo()
# A.bar is static, you can use it without an object.
A.bar()
class B(A):
def foo(self):
# Again, B.foo shouldn't be a staticmethod, because A.foo isn't.
super(B, self).foo()
#staticmethod
def bar():
# You have to use super(type, type) if you don't have an instance.
super(B, B).bar()
a, b = A(), B()
a.B.bar(a)
b.foo()
B.bar()
See this for details on super(B, B).
You need to use a fully-qualified name. Also, in python 2.7, you need to use (object), else super(A.B) will give TypeError: must be type, not classobj
class A(object):
def A_M(self):
pass
class B(object):
#staticmethod
def C():
super(A.B).A_M()
Finally, super(A.B) is essentially object here. Did you mean for B to inherit from A? Or were you simply looking for A.A_M()?
A latecommer, to just encapsulate B in A the easy way is this:
class A:
def A_M(self):
return "hi"
class B:
#staticmethod
def C():
return A().A_M()
a = A()
print a.B().C()
Not sure this is what you need, but the question was still unsolved, so I guessed.
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
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.