I have a bunch of classes in a module. Let's say:
'''players.py'''
class Player1:
def __init__(self, name='Homer'):
self.name = name
class Player2:
def __init__(self, name='Barney'):
self.name = name
class Player3:
def __init__(self, name='Moe'):
self.name = name
...
Now, in another module, I want to dynamically load all classes in players.py
and instantiate them. I use the python inspect
module for this.
'''main.py'''
import inspect
import players
ai_players = inspect.getmembers(players, inspect.isclass)
# ai_players is now a list like: [('Player1',<class instance>),(...]
ai_players = [x[1] for x in ai_players]
# ai_players is now a list like: [<class instance>, <class...]
# now let's try to create a list of initialized objects
ai_players = [x() for x in ai_players]
I would ai_players
expect to be a list of object instances like so (let's suppose __str__
returns the name): ['Homer', 'Barney...]
However, I get the following error
TypeError: __init__() takes exactly 2 arguments (1 given)
The funny thing is that when I don't instantiate the class objects in a list comprehension or a for loop everything works fine.
# this does not throw an error
ai_player1 = ai_players[0]()
print ai_player1
# >> 'Homer'
So why doesn't Python allow me to instantiate the classes in a list comprehensions/for loop (I tried it in a for loop, too)?
Or better: How would you go about dynamically loading all classes in a module and instantiating them in a list?
* Note, I'm using Python 2.6
EDIT
Turns out I oversimplified my problem and the above code is just fine.
However if I change players.py
to
'''players.py'''
class Player():
"""This class is intended to be subclassed"""
def __init__(self, name):
self.name = name
class Player1(Player):
def __init__(self, name='Homer'):
Player.__init__(self, name)
class Player2(Player):
def __init__(self, name='Barney'):
Player.__init__(self, name)
class Player3(Player):
def __init__(self, name='Moe'):
Player.__init__(self, name)
and change the 5th line in main.py
to
ai_players = inspect.getmembers(players,
lambda x: inspect.isclass(x) and issubclass(x, players.Player))
I encounter the described problem. Also that doesn't work either (it worked when I tested it on the repl)
ai_player1 = ai_players[0]()
It seems to have something to do with inheritance and default arguments. If I change the base class Player
to
class Player():
"""This class is intended to be subclassed"""
def __init__(self, name='Maggie'):
self.name = name
Then I don't get the error but the players' names are always 'Maggie'.
EDIT2:
I played around a bit and it turns out the getmembers function somehow "eats" the default parameters. Have a look at this log from the repl:
>>> import players
>>> import inspect
>>> ai_players = inspect.getmembers(players,
... lambda x: inspect.isclass(x) and issubclass(x, players.Player))
>>> ai_players = [x[1] for x in ai_players]
>>> ai_players[0].__init__.func_defaults
>>> print ai_players[0].__init__.func_defaults
None
>>> players.Player1.__init__.func_defaults
('Homer',)
Do you know of any alternative to getmembers from the inspect module?
EDIT3:
Note that issubclass() returns True if given the SAME class twice. (Tryptich)
That's been the evildoer