1

The best way to explain this, I guess, is by example. Writing a game, have a parent class for all character classes that establishes the basic methods and values, with a whole horde of child classes for race and profession.

So you've got Elf, Orc, Human classes that modify basic stats, and then classes like Tank, Rogue, Spellcaster, that do additional modifications. By the use of super(), it's fairly simple to do:

class ElfTank( Elf, Tank )

And get the combined modifications you want.

Is there a way to dynamically create an object and specify its parents without making nine pretedermined classes (in the case of three professions and three races)?

Note: This is an example because it's much easier for me to explain any OOP thing using either PacMan or D&D. The actual application involves anywhere from two to six parents, and would have involved waaaaaaaaay too much backstory.

EDIT: Okay, based on the feedback, and the digging into some of the sidelinks, and some tinkering of my own, I'm going to answer my own question.

Cather Steincamp
  • 104
  • 1
  • 11
  • Does this answer your question? [Dynamic inheritance in Python](https://stackoverflow.com/questions/21060073/dynamic-inheritance-in-python) – ljmc Dec 23 '21 at 23:45
  • 5
    Given how fragile multiple inheritance gets, I'll tell you right now this is a terrible idea. Use composition (an `Elf` might contain an attribute `.class_` that references `Tank` or other classes, and possibly dynamically redirect lookups via `__getattr__`) and/or common attributes (possibly using an `ABC` for `Race` classes that requires the existence of a `Class` as an attribute), not multiple inheritance. If you ever feel you want not only multiple inheritance, but *dynamic* multiple inheritance, you've almost certainly got [an XY problem](https://meta.stackexchange.com/q/66377/322040). – ShadowRanger Dec 23 '21 at 23:46
  • 1
    Consider using composition instead, look up entity component systems. – Passerby Dec 23 '21 at 23:47

1 Answers1

0

These solutions will, technically, work. But, be aware, they're all really bad ideas.

If you don't need to pass any arguments to the constructor:

def makeMeAClass( *parents ):
    class dynamicClass( *parents ):
        def __init__(self):
            super().__init__()
    return dynamicClass()   

If you want to pass an argument to the constructor, you can simply put it before you start listing the parent classes. (This will work for any number of arguments, so long as it's a consistent number of arguments).

def MakeMeAClassWithAnArgument( argument, *parents )
    class dynamicClass( *parents ):
        def __init__(self, argument):
             super().__init__(argument)
    return dynamicClass( argument )

If you really want to get crazy, you can even have a dynamic number of arguments passed to the constructor, but they have to be specified by keyword.

def MakeMeAClassWithDynamicArguments( *parents, **arguments )
    class dynamicClass( *parents ):
        def __init__(self, **arguments):
            super().__init__(**arguments)
    return dynamicClass(**arguments)

But, there are better ways to do it, particularly if you're going to end up with multiple objects of the same parentage-- you'll end up with multiple dynamically-generated object classes that are exactly identical, but taking up multiple places in memory, which sorta defeats a lot of the benefits of OOP.

I have encountered enough resistance to this sort of thing while researching it to accept "Don't Do That" that I haven't really dug further into why. I welcome comments from the more qualified for those who need to hear the followup "No, Really, Don't Do That."

Cather Steincamp
  • 104
  • 1
  • 11
  • Similar to your functions here, Python's `type()` builtin can be used to dynamically create a class. See https://www.google.com/amp/s/www.geeksforgeeks.org/python-type-function/amp/ – Luke Nelson Dec 24 '21 at 01:04
  • Though I would agree wholeheartedly that there is probably a better way, and that dynamic class generation is probably not the solution you are looking for. (Not you as in OP... You as in anyone reading this comment). – Luke Nelson Dec 24 '21 at 01:05
  • There's no need to override `__init__` at all; if you don't override it, and all the parents use `super()` appropriately, the child will inherit from all of them correctly (having the child explicitly implement `__init__` won't fix anything if the parents *fail* to use `super()` appropriately, just to be clear). `class dynamicClass( *parents ): pass` would accomplish the same job, faster, and without needing to choose which `__init__` signature to use in a vacuum. – ShadowRanger Dec 24 '21 at 02:04