How to add hint to a factory method? - python

I'm looking for a way to annotate return type of a factory function.
It returns random child of 'AlgorithmBase'.
class AlgorithmFactory:
_algorithm_types = AlgorithmBase.__subclasses__()
def select_random_algorithm(self) -> AlgorithmBase:
# Select random algorithm
algorithm_class = self._random_generator.choice(AlgorithmFactory._algorithm_types)
algorithm = algorithm_class()
return algorithm
I get error from mypy:
The error I'm getting is:
Cannot instantiate abstract class 'AlgorithmBase' with abstract attributes 'get_constraints' and 'satisfy_constraints'
There is no way to instantiate class 'AlgorithmBase' in this code, how to make mypy understand it?
I want to avoid specifying actual sub-classes with 'Union' in return type. Any suggestions?

The problem here wasn't return type, but '_algorithm_types'. mypy has no way to understand what type it is, so it assumed that it is like return type and got error.
The following code fix the issue:
_algorithm_types: List[Type[AlgorithmBase]] = AlgorithmBase.__subclasses__()

As far as I can tell this should work, but it seems like one or more of your AlgorithmBase subclasses doesn't implement these two abstract methods.
Running MyPy for
import abc
class AlgorithmBase(abc.ABC):
#abc.abstractmethod
def get_constraints(self):
raise NotImplementedError
#abc.abstractmethod
def satisfy_constraints(self):
raise NotImplementedError
class SomeAlgorithm(AlgorithmBase):
pass
class AlgorithmFactory:
def get(self) -> AlgorithmBase:
algorithm = SomeAlgorithm()
return algorithm
yields the same error you get, and it runs without any error once the methods are implemented.
import abc
class AlgorithmBase(abc.ABC):
#abc.abstractmethod
def get_constraints(self):
raise NotImplementedError
#abc.abstractmethod
def satisfy_constraints(self):
raise NotImplementedError
class SomeAlgorithm(AlgorithmBase):
def get_constraints(self):
pass
def satisfy_constraints(self):
pass
class AlgorithmFactory:
def get(self) -> AlgorithmBase:
algorithm = SomeAlgorithm()
return algorithm

Related

OOP in Python beginner issues, finding all methods of the same starting name

I would like to create an abstract method in parent class which would be overridden in subclasses. This method would print all methods in the given subclass which start with 'on_'.
from abc import ABC, abstractmethod
class abstract_class(ABC):
#abstractmethod
def get_all_on_methods(self):
pass
class sub(abstract_class):
an_object = sub()
def get_all_on_methods(self):
for attribute in dir(self):
if attribute.startswith("on_"):
print(attribute)
def nothin(self):
print("nothin")
def on_fallback(self):
raise NotImplementedError()
def on_no(self):
raise NotImplementedError()
sub.get_all_on_methods()
I have two problems. First, I have:
Unresolved reference 'sub'
Second, I don't know whether my approach as actually all that good.

mypy error: Cannot instantiate abstract class with abstract attributes when adding types to an abstract method

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.

How to get all class methods

I have the following classes:
class DBManagerInterface:
#abstractmethod
def __init__(self, table_name): raise NotImplementedError
#abstractmethod
def create_table(self): raise NotImplementedError
#abstractmethod
def drop_table(self): raise NotImplementedError
class DataBaseManager(DBManagerInterface):
def __init__(self, table_name):
self.table_name = table_name
def drop_table(self):
None
def create_table(self):
None
class Example:
def __init__(self, db_manager):
self.db_manager = db_manager
def test(self):
self.db_manager.create_table() # can't see the db_manager methods
In Example class I'm getting the DataBaseManager pointer.
I wan't to be able to see all the DataBaseManager methods (without the need to search them manually in DataBaseManager.py file)
I'm using python 3.5.2 and pycharm editor
Is it possible ?
PyCharm has no idea what db_manager could be, so it can't give you edit hints.
If your environment supports it, annotate the argument and PyCharm can do type inference from there:
def __init__(self, db_manager: DBManagerInterface):
self.db_manager = db_manager
or if that's not supported, you can add an annotation in a docstring:
def __init__(self, db_manager):
"""
:type db_manager: DBManagerInterface
"""
self.db_manager = db_manager
The reason you are not able to see the methods of the DataBaseManager class in the attribute db_manager in Example class, is because there is no reason why the variable db_manager in the __init__ method is supposed to be an instance of DataBaseManager.
You can either specify the type directly: https://docs.python.org/3/library/typing.html
Or you can check the instance type in the __init__ method:
class Example:
def __init__(self, db_manager):
if not isinstance(db_manager, DataBaseManager):
raise ValueError
self.db_manager = db_manager
Pycharm will afterwards understand the type of the attribute and show you all the possible methods for the object.
Try
print(dir(DataBaseManager))
From the docs:
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
dir([object])

Proper way to implement ABC SubClass

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 :)

Raise exception while defining a class improperly

How can I write a mixin, which raises an Exception if the class which is using this specific mixin is not created properly.
If I do these checks and balances in the __init__ or __new__ methods of the mixin, Exception is raised when this erroneous class tries to create an instance. Which is late, ideally the exception needs to be thrown when the compiler detects a wrong class. (Assuming, how to detect if a class is acceptable or not is a trivial matter)
To Illustrate the question
class ASampleMixin:
"""
A sample docstring
"""
def a_method(self):
raise NotImplementedError
def class_rule(self):
if something is wrong:
return False
return True
# more methods
class AClass(ASampleMixin, BaseClass):
"""
This class should satisfy a condition specified in class_rule method of the mixin
"""
# some methods
I am right now performing the check in the init method of mixin. Which raises an exception if rule returns False. Now this needs to be done at the time AClass is read by interpreter and not when I try to create an instance of AClass.
Is it possible even in dynamically typed languages like Python 3.5?
This sounds as if you want to create a custom metaclass that performs the check upon creation of the class object. See the documentation for metaclasses.
A metaclass example as reference:
class CustomType(type):
def __call__(cls, *args, **kwargs):
if not CustomType.some_rule(kwargs.pop('some_attr', None)):
raise Exception('Abort! Abort!')
return super(CustomType, cls).__call__(*args, **kwargs)
#staticmethod
def some_rule(var):
if type(var) is not str:
return False
return True
class A(object):
__metaclass__ = CustomType
class B(A):
pass
b = B(some_attr='f') # all is well
b = B() # raises

Categories

Resources