I know python likes to play it nice and loose with types, but sometimes you want a plugin type interface, and want to discover before production that someone has missed something. I found abcmeta - so did the following:
class Abstract_Base(metaclass=abc.ABCMeta):
#abc.abstractmethod
def a():
pass
#abc.abstractmethod
def b():
pass
class Inheritor_One(Abstract_Base):
def a():
pass
but when I do python -m flake8.... it has no problem with that. Is there any way of writing it such that someone not overriding an abstract method will go bang at linting time?
Pylint raises abstract-method for your example:
W0223: Method 'b' is abstract in class 'Abstract_Base' but is not overridden (abstract-method)
If you actually want Inheritor_One to be an abstract class you can disable the warning locally in this class and still have the warning when you use the abstract class later on:
import abc
class Abstract_Base(metaclass=abc.ABCMeta):
#abc.abstractmethod
def a(self):
pass
#abc.abstractmethod
def b(self):
pass
class Inheritor_One(Abstract_Base):
# pylint: disable=abstract-method
def a(self):
pass
Related
I have a class I would like to add typehints to that looks as following:
import yaml
class TestClass(dict):
#classmethod
def load(cls, fname) -> "TestClass":
return cls(yaml.safe_load(""))
#property
#abc.abstractmethod
def test(self):
raise
when I run mypy on a module containing only this class I get the following error message:
error: Cannot instantiate abstract class 'TestClass' with abstract attribute 'test'
From what I have understood from other posts this has something to do with when the 'test' method is instantiated when executing the load method. Is there a way to fix this issue using typehints alone or would I need to adjust my code here?
What mypy is telling you is that TestClass.load(...) will fail, because it will try to create an instance of the abstract class TestClass.
We can fix this by requiring that cls can be called with whatever yaml.safe_load returns (I'm assuming dict here), and returns an instance of TestClass:
from typing import Callable
import abc
import yaml
class TestClass(dict):
#classmethod
def load(cls: Callable[[dict], TestClass], fname) -> "TestClass":
return cls(yaml.safe_load(""))
#property
#abc.abstractmethod
def test(self):
raise
Note that TestClass.load("foo") will now also pass type checking. This is fair to some extent, because it's also fine at runtime, until you call test() on the created instance. I think this might be a limitation of how mypy implements protocols.
The background
In python, if you were defining an Abstract Base Class which requires that its methods be overwritten, you'd do:
from abc import ABC, abstractmethod
class MyAbstractClass(ABC):
#abstractmethod
def my_method(self):
pass
The following code would then fail because it doesn't implement my_method.
class MyConcreteClass(MyAbstractClass):
pass
But what if I want to define the method requirements of a mixin class?
class MyMixin:
def my_mixin_method(self):
self.a_required_method()
The following code is then valid:
class MyBase:
def a_required_method(self):
pass
class MyFull(MyMixin, MyBase):
pass
The following code is also valid...
class MyDubious(MyMixin):
pass
But exposes an error at runtime:
MyFull().my_mixin_method() # Works
MyDubious().my_mixin_method() # Runtime error
The Question
Is there something like AbstractBaseClass which can be added to Mixin classes, to ensure that a derived class can't be instantiated unless it inherits correctly?
I'm thinking a nice API would look like:
from asc import ASC, requiredmethod
class MyRobustMixin(ASC):
#requiredmethod
def a_required_method(self):
pass
def my_mixin_method(self):
self.a_required_method()
I'm currently refactoring features and I ended up with this abstractions
I have this classes
class AbstractClassA(SomeOtherAbstractClass):
#abstractmethod
def some_abstract_method(self):
pass
def my_method(self)):
service.some_method
class AbstractClassB(AbstractClassA):
#abstractmethod
def another_abstract_method(self):
pass
def some_abstract_method(self):
some_implementation
def my_method(self):
super().my_method()
do_any_other_stuff
And I need to test if the AbstractClassB.my_method calls super().my_method().
I've tried to test this by creating some ImplementationClass that inherits from AbstractClassB and then mocking the AbstractClassA.my_method and checking if it was called but it didn't work...
class AbstractClassBImplementation(AbstractClassB):
def some_abstract_method(self):
calls_service()
class TestAbstractClassB(TestCase):
#patch('module.submodule.AbstractClassA.my_method')
def test_class_b_calls_class_a_my_method(self, my_method_mock):
instance = AbstractClassBImplementation()
instance.my_method()
self.assertTrue(my_method_mock.called)
Someone know how to test this?
I have an Interface class which defines the requirements to an active "in-use" class:
class Portfolio(ABC):
#abstractmethod
def update_portfolio(self):
raise NotImplementedError
#abstractmethod
def update_from_fill(self):
raise NotImplementedError
#abstractmethod
def check_signal(self, signal_event):
raise NotImplementedError
The methods update_portfolio and update_from_fill are both methods which will be the same in 99% of the required cases. Only the check_signal method will vary. Therefore, to avoid having to write the same code again and again, I have defined a base class with default methods for update_portfolio and update_from_fill:
class BaseBacktestPortfolio(Portfolio):
def __init__(self, ...):
...
def update_portfolio(self, ...):
...
def update_from_fill(self, ...):
...
Then, finally, I have a class inheriting from the BacktestPortfolio class which specifies the correct implementation of the check_signal method:
class USBacktestPortfolio(BaseBacktestPortfolio):
def check_signal(self, ...):
...
Now, the problem is that my editor complains about the BacktestPortfolio classing not having all the required abstract methods. I could ignore this, of course, but the perfect scenario would be if I could make sure that it is not possible to instantiate an object form the BacktestPortfolio class.
Is this possible? And/or is there a more correct way to implement a structure like this?
I could ignore this, of course, but the perfect scenario would be if I could make sure that it is not possible to instantiate an object from the BacktestPortfolio class.
That is the case in your example already:
>>> BaseBacktestPortfolio.mro()
[__main__.BaseBacktestPortfolio, __main__.Portfolio, abc.ABC, object]
>>> BaseBacktestPortfolio()
TypeError: Can't instantiate abstract class BaseBacktestPortfolio with abstract methods check_signal
Since ABC and ABCMeta are just regular types, their features are inherited. This includes their guards against instantiating incomplete classes. Your BaseBacktestPortfolio already is an abstract class.
The warning from your IDE/linter/... exists specifically to warn you that instantiating BaseBacktestPortfolio is not possible.
You can make the BaseBacktestPortfolio also as Abstract class.
from abc import ABC, abstractmethod
class Portfolio(ABC):
#abstractmethod
def update_portfolio(self):
pass
#abstractmethod
def update_from_fill(self):
pass
#abstractmethod
def check_signal(self, signal_event):
pass
class BaseBacktestPortfolio(Portfolio, ABC):
def update_portfolio(self):
print("updated portfolio")
def update_from_fill(self):
print("update from fill")
#abstractmethod
def check_signal(self):
pass
class USBacktestPortfolio(BaseBacktestPortfolio):
def check_signal(self):
print("checked signal")
Also notice that you don't need raise NotImplementedError inside abstract method. You can just pass. Its more Pythonic :)
Say we have two classes, Class A with a custom error which is thrown frequently and it is part of its functionality.
#a.py
class AError(Exception):
"""This exception flags a functional error"""
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError() #This exception is raised eventually, business code removed for clarity
Class B, which uses class A to perform some operations.
#b.py
import a
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except a.AError:
pass #This was expected, here will go some business logic
This works great, but it becomes an issue when I have different types of A. Ideally, I'd like to fully decouple A from B so I can pass any class like A that satisfies the same interface, but I cannot due to the exception(as it is not part of the interface itself)
In C++ I would have a header file with the definition of my Interface plus the exceptions which the concrete classes will implement. How is this usually solved in Python? Or said another way, what is the most pythonic approach?
I thought the following options:
1. create module with exceptions and maybe a base class/metaclass (the C++/Java way)
#common.py
class AErrorBase(Exception):
pass
class AIface(object):
def work(self):
raise NotImplemented()
.
#a.py
import common
class AError(common.AErrorBase):
pass
class A(common.AIface):
def work(self):
"""Throws AError when it is tired"""
raise AError()
.
#b.py
import common
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except common.AErrorBase:
pass #This was expected
2. pass exception as an argument
#a.py
class AError(Exception):
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
.
#b.py
class B(object):
def make_him_work(self, afected, ex_type):
try:
afected.work()
except ex_type:
pass #This was expected
3. Exception as an attribute of the Class so it becomes part of the interface.
#a.py
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
class AError(Exception):
pass
.
#b.py
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except afected.AError:
pass #This was expected
4. Dont use exception, just a return code. !C days are back!
Any other option? What do you find more "pythonic"?
Edit: Added comments to clarify the purpose of the exception. It needs to be handled in B
Note: This might perfectly be that I am approaching the problem with my old C++ background, I just want to know how do you apply IoC in python when we have exceptions. Feel free to say all my approaches are garbage and I should do it in another way
My class would look like this:
class A(object):
def can_work(self):
"returns True if an A can work otherwise False (e.g. is an A is tired)"
return ...
def work(self):
assert not self._is_tired, "Test if self.can_work() first!"
...
This way you allow the users of A to test whether they should use work.
The assertion is useful for debugging and making sure you or others did not forget about the Interface.
Class B will use A as follows:
class B(object):
def make_him_work(self, afected):
if afected.can_work():
afected.work()