I have an abstract class with a static function that calls other abstract functions. But when I'm creating a new class and overriding abstract function still the original (abstract) function is running.
I have written an example similar to my problem. Please help.
In the following example, I want to run do_something() from Main not Base.
from abc import ABC, abstractmethod
class Base(ABC):
#staticmethod
#abstractmethod
def do_something():
print('Base')
#staticmethod
def print_something():
Base.do_something()
class Main(Base):
#staticmethod
def do_something():
print('Main')
Main.print_something()
Output:
Base
Main.print_something doesn't exist, so it resolves to Base.print_something, which explicitly calls Base.do_something, not Main.do_something. You probably want print_something to be a class method instead.
class Base(ABC):
#staticmethod
#abstractmethod
def do_something():
print('Base')
#classmethod
def print_something(cls):
cls.do_something()
class Main(Base):
#staticmethod
def do_something():
print('Main')
Main.print_something()
Now when Main.print_something resolves to Base.print_something, it will still receive Main (not Base) as its argument, allowing it to invoke Main.do_something as desired.
Related
I have an abstract base class Base that provides an abstract method _run() that needs to be implemented by derived classes, as well as a method run() that will call _run() and do some extra work that is common to all derived classes.
In all derived classes, I am setting the function docstring for the _run() method. As this function is not part of the public API, I want the same docstring (and function signature) to instead show up for the run() method.
Consider the following example:
import inspect
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def _run(self):
return
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""new_doc"""
return
My initial idea was to manipulate the docstring in Base.__init__ or Base.__new__. This works to some extent, but presents a number of problems:
I want to be able to override these two methods (at the very least __init__) in derived classes.
This requires the class to be instantiated before the docstring is available.
By setting the docstring for Base.run when instantiating the derived class, it would in fact set the docstring for all derived classes.
class Base(ABC):
def __init__(self):
type(self).run.__doc__ = type(self)._run.__doc__
type(self).run.__signature__ = inspect.signature(type(self)._run)
...
What I am hoping for:
>>> Derived.run.__doc__
'new_doc'
What I get so far:
>>> Derived.run.__doc__
'old_doc'
>>> Derived().run.__doc__
'new_doc'
Are there any solutions to this?
Don't modify the docstring of Base.run; instead, document what it does: it invokes a subclass-defined method.
class Base(ABC):
#abstractmethod
def _run(self):
"Must be replaced with actual code"
return
def run(self, *args, **kwargs):
"""Does some setup and runs self._run"""
return self._run(*args, **kwargs)
class Derived(Base):
def _run(self):
"""Does some work"""
return
There is no need to generate a new docstring for Derived.run, because Derived.run and Base.run evaluate to the exact same object: the run method defined by Base. Inheritance doesn't change what Base.run does just because it is invoked from an instance of Derived rather than an instance of Base.
The best workaround I have come up with is to create a decorator instead:
from abc import ABC, abstractmethod
class Base(ABC):
#abstractmethod
def run(self, *args, **kwargs):
"""old_doc"""
return self._run(*args, **kwargs)
def extra_work(func):
# Do some extra work and modify func.__doc__
...
return func
class Derived(Base):
#extra_work
def run(self):
"""new_doc"""
return
This way the extra work can still be defined outside the derived class to avoid duplicating it in every class derived from Base, and I am able to automatically update the docstring to reflect the added functionality.
I was making parser stuff, and I want to make some "typing" in my code for type hinting. But, the classes of a file has been referenced and inherited each other. So, my code is now spaghetti :(
I've tried tons of numbers of cases that I can make. I tried everything in one class, and I changed the order. But, nothing has changed. Still, got errors kinda 'Unresolved reference'
from abc import ABC, abstractmethod
class Expression(ABC):
class Visitor(ABC):
#abstractmethod
def visit_assign(self, expr: Assign):
pass
#abstractmethod
def accept(self, visitor: Visitor):
pass
class Assign(Expression):
def accept(self, visitor: Visitor):
# ...
Assign has been declared later than Visitor class. So, it occurs "Unresolved Reference" error.
There are two ways to do this.
The first is to run from __future__ import annotations, which defers annotation parsing to runtime, allowing your code to run unmodified.
The second is to change your type annotations to strings, which has practically the same effect:
from abc import ABC, abstractmethod
class Expression(ABC):
class Visitor(ABC):
#abstractmethod
def visit_assign(self, expr: 'Assign'):
pass
#abstractmethod
def accept(self, visitor: 'Visitor'): # not needed; for consistency only
pass
class Assign(Expression):
def accept(self, visitor: 'Visitor'):
pass
A solution for cross-references issues can be to declare what you can first and add the rest later.
So you could have something like:
from abc, import ABC, abstractmethod
class Expression(ABC):
class Visitor(ABC):
pass
class Assign(Expression):
def accept(self, visitor: Visitor):
# ...
#abstractmethod
def visit_assign(self, expr: Assign):
pass
#abstractmethod
def accept(self, visitor: Visitor):
pass
Expression.Visitor.visit_assign = visit_assign
Expression.accept = accept
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')
I'd like to designate some of my baseclass functions for override. (The override should not be necessarily enforced.) What is the proper way of doing this?
I mean sth like
class Base(object):
...
#abstractmethod
def fun(self):
pass
class Derived(Base):
#override
def fun(self):
pass
EDIT:
I want my code to raise an UnimplementedException if the function is not overridden.
class Base(object):
...
def fun(self):
raise NotImplementedError
Source: http://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods
I have the following kind of superclass / subclass setup:
class SuperClass(object):
def __init__(self):
self.do_something() # requires the do_something method always be called
def do_something(self):
raise NotImplementedError
class SubClass(SuperClass):
def __init__(self):
super(SuperClass, self).__init__() # this should do_something
def do_something(self):
print "hello"
I would like the SuperClass init to always call a not-yet-implemented do_something method. I'm using python 2.7. Perhaps ABC can do this, but it is there another way?
Thanks.
Your code is mostly correct, except for the use of super. You need to put the current class name in the super call, so it would be:
super(SubClass, self).__init__()
Since you put in the wrong class name, SuperClass.__init__ wasn't called, and as a result do_something wasn't called either.