Python 3 dynamically assign base class - python

I am writing a simple chess program to practice my OOP in python 3 and was wondering how to dynamically change (before class creation) the base class for a class definition. My class structure is this.
abstract Piece class -> various derived pieces
Board class, has a composite of derived Pieces, and 8x8 matrix, and some methods
abstract Interface class -> CLI or
abstract Interface class -> GUI (also subclassing Tkinter)
Game class (for processing the game logic and main loop), which currently has a Board class member.
I initially implemented the Game class as having an interface data member that is defined during init but I'm finding myself sending a lot of the other internal Game data to the Interface composite member. I feel it would be more elegant to have the Game class be a subclass of either Interface subclass so the it could access their methods directly (and make them abstract).
However I want a version of the Game class that can do this dynamically so that I don't have to code it twice or inherit from both and make runtime decisions on which base class to use. I've currently done this by nesting the Game class inside a function like so.
def Game(ui):
class Game(ui):
...
return Game()
The crummy naming is part of the reason I don't like this solution. I want to be able to call the Game class on its own without explicitly using or acknowledging that I'm doing anything out of the ordinary.
Is there a way to do this with a metaclass or a class decorator? I have only been able to get them to affect class attributes, not the parent classes.

The class statement is "syntactic sugar" for
type(name, bases, dict)
You can create such a dynamic class using type like this
>>> class ui():
... def start(self): print("Started!")
...
>>> Game = type("Game", (ui,), {})
>>> game = Game()
>>> game.start()
Started!

You could use a very simple metclass here, but that's overkill. You can just swap out which class you're using as the base class based on whatever condition you want:
>>> class Foo: pass
...
>>> class Bar: pass
...
>>> x = 3
>>> class Game(Foo if x < 3 else Bar):pass
...
>>> Game.__bases__
(<class '__main__.Bar'>,)
Note that this isn't really any different than the formalism you have. However, if I was to use your code, I wouldn't create the Game class and the instance all in the function. I would do something like:
def Game_Factory(base):
class Game(base):
...
return Game
Game1 = Game_Factory(base1)
Game2 = Game_Factory(base2)
game1_instance = Game1()
game2_instance = Game2()
This gives you much easier access to the Game class (rather than needing to inspect an instance to get it).

Related

Can I implement an abstact method by a concrete method with a different name?

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

Extending parent/children classes in python

