Related
I'm trying to implement an abstract class with attributes and I can't get how to define it simply.
I just want to define the attribute name to constrain child classes to have it but I don't want to copy/paste getters & setters in every classes that inherit my abstract class.
Here are solutions I found but not very elegant in my opinion:
Maybe the most efficient and robust way, but quite ugly and redundant. We have to put 'def a(): pass', in each child class
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
def a(self):
pass
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
Quite the same, but uglier imo
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC1(AbstractC):
a = None
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC3(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # error !
ConcreteC3('foobar') # error !
Most compact way, but not robust. No error if 'a' is missing
class AbstractC(ABC):
#abstractmethod
def __init__(self, val):
self.a = val
class ConcreteC1(AbstractC):
def __init__(self, name):
self.a = name
class ConcreteC2(AbstractC):
def __init__(self, name):
self.poney = name
ConcreteC1('foobar') # ok
ConcreteC2('foobar') # no error !
So is there a way to get an elegant, robust and compact abstract class with abstract attribute ? Or am I trying to get something impossible ? I was thinking about something close to that :
class AbstractC(ABC):
#property
#abstractmethod
def a(self):
pass
class ConcreteC(AbstractC):
def __init__(self, name):
self.a = name
If there is no such solution, what is the best one ?
You could misuse namedtuples for fancy inheritance
from collections import namedtuple
BaseAttributes = namedtuple('base', ['attr1', 'attr2'])
print(BaseAttributes('one', 2))
class SomethingElse(BaseAttributes):
def method(self):
return 3
blubb = SomethingElse('A', 5)
blubb.method()
but imho your last proposal(s) makes sense if you raise NotImplementedError, e.g.:
class AbstractC(ABC):
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.a = name
Maybe this will help. I made a class which inherits from ABC. It defines the method __init_subclass__ that is invoked after a new subclass is created. It does the next: For each abstract property declared, search the same method in the subclass. If it exists (its a function object) convert it to a property and replace it in the subclass dictionary.
from abc import ABC, abstractmethod
class Foo(ABC):
def __init_subclass__(cls):
super().__init_subclass__()
###### This is the new part. I explain it at the end of the answer
for name, value in attrs.items():
if name not in cls.__dict__:
setattr(cls, name, property(lambda *args, **kwargs: value))
######
# Iterate throught all abstract methods on the class
for name in Foo.__abstractmethods__:
absmethod = Foo.__dict__[name]
# Check if the abstract method is a property
if not isinstance(absmethod, property):
continue
# Check if there is a method defined in the subclass with the same name
if name not in cls.__dict__ or not callable(cls.__dict__[name]):
continue
method = cls.__dict__[name]
# If the method is not already a property, we decorate it automatically...
if not isinstance(method, property):
setattr(cls, name, property(method))
#property
#abstractmethod
def a(self):
return 1
Now define a subclass and test it:
class Bar(Foo):
def __init__(self):
pass
def a(self):
return 2
#property
def b(self):
return 3
obj = Bar()
print(obj.a)
print(obj.b)
Output will be:
2
3
The next code will raise an error, because not all abstract methods are implemented:
class Qux(Foo):
pass
EDIT:
Now you can also do:
class Bar(Foo, a=1):
pass
print(Bar().a) # 1
There's still a problem. If i choose the implementation that raise an error, i have to add #property to the method or i can call ConcreteC().a even if a is not set and it will not raise the error:
class AbstractC(ABC):
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.poney = val
In [3]: ConcreteC('foobar').a
Out[3]: <bound method AbstractC.a of <__main__.ConcreteC object at 0x7f2e1c6b0518>>
But if i add #property i get an error :
class AbstractC(ABC):
#property
def a(self):
raise NotImplementedError('Implement _a_ method')
class ConcreteC(AbstractC):
def __init__(self, val):
super().__init__()
self.a = val
In [4]: ConcreteC('foobar')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-55-587237cb76e5> in <module>
----> 1 ConcreteC('foobar')
~/workspace/draft.py in __init__(self, val)
151 def __init__(self, val):
152 super().__init__()
--> 153 self.a = val
154
155
AttributeError: can't set attribute
EDIT:
Here the solution I chose:
class AbstractC(ABC):
#property
def a(self):
try:
return self._a
except AttributeError:
raise NotImplementedError('Implement _a_ method')
#a.setter
def a(self, val):
self._a = val
class ConcreteC(AbstractC):
def __init__(self, val):
self.a = val
This way I can edit 'a' very simply and if it's not definied, an exception is raised on get. I didn't know that to make a setter work, it must has the same name as the property.
In the end, what I wanted isn't an abstract attribute, but a concrete one in an abstract class.
In [1]: c = ConcreteC('foobar')
In [2]: c.a
Out[2]: 'foobar'
In [3]: c.a = 'poney'
In [4]: c.a
Out[4]: 'poney'
I have the following classes implementing a "Delegation Design Pattern" with an additional DelegatorParent class:
class DelegatorParent():
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee():
def myMethod(self):
return 'myMethod'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
def __getattr__(self, attrname):
return getattr(self.delegatee, attrname)
a = Delegator()
result = a.myMethod()
Everything looks fine.
Now I would like to put an abstract method in DelegatorParent, to ensure that "myMethod" is always defined.
from abc import ABCMeta, abstractmethod
class DelegatorParent():
__metaclass__ = ABCMeta
#abstractmethod
def myMethod(self):
pass
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee():
def myMethod(self):
return 'myMethod'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
def __getattr__(self, attrname):
return getattr(self.delegatee, attrname)
# This method seems unnecessary, but if I erase it an exception is
# raised because the abstract method's restriction is violated
def myMethod(self):
return self.delegatee.myMethod()
a = Delegator()
result = a.myMethod()
Can you help me find an "elegant" way to remove "myMethod" from "Delegator"... Intuition tells me that it is somehow redundant (considering that a custom getattr method is defined).
And more importantly, notice that with this implementation, if I forget to define myMethod in ConcreteDelegatee the program compiles, but it may crash in runtime if I call Delegator.myMethod(), which is exactly what I wanted to avoid by using abstract methods in DelegatorParent.
Obviously a simple solution would be to move #abstractmethod to the Delegator class, but I want to avoid doing that because in my program DelegatorParent is a very important class (and Delegator is just an auxiliary class).
You can decide to automatically implement abstract methods delegared to ConcreteDelegatee.
For each abstract method, check if it's name exist in the ConcreteDelegatee class and implement this method as a delegate to this class method.
from abc import ABCMeta, abstractmethod
class DelegatorParent(object):
__metaclass__ = ABCMeta
def __init__(self):
self.a = 'whatever'
#abstractmethod
def myMethod(self):
pass
class Delegatee(object):
pass
class ConcreteDelegatee(Delegatee):
def myMethod(self):
return 'myMethod'
def myMethod2(self):
return 'myMethod2'
class Delegator(DelegatorParent):
def __new__(cls, *args, **kwargs):
implemented = set()
for name in cls.__abstractmethods__:
if hasattr(ConcreteDelegatee, name):
def delegated(this, *a, **kw):
meth = getattr(this.delegatee, name)
return meth(*a, **kw)
setattr(cls, name, delegated)
implemented.add(name)
cls.__abstractmethods__ = frozenset(cls.__abstractmethods__ - implemented)
obj = super(Delegator, cls).__new__(cls, *args, **kwargs)
obj.delegatee = ConcreteDelegatee()
return obj
def __getattr__(self, attrname):
# Called only for attributes not defined by this class (or its bases).
# Retrieve attribute from current behavior delegate class instance.
return getattr(self.delegatee, attrname)
# All abstract methods are delegared to ConcreteDelegatee
a = Delegator()
print(a.myMethod()) # correctly prints 'myMethod'
print(a.myMethod2()) #correctly prints 'myMethod2'
This solves the main problem (prevent ConcreteDelegatee from forgetting to define myMethod). Other abstract methods are still checked if you forgot to implement them.
The __new__ method is in charge of the delegation, that frees your __init__ to do it.
Since you use ABCMeta, you must defined the abstract methods. One could remove your method from the __abstractmethods__ set, but it is a frozenset. Anyway, it involves listing all abstract methods.
So, instead of playing with __getattr__, you can use a simple descriptor.
For instance:
class Delegated(object):
def __init__(self, attrname=None):
self.attrname = attrname
def __get__(self, instance, owner):
if instance is None:
return self
delegatee = instance.delegatee
return getattr(delegatee, self.attrname)
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
myMethod = Delegated('myMethod')
An advantage here: the developer has the explicit information that "myMethod" is delegated.
If you try:
a = Delegator()
result = a.myMethod()
It works! But if you forget to implement myMethod in Delegator class, you have the classic error:
Traceback (most recent call last):
File "script.py", line 40, in <module>
a = Delegator()
TypeError: Can't instantiate abstract class Delegator with abstract methods myMethod
Edit
This implementation can be generalized as follow:
class DelegatorParent():
__metaclass__ = ABCMeta
#abstractmethod
def myMethod1(self):
pass
#abstractmethod
def myMethod2(self):
pass
def __init__(self):
self.a = 'whatever'
class ConcreteDelegatee1():
def myMethod1(self):
return 'myMethod1'
class ConcreteDelegatee2():
def myMethod2(self):
return 'myMethod2'
class DelegatedTo(object):
def __init__(self, attrname):
self.delegatee_name, self.attrname = attrname.split('.')
def __get__(self, instance, owner):
if instance is None:
return self
delegatee = getattr(instance, self.delegatee_name)
return getattr(delegatee, self.attrname)
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee1 = ConcreteDelegatee1()
self.delegatee2 = ConcreteDelegatee2()
DelegatorParent.__init__(self)
myMethod1 = DelegatedTo('delegatee1.myMethod1')
myMethod2 = DelegatedTo('delegatee2.myMethod2')
a = Delegator()
result = a.myMethod2()
Here, we can specify the delegatee name and delegatee method.
Here is my current solution. It solves the main problem (prevent ConcreteDelegatee from forgetting to define myMethod), but I'm still not convinced because I still need to define myMethod inside Delegator, which seems redundant
from abc import ABCMeta, abstractmethod
class DelegatorParent(object):
__metaclass__ = ABCMeta
def __init__(self):
self.a = 'whatever'
#abstractmethod
def myMethod(self):
pass
class Delegatee(object):
def checkExistence(self, attrname):
if not callable(getattr(self, attrname, None)):
error_msg = "Can't instantiate " + str(self.__class__.__name__) + " without abstract method " + attrname
raise NotImplementedError(error_msg)
class ConcreteDelegatee(Delegatee):
def myMethod(self):
return 'myMethod'
def myMethod2(self):
return 'myMethod2'
class Delegator(DelegatorParent):
def __init__(self):
self.delegatee = ConcreteDelegatee()
DelegatorParent.__init__(self)
for method in DelegatorParent.__abstractmethods__:
self.delegatee.checkExistence(method)
def myMethod(self, *args, **kw):
return self.delegatee.myMethod(*args, **kw)
def __getattr__(self, attrname):
# Called only for attributes not defined by this class (or its bases).
# Retrieve attribute from current behavior delegate class instance.
return getattr(self.delegatee, attrname)
# if I forget to implement myMethod inside ConcreteDelegatee,
# the following line will correctly raise an exception saying
# that 'myMethod' is missing inside 'ConcreteDelegatee'.
a = Delegator()
print a.myMethod() # correctly prints 'myMethod'
print a.myMethod2() #correctly prints 'myMethod2'
I need the settings in the parent to contain the functions from the child. (Every child of this parent will use that settings, but the way the functions are defined will change)
Is this possible? Currently it appears to read in the undefined functions from the parent class (and thus breaks)
class Mine(object):
__metaclass__ = ABCMeta
#abstractmethod
def _get_stuff(self):
raise NotImplementedError()
#abstractmethod
def _set_stuff(self, value):
raise NotImplementedError()
settings = property(_get_stuff, _set_stuff)
def do_stuff_with_settings(self):
return settings
class Child(Mine):
def _get_stuff(self):
return {"a": 2}
def _set_stuff(self):
pass
def do_stuff(self):
self.a = do_stuff_with_settings.a
From the docs
a metaclass derived from ABCMeta cannot be instantiated unless all of
its abstract methods and properties are overridden.
I changed how you created the base class to class Mine(metaclass=ABCMeta):
and received the error
Traceback (most recent call last):
File "g.py", line 24, in <module>
Child().settings
TypeError: Can't instantiate abstract class Child with abstract methods settings
So that makes the problem more explicit. To fix the problem, just have the child classes create their own settings property. Now they are bound to the concrete methods instead of the abstract methods.
from abc import ABCMeta,abstractmethod
class Mine(object):
__metaclass__ = ABCMeta
#abstractmethod
def _get_stuff(self):
raise NotImplementedError()
#abstractmethod
def _set_stuff(self, value):
raise NotImplementedError()
settings = property(_get_stuff, _set_stuff)
def do_stuff_with_settings(self):
return settings
class Child(Mine):
def _get_stuff(self):
return 1
def _set_stuff(self):
pass
settings = property(_get_stuff, _set_stuff)
Child().settings
UPDATE
This isn't just an ABC issue. You have this problem with any subclass that overrides a property method. Here, I try to override a getter but find that I still get the parent's view:
>>> class Foo:
... def __init__(self, val):
... self._val = val
... def _get_val(self):
... return self._val
... def _set_val(self, val):
... self._val = val
... val = property(_get_val, _set_val)
...
>>> class Bar(Foo):
... def _get_val(self):
... return self._val + 2
...
>>> print('want 3, got', Bar(1).val)
want 3, got 1
UPDATE 2
The problem is that property binds the class method when it is defined. If you don't mind implementing intermediate functions, then you can let them resolve the target method at runtime after the child has overriden them. Here, _get_stuff calls _get_stuff_impl at runtime and you get the child version.
from abc import ABCMeta,abstractmethod
class Mine(metaclass=ABCMeta):
def _get_stuff(self):
return self._get_stuff_impl()
#abstractmethod
def _get_stuff_impl(self):
raise NotImplementedError()
def _set_stuff(self, value):
return self._set_stuff_impl(value)
#abstractmethod
def _set_stuff_impl(self, value):
raise NotImplementedError()
settings = property(_get_stuff, _set_stuff)
def do_stuff_with_settings(self):
return settings
class Child(Mine):
def _get_stuff_impl(self):
return 1
def _set_stuff_impl(self):
pass
# settings = property(_get_stuff, _set_stuff)
Child().settings
I have a situation in which I want to enforce each and every class inheriting from a certain (abstract) class to implement a method. This is something I would normally achieve using #abstractmethod. However, considering this situation of multiple inheritance:
from abc import ABCMeta, abstractmethod
class A(object):
__metaclass__ = ABCMeta
#abstractmethod
def very_specific_method(self):
pass
class B(A):
def very_specific_method(self):
print 'doing something in B'
class C(B):
pass
I want to enforce C to implement the method as well. I want each and every class that inherits A either directly or indirectly to be forced to implement the method. Is this possible?
Clarification: I want this to apply for a specific method, not to all abstract methods. abstract methods should continue to work the same, but perhaps a new decorator signaling a different kind of methods should be created.
Side note: I used abc in the question because this seems like the most related to the issue. I understand how abstract methods usually work and use them regularly. This is a different situation, and I don't mind if it's not done via abc.
A modified version of ABCMeta should do the trick.
Here instead of checking for methods with __isabstractmethod__ set to True only in base classes we can check for this is in class's MRO, and if it is found in any of the class in MRO and it is not present in current class then we can add this to the set abstracts.
from abc import ABCMeta, abstractmethod
from _weakrefset import WeakSet
class EditedABCMeta(ABCMeta):
def __new__(mcls, name, bases, namespace):
cls = type.__new__(mcls, name, bases, namespace)
# Compute set of abstract method names
abstracts = set(name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False))
for base in cls.__mro__:
for name, value in base.__dict__.items():
if getattr(value, "__isabstractmethod__", False) and name not in cls.__dict__:
abstracts.add(name)
cls.__abstractmethods__ = frozenset(abstracts)
# Set up inheritance registry
cls._abc_registry = WeakSet()
cls._abc_cache = WeakSet()
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
return cls
class A(object):
__metaclass__ = EditedABCMeta
#abstractmethod
def veryspecificmethod(self):
pass
class B(A):
def veryspecificmethod(self):
print 'doing something in B'
#abstractmethod
def foo(self):
print 'foo from B'
class C(B):
def foo(self):
pass
class D(C, B):
pass
if __name__ == '__main__':
for cls in (C, D):
try:
cls().veryspecificmethod
except TypeError as e:
print e.message
print '-'*20
for cls in (C, D):
try:
cls().foo
except TypeError as e:
print e.message
Output:
Can't instantiate abstract class C with abstract methods veryspecificmethod
Can't instantiate abstract class D with abstract methods foo, veryspecificmethod
--------------------
Can't instantiate abstract class C with abstract methods veryspecificmethod
Can't instantiate abstract class D with abstract methods foo, veryspecificmethod
EDIT:
Adding a special decorator #enforcedmethod that can meet your requirements without affecting #abstractmethod:
from abc import ABCMeta, abstractmethod
def enforcedmethod(func):
func.__enforcedmethod__ = True
return func
class EditedABCMeta(ABCMeta):
def __call__(cls, *args, **kwargs):
enforcedmethods = set()
for base in cls.__mro__:
for name, value in base.__dict__.items():
if getattr(value, "__enforcedmethod__", False) and name not in cls.__dict__:
enforcedmethods.add(name)
if enforcedmethods:
raise TypeError("Can't instantiate abstract class {} "
"with enforced methods {}".format(
cls.__name__, ', '.join(enforcedmethods)))
else:
return super(EditedABCMeta, cls).__call__(*args, **kwargs)
class A(object):
__metaclass__ = EditedABCMeta
#enforcedmethod
def veryspecificmethod(self):
pass
#abstractmethod
def simplemethod(self):
pass
class B(A):
def veryspecificmethod(self):
print 'doing something in B'
def simplemethod(self):
pass
class C(B):
pass
class D(C):
def veryspecificmethod(self):
print 'doing something in D'
Output:
>>> D().veryspecificmethod()
doing something in D
>>> C().veryspecificmethod()
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
C().veryspecificmethod()
File "C:\Python27\so.py", line 19, in __call__
cls.__name__, ', '.join(enforcedmethods)))
TypeError: Can't instantiate abstract class C with enforced methods veryspecificmethod
I'm pretty sure that this isn't a great idea, but I think that you can do this. Checking out the ABCMeta implementation for inspiration:
from abc import ABCMeta
def always_override(func):
func._always_override = True
return func
class always_override_property(property):
_always_override = True
class CrazyABCMeta(ABCMeta):
def __new__(mcls, name, bases, namespace):
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
abstracts = set()
# first, get all abstracts from the base classes
for base in bases:
abstracts.update(getattr(base, "_all_always_override", set()))
all_abstracts = abstracts.copy()
# Now add abstracts from this class and remove abstracts that this class defines
for name, value in namespace.items():
always_override = getattr(value, '_always_override', False)
if always_override:
abstracts.add(name)
all_abstracts.add(name)
elif name in abstracts:
abstracts.remove(name)
cls._all_always_override = frozenset(all_abstracts)
cls._always_override = frozenset(abstracts)
return cls
def __call__(cls, *args, **kwargs):
if cls._always_override:
raise TypeError(
'The following methods/properties must '
'be overridden {}'.format(cls._all_always_override))
return super(CrazyABCMeta, cls).__call__(*args, **kwargs)
# # # # # # # # # # #
# TESTS!
# # # # # # # # # # #
class A(object):
__metaclass__ = CrazyABCMeta
#always_override
def foo(self):
pass
#always_override_property
def bar(self):
pass
class B(A):
def foo(self):
pass
bar = 1
class C(B):
pass
class D(C):
pass
class E(D):
def foo(self):
pass
#property
def bar(self):
return 6
for cls in (B, E):
cls()
print ("Pass {}".format(cls.__name__))
for cls in (C, D):
try:
print cls()
except TypeError:
print ("Pass {}".format(cls.__name__))
How can I make a class or method abstract in Python?
I tried redefining __new__() like so:
class F:
def __new__(cls):
raise Exception("Unable to create an instance of abstract class %s" %cls)
But now, if I create a class G that inherits from F like so:
class G(F):
pass
Then, I can't instantiate G either, since it calls its super class's __new__ method.
Is there a better way to define an abstract class?
Use the abc module to create abstract classes. Use the abstractmethod decorator to declare a method abstract, and declare a class abstract using one of three ways, depending upon your Python version.
In Python 3.4 and above, you can inherit from ABC. In earlier versions of Python, you need to specify your class's metaclass as ABCMeta. Specifying the metaclass has different syntax in Python 3 and Python 2. The three possibilities are shown below:
# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
#abstractmethod
def foo(self):
pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#abstractmethod
def foo(self):
pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
Whichever way you use, you won't be able to instantiate an abstract class that has abstract methods, but will be able to instantiate a subclass that provides concrete definitions of those methods:
>>> Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
... pass
...
>>> StillAbstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
... def foo(self):
... print('Hello, World')
...
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>
The old-school (pre-PEP 3119) way to do this is just to raise NotImplementedError in the abstract class when an abstract method is called.
class Abstract(object):
def foo(self):
raise NotImplementedError('subclasses must override foo()!')
class Derived(Abstract):
def foo(self):
print 'Hooray!'
>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]
This doesn't have the same nice properties as using the abc module does. You can still instantiate the abstract base class itself, and you won't find your mistake until you call the abstract method at runtime.
But if you're dealing with a small set of simple classes, maybe with just a few abstract methods, this approach is a little easier than trying to wade through the abc documentation.
Here's a very easy way without having to deal with the ABC module.
In the __init__ method of the class that you want to be an abstract class, you can check the "type" of self. If the type of self is the base class, then the caller is trying to instantiate the base class, so raise an exception. Here's a simple example:
class Base():
def __init__(self):
if type(self) is Base:
raise Exception('Base is an abstract class and cannot be instantiated directly')
# Any initialization code
print('In the __init__ method of the Base class')
class Sub(Base):
def __init__(self):
print('In the __init__ method of the Sub class before calling __init__ of the Base class')
super().__init__()
print('In the __init__ method of the Sub class after calling __init__ of the Base class')
subObj = Sub()
baseObj = Base()
When run, it produces:
In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__ method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
baseObj = Base()
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly
This shows that you can instantiate a subclass that inherits from a base class, but you cannot instantiate the base class directly.
Most Previous answers were correct but here is the answer and example for Python 3.7. Yes, you can create an abstract class and method. Just as a reminder sometimes a class should define a method which logically belongs to a class, but that class cannot specify how to implement the method. For example, in the below Parents and Babies classes they both eat but the implementation will be different for each because babies and parents eat a different kind of food and the number of times they eat is different. So, eat method subclasses overrides AbstractClass.eat.
from abc import ABC, abstractmethod
class AbstractClass(ABC):
def __init__(self, value):
self.value = value
super().__init__()
#abstractmethod
def eat(self):
pass
class Parents(AbstractClass):
def eat(self):
return "eat solid food "+ str(self.value) + " times each day"
class Babies(AbstractClass):
def eat(self):
return "Milk only "+ str(self.value) + " times or more each day"
food = 3
mom = Parents(food)
print("moms ----------")
print(mom.eat())
infant = Babies(food)
print("infants ----------")
print(infant.eat())
OUTPUT:
moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day
As explained in the other answers, yes you can use abstract classes in Python using the abc module. Below I give an actual example using abstract #classmethod, #property and #abstractmethod (using Python 3.6+). For me it is usually easier to start off with examples I can easily copy&paste; I hope this answer is also useful for others.
Let's first create a base class called Base:
from abc import ABC, abstractmethod
class Base(ABC):
#classmethod
#abstractmethod
def from_dict(cls, d):
pass
#property
#abstractmethod
def prop1(self):
pass
#property
#abstractmethod
def prop2(self):
pass
#prop2.setter
#abstractmethod
def prop2(self, val):
pass
#abstractmethod
def do_stuff(self):
pass
Our Base class will always have a from_dict classmethod, a property prop1 (which is read-only) and a property prop2 (which can also be set) as well as a function called do_stuff. Whatever class is now built based on Base will have to implement all of these four methods/properties. Please note that for a method to be abstract, two decorators are required - classmethod and abstract property.
Now we could create a class A like this:
class A(Base):
def __init__(self, name, val1, val2):
self.name = name
self.__val1 = val1
self._val2 = val2
#classmethod
def from_dict(cls, d):
name = d['name']
val1 = d['val1']
val2 = d['val2']
return cls(name, val1, val2)
#property
def prop1(self):
return self.__val1
#property
def prop2(self):
return self._val2
#prop2.setter
def prop2(self, value):
self._val2 = value
def do_stuff(self):
print('juhu!')
def i_am_not_abstract(self):
print('I can be customized')
All required methods/properties are implemented and we can - of course - also add additional functions that are not part of Base (here: i_am_not_abstract).
Now we can do:
a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})
a1.prop1
# prints 10
a1.prop2
# prints 'stuff'
As desired, we cannot set prop1:
a.prop1 = 100
will return
AttributeError: can't set attribute
Also our from_dict method works fine:
a2.prop1
# prints 20
If we now defined a second class B like this:
class B(Base):
def __init__(self, name):
self.name = name
#property
def prop1(self):
return self.name
and tried to instantiate an object like this:
b = B('iwillfail')
we will get an error
TypeError: Can't instantiate abstract class B with abstract methods
do_stuff, from_dict, prop2
listing all the things defined in Base which we did not implement in B.
This one will be working in python 3
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#abstractmethod
def foo(self):
pass
Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo
also this works and is simple:
class A_abstract(object):
def __init__(self):
# quite simple, old-school way.
if self.__class__.__name__ == "A_abstract":
raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")
class B(A_abstract):
pass
b = B()
# here an exception is raised:
a = A_abstract()
You can also harness the __new__ method to your advantage. You just forgot something.
The __new__ method always returns the new object so you must return its superclass' new method. Do as follows.
class F:
def __new__(cls):
if cls is F:
raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
return super().__new__(cls)
When using the new method, you have to return the object, not the None keyword. That's all you missed.
I find the accepted answer, and all the others strange, since they pass self to an abstract class. An abstract class is not instantiated so can't have a self.
So try this, it works.
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#staticmethod
#abstractmethod
def foo():
"""An abstract method. No need to write pass"""
class Derived(Abstract):
def foo(self):
print('Hooray!')
FOO = Derived()
FOO.foo()
from abc import ABCMeta, abstractmethod
#Abstract class and abstract method declaration
class Jungle(metaclass=ABCMeta):
#constructor with default values
def __init__(self, name="Unknown"):
self.visitorName = name
def welcomeMessage(self):
print("Hello %s , Welcome to the Jungle" % self.visitorName)
# abstract method is compulsory to defined in child-class
#abstractmethod
def scarySound(self):
pass
Late to answer here, but to answer the other question "How to make abstract methods" which points here, I offer the following.
# decorators.py
def abstract(f):
def _decorator(*_):
raise NotImplementedError(f"Method '{f.__name__}' is abstract")
return _decorator
# yourclass.py
class Vehicle:
def add_energy():
print("Energy added!")
#abstract
def get_make(): ...
#abstract
def get_model(): ...
The class base Vehicle class can still be instantiated for unit testing (unlike with ABC), and the Pythonic raising of an exception is present. Oh yes, you also get the method name that is abstract in the exception with this method for convenience.
You can create an abstract class by extending ABC which stands for "Abstract Base Classes" and can create the abstract method with #abstractmethod in the abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
And, to use an abstract class, it should be extended by a child class and the child class should override the abstract method of the abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
class Cat(Animal): # Extends "Animal" abstract class
def sound(self): # Overrides "sound()" abstract method
print("Meow!!")
obj = Cat()
obj.sound()
Output:
Meow!!
And, an abstract method can have code rather than pass and can be called by a child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
print("Wow!!") # Here
class Cat(Animal):
def sound(self):
super().sound() # Here
obj = Cat()
obj.sound()
Output:
Wow!!
And, an abstract class can have the variables and non-abstract methods which can be called by a child class and non-abstract methods don't need to be overridden by a child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
def __init__(self): # Here
self.name = "John" # Here
x = "Hello" # Here
def test1(self): # Here
print("Test1")
#classmethod # Here
def test2(cls):
print("Test2")
#staticmethod # Here
def test3():
print("Test3")
class Cat(Animal):
def sound(self):
print(self.name) # Here
print(super().x) # Here
super().test1() # Here
super().test2() # Here
super().test3() # Here
obj = Cat()
obj.sound()
Output:
John
Hello
Test1
Test2
Test3
And, you can define an abstract class and static methods and an abstract getter, setter and deleter in an abstract class as shown below. *#abstractmethod must be the innermost decorator otherwise error occurs and you can see my answer which explains more about an abstract getter, setter and deleter:
from abc import ABC, abstractmethod
class Person(ABC):
#classmethod
#abstractmethod # The innermost decorator
def test1(cls):
pass
#staticmethod
#abstractmethod # The innermost decorator
def test2():
pass
#property
#abstractmethod # The innermost decorator
def name(self):
pass
#name.setter
#abstractmethod # The innermost decorator
def name(self, name):
pass
#name.deleter
#abstractmethod # The innermost decorator
def name(self):
pass
Then, you need to override them in a child class as shown below:
class Student(Person):
def __init__(self, name):
self._name = name
#classmethod
def test1(cls): # Overrides abstract class method
print("Test1")
#staticmethod
def test2(): # Overrides abstract static method
print("Test2")
#property
def name(self): # Overrides abstract getter
return self._name
#name.setter
def name(self, name): # Overrides abstract setter
self._name = name
#name.deleter
def name(self): # Overrides abstract deleter
del self._name
Then, you can instantiate the child class and call them as shown below:
obj = Student("John") # Instantiates "Student" class
obj.test1() # Class method
obj.test2() # Static method
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))
Output:
Test1
Test2
John
Tom
False
And, if you try to instantiate an abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
obj = Animal()
The error below occurs:
TypeError: Can't instantiate abstract class Animal with abstract methods sound
And, if you don't override the abstract method of an abstract class in a child class and you instantiate the child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
class Cat(Animal):
pass # Doesn't override "sound()" abstract method
obj = Cat() # Here
The error below occurs:
TypeError: Can't instantiate abstract class Cat with abstract methods sound
And, if you define an abstract method in the non-abstract class which doesn't extend ABC, the abstract method is a normal instance method so there are no errors even if the non-abstract class is instantiated and even if a child class doesn't override the abstract method of the non-abstract class as shown below:
from abc import ABC, abstractmethod
class Animal: # Doesn't extend "ABC"
#abstractmethod # Here
def sound(self):
print("Wow!!")
class Cat(Animal):
pass # Doesn't override "sound()" abstract method
obj1 = Animal() # Here
obj1.sound()
obj2 = Cat() # Here
obj2.sound()
Output:
Wow!!
Wow!!
In addition, you can replace Cat class extending Animal class below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
# ↓↓↓ Here ↓↓↓
class Cat(Animal):
def sound(self):
print("Meow!!")
# ↑↑↑ Here ↑↑↑
print(issubclass(Cat, Animal))
With this code having register() below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
# ↓↓↓ Here ↓↓↓
class Cat:
def sound(self):
print("Meow!!")
Animal.register(Cat)
# ↑↑↑ Here ↑↑↑
print(issubclass(Cat, Animal))
Then, both of the code above outputs the same result below showing Cat class is the subclass of Animal class:
True
In your code snippet, you could also resolve this by providing an implementation for the __new__ method in the subclass, likewise:
def G(F):
def __new__(cls):
# do something here
But this is a hack and I advise you against it, unless you know what you are doing. For nearly all cases I advise you to use the abc module, that others before me have suggested.
Also when you create a new (base) class, make it subclass object, like this: class MyBaseClass(object):. I don't know if it is that much significant anymore, but it helps retain style consistency on your code
Just a quick addition to #TimGilbert's old-school answer...you can make your abstract base class's init() method throw an exception and that would prevent it from being instantiated, no?
>>> class Abstract(object):
... def __init__(self):
... raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class!