Can't instantiate abstract class with abstract methods - python

I'm working on a kind of lib, and I'm getting an error.
Here is my code. Of course #abc.abstractmethod have to be uncommented
Here are my tests
Sorry couldn't just copy and paste it
I went on the basis that the code below works.
test.py:
import abc
import six
#six.add_metaclass(abc.ABCMeta)
class Base(object):
#abc.abstractmethod
def whatever(self,):
raise NotImplementedError
class SubClass(Base):
def __init__(self,):
super(Base, self).__init__()
self.whatever()
def whatever(self,):
print("whatever")
In the python shell:
>>> from test import *
>>> s = SubClass()
whatever
For my roster module, why am I getting this error:
Can't instantiate abstract class Player with abstract methods _Base__json_builder, _Base__xml_builder

Your issue comes because you have defined the abstract methods in your base abstract class with __ (double underscore) prepended. This causes python to do name mangling at the time of definition of the classes.
The names of the function change from __json_builder to _Base__json_builder or __xml_builder to _Base__xml_builder . And this is the name you have to implement/overwrite in your subclass.
To show this behavior in your example -
>>> import abc
>>> import six
>>> #six.add_metaclass(abc.ABCMeta)
... class Base(object):
... #abc.abstractmethod
... def __whatever(self):
... raise NotImplementedError
...
>>> class SubClass(Base):
... def __init__(self):
... super(Base, self).__init__()
... self.__whatever()
... def __whatever(self):
... print("whatever")
...
>>> a = SubClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class SubClass with abstract methods _Base__whatever
When I change the implementation to the following, it works
>>> class SubClass(Base):
... def __init__(self):
... super(Base, self).__init__()
... self._Base__whatever()
... def _Base__whatever(self):
... print("whatever")
...
>>> a = SubClass()
whatever
But this is very tedious , you may want to think about if you really want to define your functions with __ (double underscore) . You can read more about name mangling here .

I got the same error below:
TypeError: Can't instantiate abstract class Animal with abstract methods sound
When I tried to instantiate Animal abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
print("Wow!!")
obj = Animal() # Here
obj.sound()
And also, I got the same error below:
TypeError: Can't instantiate abstract class Cat with abstract methods sound
When I didn't override sound() abstract method in Cat class, then I instantiated Cat class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
print("Wow!!")
class Cat(Animal):
pass # I didn't override "sound()" abstract method
obj = Cat() # Here
obj.sound()
So, I overrided sound() abstract method in Cat class, then I instantiated Cat class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
print("Wow!!")
# I overrided "sound()" abstract method
class Cat(Animal):
def sound(self):
print("Meow!!")
obj = Cat() # Here
obj.sound()
Then, I could solve the error:
Meow!!

Easiest way to make it behave like it did in Python 2 is to use
exec('print("your code")', globals())
This will allow your code to access imports, classes and functions defined in the code will work correctly, etc.
This should only be done w/ code you trust.

Related

Python and Mixins with Super

I have the following class
from somewhere import ParentClass
from mixin_file import Mixin1 and Mixin2
class Test(Mixin1, Mixin2, ParentClass):
def __init__(self):
super(Test, self).__init__()
in mixin_file
from abc import ABCMeta, abstractmethod
class Mixin1(object):
__metaclass__ = ABCMeta
#abstractmethod
def do_something(self):
pass
class Mixin2(object):
def hello(self):
pass
and when I run
x = Test()
the error
TypeError:__init__() takes exactly 0 arguments (2 given)
Do I need to pass the Mixin whens I initialize 'x'? I'm not quite clear on this behavior.
Order of operations ended up being the issue here. I needed to add the mixins after so
class Test(Mixin1, Mixin2, ParentClass):
goes too
class Test(ParentClass, Mixin1, Mixin2):

Python: enforcing abstract method

I have written the follow code to demonstrate abstract methods that must be implemented by its subclasses. I read that when a method in a parent class is decorated as abstract, its subclass must implement it otherwise it cannot be instantiated. However, in the following code, the 'Slug' subclass does not implement the abstract method, yet it can still instantiate without errors being thrown. I thought in this case Python will complain?
Have I misunderstood something?
Thanks
import abc
class Animal(object):
__metaclass__ = abc.ABCMeta
def __init__(self, species):
self._species=species
def get_species(self):
return self._species
#abc.abstractmethod
def eat(self):
pass
class Slug(Animal):
def __init(self, species):
super(species)
def run(self):
print("running")
# def eat(self):
# pass
sl = Slug("slug")
print(sl.get_species())
no, you understood perfectly well! it is just that the python 3 syntax for abc.ABCMeta is:
class Animal(metaclass=abc.ABCMeta):
...
instead of __metaclass__ = abc.ABCMeta which was used in python 2. alternatively you can just inherit from ABC: class Animal(abc.ABC):. see doc.
and then:
class Slug(Animal):
def __init(self, species):
super.__init__(species)
...
this will result in
Traceback (most recent call last):
File "/home/.../...py", line 33, in <module>
sl = Slug("slug")
TypeError: Can't instantiate abstract class Slug with abstract methods eat

Why doesn't the abc.ABCMeta abstract instantiation check work on derivatives of `list` and `dict`?