Is it possible in python for a nested class to extend its parent?
Like this:
class Parent:
class Child(Parent):
pass
child = Parent.Child()
Is it possible to do this in the opposite direction?
Like this:
class Parent(Child):
class Child:
pass
parent = Parent()
From what I know this is not possible, even with from __future__ import annotations.
The best known way around this is just not to make nested classes.
Important:
The purpose of this question is to make it clear if this is even possible in the python language.
There is no "final goal", objectives to be accomplished with this approach or justification for it.
Don't spam in the comments/answers about "how bad this code is".
No and Yes.
No, because when you inherit from a class, that class must be defined before you can inherit from it. Neither of your code examples will work due to this.
Yes, because Python is a dynamic language and you can change (monkey-patch) the base classes even after defining them:
class Temp:
pass
# example 1
class Parent:
class Child(Temp):
pass
Parent.Child.__bases__ = (Parent,)
# example 2
class Parent(Temp):
class Child:
pass
Parent.__bases__ = (Parent.Child,)
Why use the Temp class?
Classes automatically inherit from object. Due to a bug (https://bugs.python.org/issue672115), we cannot change __bases__ if a class inherits from object. Hence, we inherit from a temporary (Temp) class to avoid that issue.

Python - Child Class to call a function from another Child Class

I have a pretty big class that i want to break down in smaller classes that each handle a single part of the whole. So each child takes care of only one aspect of the whole.
Each of these child classes still need to communicate with one another.
For example Data Access creates a dictionary that Plotting Controller needs to have access to.
And then plotting Controller needs to update stuff on Main GUI Controller. But these children have various more inter-communication functions.
How do I achieve this?
I've read Metaclasses, Cooperative Multiple Inheritence and Wonders of Cooperative Multiple Inheritence, but i cannot figure out how to do this.
The closest I've come is the following code:
class A:
def __init__(self):
self.myself = 'ClassA'
def method_ONE_from_class_A(self, caller):
print(f"I am method ONE from {self.myself} called by {caller}")
self.method_ONE_from_class_B(self.myself)
def method_TWO_from_class_A(self, caller):
print(f"I am method TWO from {self.myself} called by {caller}")
self.method_TWO_from_class_B(self.myself)
class B:
def __init__(self):
self.me = 'ClassB'
def method_ONE_from_class_B(self, caller):
print(f"I am method ONE from {self.me} called by {caller}")
self.method_TWO_from_class_A(self.me)
def method_TWO_from_class_B(self, caller):
print(f"I am method TWO from {self.me} called by {caller}")
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
def children_start_talking(self):
self.method_ONE_from_class_A('Big Poppa')
poppa = C()
poppa.children_start_talking()
which results correctly in:
I am method ONE from ClassA called by Big Poppa
I am method ONE from ClassB called by ClassA
I am method TWO from ClassA called by ClassB
I am method TWO from ClassB called by ClassA
But... even though Class B and Class A correctly call the other children's functions, they don't actually find their declaration. Nor do i "see" them when i'm typing the code, which is both frustrating and worrisome that i might be doing something wrong.
Is there a good way to achieve this? Or is it an actually bad idea?
EDIT: Python 3.7 if it makes any difference.
Inheritance
When breaking a class hierarchy like this, the individual "partial" classes, we call "mixins", will "see" only what is declared directly on them, and on their base-classes. In your example, when writing class A, it does not know anything about class B - you as the author, can know that methods from class B will be present, because methods from class A will only be called from class C, that inherits both.
Your programming tools, the IDE including, can't know that. (That you should know better than your programming aid, is a side track). It would work, if run, but this is a poor design.
If all methods are to be present directly on a single instance of your final class, all of them have to be "present" in a super-class for them all - you can even write independent subclasses in different files, and then a single subclass that will inherit all of them:
from abc import abstractmethod, ABC
class Base(ABC):
#abstractmethod
def method_A_1(self):
pass
#abstractmethod
def method_A_2(self):
pass
#abstractmethod
def method_B_1(self):
pass
class A(Base):
def __init__(self, *args, **kwargs):
# pop consumed named parameters from "kwargs"
...
super().__init__(*args, **kwargs)
# This call ensures all __init__ in bases are called
# because Python linearize the base classes on multiple inheritance
def method_A_1(self):
...
def method_A_2(self):
...
class B(Base):
def __init__(self, *args, **kwargs):
# pop consumed named parameters from "kwargs"
...
super().__init__(*args, **kwargs)
# This call ensures all __init__ in bases are called
# because Python linearize the base classes on multiple inheritance
def method_B_1(self):
...
...
class C(A, B):
pass
(The "ABC" and "abstractmethod" are a bit of sugar - they will work, but this design would work without any of that - thought their presence help whoever is looking at your code to figure out what is going on, and will raise an earlier runtime error if you per mistake create an instance of one of the incomplete base classes)
Composite
This works, but if your methods are actually for wildly different domains, instead
of multiple inheritance, you should try using the "composite design pattern".
No need for multiple inheritance if it does not arise naturally.
In this case, you instantiate objects of the classes that drive the different domains on the __init__ of the shell class, and pass its own instance to those child, which will keep a reference to it (in a self.parent attribute, for example). Chances are your IDE still won't know what you are talking about, but you will have a saner design.
class Parent:
def __init__(self):
self.a_domain = A(self)
self.b_domain = B(self)
class A:
def __init__(self, parent):
self.parent = parent
# no need to call any "super...init", this is called
# as part of the initialization of the parent class
def method_A_1(self):
...
def method_A_2(self):
...
class B:
def __init__(self, parent):
self.parent = parent
def method_B_1(self):
# need result from 'A' domain:
a_value = self.parent.a_domain.method_A_1()
...
This example uses the basic of the language features, but if you decide
to go for it in a complex application, you can sophisticate it - there are
interface patterns, that could allow you to swap the classes used
for different domains, in specialized subclasses, and so on. But typically
the pattern above is what you would need.

Python metaclass (abc module) inheritance with nested classes

I've written a Python 3 metaclass containing a nested metaclass (with abc), like:
class A_M(object, metaclass=abc.ABCMeta):
class A_nested_M(object, metaclass=abc.ABCMeta):
def ... # some methods
Now, implementing like
class A(A_M):
class A_nested(A_nested_M):
def ...
doesn't work. So, did i miss something about usage of metaclasses or is this type of implementation with nested metaclasses not working at all?
First thing:
Nesting class declarations is of near no use for anything in Python. Unless you are using the nested class hierarchy itself as a hard-coded namespace to keep attributes, you probably are doing the wrong thing already.
You did not tell what your (actual) problem is and what you are trying to achieve there, nor why you are using the ABCmeta metaclass. So it is hard to suggest any actually useful answers - but we can try clarifying some things:
First: you are not writting a metaclass, as you suggest in the text "I've written a Python 3 metaclass containing a nested metaclass..." - you are creating ordinary classes that have the ABCmeta as its metaclass. But you are not creating new metaclasses - You would if you were inheriting from type or from ABCMeta itself - them your new class would be used in the metaclass= parameter of subsequent (ordinary) classes. That is not the case.
Now, second, everything that is defined inside the body of your outermost A_M class will be only "visible" as attributes of A_M itself. That is the source of your error - when you try to inherit from A_nested_M you should actually write:
class A_M(object, metaclass=abc.ABCMeta):
class A_nested_M(object, metaclass=abc.ABCMeta):
def ... # some methods
class A(A_M):
class A_nested(A_M.A_nested_M):
def ...
See - A_M.A_nested_M will make Python find the superclass for A_nested: there is no reference in the local or global namespaces for A_nested_M as it only exists as an attribute of A_M outside the body of the class A_M... statement.
That said, this is still useless. If you want to have instances of A_nested referenced by instances of A class, you have to create these instances inside A.__init__() call - at which point it makes no difference if A_nested is declared inside a class body or at the module level:
class A_M(object, metaclass=abc.ABCMeta):
pass
class A_nested_M(object, metaclass=abc.ABCMeta):
def ... # some methods
class A_nested(A_nested_M):
...
class A(A_M):
def __init__(self):
self.nested = A_nested()
Now, that can be of some use. You can also declare the classes actually nested, but the only way they can be useful is by creating instances of them anyway. And unlike nested functions, nested classes do not have access to attributes or variables declared on the "nesting" class namespace (but for referring to them by their qualified name. I.e. in your example, if the A class would contain a b classmethod, a method inside A_nested that would call this method would have to call A.b(), not b())
You should implement your class like this:
class A(A_M):
class A_nested(A_M.A_nested_M):
def ...
Because A_nested_M is an inner class, you should access it just like you would access any of the class attributes, i.e. A_M.A_nested_M. See this link.

Python inheritance: convert from Base class to Derived class

I'm still pretty new to Python, so bear with me. Here is my problem:
I have a base class, lets call it a sports game:
class Game:
def __init__(self):
self.home_team = None
self.away_team = None
and I have multiple derived classes for each sport, but lets use baseball as an example:
class BaseballGame(Game):
def __init__(self):
self.home_pitcher = None
self.away_pitcher = None
So far everything is good. However I have another utility function in a separate python module which will generate and populate a list of all the games being played on a given day for that sport.
def fetch_game_data:
games = []
games_found_online = code_that_fetches_online_games
for online_game in games_found_online:
new_game = Game()
new_game.home_team = ...
new_game.away_team = ...
games.append(new_game)
return games
Its obviously much more complicated than this with a lot of parsing using BeautifulSoup, but you get the point. My problem is that this function returns a list of the Base class, but I need a list of the Derived class. The derived classes will be the ones calling this function to populate a list and operate on it. The way I see it, I have two options:
I could implement a dreaded circular dependency and have the fetch_game_data function know about all of the derived classes and call the derived class constructors instead of the base class constructors. The derived classes already need to import the fetch_data module, but now the fetch_data module will have to import all of the derived classes to know about their constructors. And what makes it worse is that the fetch_data module won't have any need to touch any of the derived class fields - it only populates base class fields. The circular dependency is JUST so I can create the objects.
I could implement code that downcasts a Base class Game to a Derived class Game (like BaseballGame). Then, when the fetch_game_data function returns all of the games I can just convert them all to a Derived class object and continue on my way. Unfortunately I haven't seen much in the way of how to implement this. I tried just changing the class variable, but then the code complains because Derived class variables don't exist.
Another option I considered, but quickly fell apart, was to send an existing list of derived class objects into the fetch_game_data function, and instead of creating new Game objects it would just populate existing ones. The problem is that I won't know how many game objects I need. The fetch_game_data function determines how many games are needed by parsing the webpage. I suppose I could send in the max number of games but using number_of_teams/2, but what if there is a double-header in baseball? This quickly falls apart. I suppose I could write a function that will fetch the game data and just return the number of games for the day. Then I could populate a list of Derived games that size and send it in to be populated. But I would have to fetch all the webpage data AGAIN and parse it AGAIN to populate the list.
Nothing but bad options! I'm hoping there is a simple and elegant solution that has just eluded me thus far. I'm open to any suggestions, including a redesign if it makes sense.
Thanks!
I am porting existing code from c++ and I encountered a similar problem.
I have a generic class X and type specific classes e.g. XInt, XStr etc. There is more differences between these classes than just the type of the value. In c++ it was easy: I have virtual X::compare(X const& other).
It is overridden in XInt. Inside the overridden method I first handle cases where the 'other' is not XInt and then do static_cast<XInt const&>(other).
It is clearly impossible in python. So here is my solution. I added a non-virtual non-public function to do actual comparison to XInt and did not annotate the type of the parameter:
def _compare(self, other) -> int:
<do-actual-comparison>
def compare(self, other: X) -> int:
<handle cases where other is not XInt>
return self._compare_worker(other)
Did not test it yet but mypy does not complain and it seems that it will work due to duck typing in python. Maybe something similar will work for you.
You can convert a class of instance after it is initiated, see example below:
class A:
def __repr__(self):
return "class A"
def convert(self):
'convert A to B class'
self.__class__ = B
def hello(self):
print('hello from A')
class B(A):
""
def __repr__(self):
return "class B"
def hello(self):
print('hello from B')
a = A()
print(a)
a.hello()
a.convert()
print(a)
a.hello()
# output:
>>> "class A"
>>> "hello from A"
>>> "class B"
>>> "hello from B"
In your case, you can convert the class Game to whatever subclass you want after the instance is created.
Python cannot cast an object to another class (even subclass).
You must use concrete class when you create the game object. It can be done in a factory method (e.g. create_game), like this:
def create_game(online_game):
if online_game.type == 'baseball':
return BaseballGame()
else:
return Game()
def fetch_game_data:
games = []
games_found_online = code_that_fetches_online_games
for online_game in games_found_online:
new_game = create_game(online_game)
new_game.home_team = ...
new_game.away_team = ...
games.append(new_game)
return games

Categories