i am experimenting with implementation of the monte-carlo-tree-search algorithm for the travelling salesman problem. In this context, I have created a class that without going into details looks somewhat like these:
class Position:
def __init__(self, salesman, cities):
self.salesman= salesman
self.cities= cities
def unvisited_cities(self):
result = {}
for name, city in self.cities.items():
if city.not_visited():
result[name] = city
return result
def travel_to_city(self, city_name):
new_salesman = self.salesman.travel_to(city_name)
return Position(new_salesman, self.cities)
Now, I want to declare this class as a child class for an abstact class:
class AbstractGamePosition(metaclass=abc.ABCMeta):
@abc.abstractmethod
def possible_actions(self):
return
But here, I face a problem. Abstact class demands possible_actions method. In my concrete Position class, method that returns a set of all possible actions from is called unvisited_cities, because for the salesman problem only those cities can be the next targets that have not been visited yet. Is it possible to declare a Position as a child class of an AbstactGamePosition abstact class, and somehow let Python know that abstact method possible_actions is implemented in Position class by a concrete method unvisited_cities?
In naive language it could be something like this:
Position <- AbstactGamePosition:
AbstactGamePosition.possible_actions = Position.unvisited_cities
However, Python does not have such a construction. Can this be somehow solved?
Obviously, this can be solved through an interface class:
class Interface(AbstactGamePosition):
def __init__(self, position, concrete_method):
self.position = position
self._possible_actions = concrete_method
def possible_actions(self):
return self._possible_actions(self.position)
position = Position(salesman, cities)
interface = Interface(position, Position.unvisited_cities)
But this looks so sloppy that I find this disgusting.
CodePudding user response:
Given the information in the comments (that you don't want to have a hard dependency between Position
and AbstractGamePosition
), I think you're looking for the adapter pattern. Your Interface
class is pretty close, though I agree that that level of reflection is unwarranted in this situation.
I would recommend created a small class alongside Position
that is specifically for position
, and that class can delegate to the original.
# (Type annotations provided for clarity; they are optional)
class PositionAdapter(AbstractGamePosition):
def __init__(self, position: Position):
self.position = position
def possible_actions(self):
return self.position.unvisited_cities()
Then, when you want to use a Position
in a situation where an AbstractGamePosition
is expected, you simply write
my_abstract_game_position = PositionAdapter(my_original_position)
and you can call possible_actions
on my_abstract_game_position
.
You will have to write one of these adapter classes for each class you want adapted to this abstract parent without a hard dependency. But it's not a lot of boilerplate (five lines of code, basically), so it's worth it if your goal is to keep all of these classes as loosely coupled as possible.
CodePudding user response:
Just implement the method by its expected and required name. You can call you actual implementation internally:
class Position(AbstractGamePosition):
...
def unvisited_cities(self):
...
def possible_actions(self):
return self.unvisited_cities()
In fact, you can simply assign the unvisited_cities
method to the required name, which has the same outcome:
class Position(AbstractGamePosition):
...
def unvisited_cities(self):
...
possible_actions = unvisited_cities
CodePudding user response:
ABCMeta
literally only cares about the name being defined with an object that does not have an __isabtractmethod__
attribute set to True
: that's it.
You can patch Position
with
Position.possible_actions = Position.unvisited_cites
then register Position
as a subclass of the abstract base class.
AbstractGamePosition.register(Position)
(If fact, registering a virtual subclass doesn't even require the name to be properly defined; you can do so without defining Position.possible_actions
.)
(Assuming you can't change the definition of Position
to inherit from the abstract class. If you can, @deceze's answer has you covered.)