I have been experimenting a little with the abc module in python. A la
>>> import abc
In the normal case you expect your ABC class to not be instantiated if it contains an unimplemented abstractmethod. You know like as follows:
>>> class MyClass(metaclass=abc.ABCMeta):
... #abc.abstractmethod
... def mymethod(self):
... return -1
...
>>> MyClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods mymethod
OR for any derived Class. It all seems to work fine until you inherit from something ... say dict or list as in the following:
>>> class YourClass(list, metaclass=abc.ABCMeta):
... #abc.abstractmethod
... def yourmethod(self):
... return -1
...
>>> YourClass()
[]
This is surprising because type is probably the primary factory or metaclass -ish thing anyway or so I assume from the following.
>>> type(abc.ABCMeta)
<class 'type'>
>>> type(list)
<class 'type'>
From some investigation I found out that it boils down to something as simple as adding an __abstractmethod__ attribute to the class' object and rest happens by itself:
>>> class AbstractClass:
... pass
...
>>> AbstractClass.__abstractmethods__ = {'abstractmethod'}
>>> AbstractClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class AbstractClass with abstract methods abstractmethod
So one can simply avoid the check by intentionally overriding the __new__ method and clearing out __abstractmethods__ as in below:
>>> class SupposedlyAbstractClass(metaclass=abc.ABCMeta):
... def __new__(cls):
... cls.__abstractmethods__ = {}
... return super(AbstractClass, cls).__new__(cls)
... #abc.abstractmethod
... def abstractmethod(self):
... return -1
...
>>> SupposedlyAbstractClass()
<__main__.SupposedlyAbstractClass object at 0x000001FA6BF05828>
This behaviour is the same in Python 2.7 and in Python 3.7 as I have personally checked. I am not aware if this is the same for all other python implementations.
Finally, down to the question ... Why has this been made to behave like so? Is it wise we should never make abstract classes out of list, tuple or dict? or should I just go ahead and add a __new__ class method checking for __abstractmethods__ before instantiation?
The problem
If you have the next class:
from abc import ABC, abstractmethod
class Foo(list, ABC):
#abstractmethod
def yourmethod(self):
pass
the problem is that and object of Foo can be created without any error because Foo.__new__(Foo) delegates the call directly to list.__new__(Foo) instead of ABC.__new__(Foo) (which is responsible of checking that all abstract methods are implemented in the class that is going to be instantiated)
We could implement __new__ on Foo and try to call ABC.__new__:
class Foo(list, ABC):
def __new__(cls, *args, **kwargs):
return ABC.__new__(cls)
#abstractmethod
def yourmethod(self):
pass
Foo()
But he next error is raised:
TypeError: object.__new__(Foo) is not safe, use list.__new__()
This is due to ABC.__new__(Foo) invokes object.__new__(Foo) which seems that is not allowed when Foo inherits from list
A possible solution
You can add additional code on Foo.__new__ in order to check that all abstract methods in the class to be instantiated are implemented (basically do the job of ABC.__new__).
Something like this:
class Foo(list, ABC):
def __new__(cls, *args, **kwargs):
if hasattr(cls, '__abstractmethods__') and len(cls.__abstractmethods__) > 0:
raise TypeError(f"Can't instantiate abstract class {cls.__name__} with abstract methods {', '.join(cls.__abstractmethods__)}")
return super(Foo, cls).__new__(cls)
#abstractmethod
def yourmethod(self):
return -1
Now Foo() raises an error. But the next code runs without any issue:
class Bar(Foo):
def yourmethod(self):
pass
Bar()

TypeError: Can't instantiate abstract class <...> with abstract methods

Here is my code:
from abc import ABC
from abc import abstractmethod
class Mamifiero(ABC):
"""docstring for Mamifiero"""
def __init__(self):
self.alimentacion = 'carnivoro'
#abstractmethod
def __respirar(self):
print('inhalar... exhalar')
class Perro(Mamifiero):
"""docstring for Perro"""
def __init__(self, ojos=2,):
self.ojos = ojos
I want that perro.respirar() prints 'inhalar... exhalar' but when I want to instantiate a Perro class show me this error. I want to know what is wrong with my script
By definition (read the docs), an abstract call is a class which CANNOT be instantiated until it has any abstract methods not overridden. So as in the Object-Oriented Programming by design.
You have an abstract method Perro.__respirar() not overridden, as inherited from the parent class. Or, override it with a method Perro.__respirar(), and do something there (maybe even call the parent's method; but not in case it is private with double-underscore, of course).
If you want to instantiate Perro, just do not make that method abstract. Make it normal. Because it also has some implementation, which suggests it is a normal base-class'es method, not an abstract method.
You need to override __respirar() abstract method in Perro class as shown below:
from abc import ABC
from abc import abstractmethod
class Mamifiero(ABC):
"""docstring for Mamifiero"""
def __init__(self):
self.alimentacion = 'carnivoro'
#abstractmethod
def __respirar(self):
print('inhalar... exhalar')
class Perro(Mamifiero):
"""docstring for Perro"""
def __init__(self, ojos=2,):
self.ojos = ojos
# You need to override
def __respirar(self):
print('Hello')

No error while instantiating abstract class, even though abstract method is not implemented

I was trying out the below python code:
from abc import ABCMeta, abstractmethod
class Bar:
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
class Bar2(Bar):
def foo2(self):
print("Foo2")
b = Bar()
b2 = Bar2()
I thought having #abstractmethod will ensure that my parent class will be abstract and the child class would also be abstract as it is not implementing the abstract method. But here, I get no error trying to instantiate both the classes.
Can anyone explain why?
You must set meta-class of Bar class to ABCMeta.
Python 2:
class Bar:
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
Python 3:
class Bar(object, metaclass=ABCMeta):
#abstractmethod
def foo(self):
pass

Categories