Python中的动态类实例化

时间:2010-07-28 11:14:05

标签: python introspection

我在模块中有很多类。让我们说:

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


现在,在另一个模块中,我想动态加载players.py中的所有类并实例化它们。我使用python inspect模块。

'''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]

我希望ai_players期望成为对象实例的列表(假设__str__返回名称):['Homer', 'Barney...]

但是,我收到以下错误

TypeError: __init__() takes exactly 2 arguments (1 given)


有趣的是,当我没有在列表理解或for循环中实例化类对象时,一切正常。

# this does not throw an error
ai_player1 = ai_players[0]()
print ai_player1
# >> 'Homer'


那么为什么Python不允许我在列表comprehensions / for循环中实例化类(我也在for循环中尝试过它)?

或更好:您如何动态加载模块中的所有类并在列表中实例化它们?

*注意,我正在使用Python 2.6

修改

原来我过分简化了我的问题,上面的代码就好了。 但是,如果我将players.py更改为

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

并将main.py中的第5行更改为

ai_players = inspect.getmembers(players, 
             lambda x: inspect.isclass(x) and issubclass(x, players.Player))

我遇到了所描述的问题。这也不起作用(当我在repl上测试它时它起作用了)

ai_player1 = ai_players[0]()

它似乎与继承和默认参数有关。如果我将基类Player更改为

class Player():
    """This class is intended to be subclassed"""
    def __init__(self, name='Maggie'):
        self.name = name

然后我没有得到错误,但球员的名字总是'Maggie'。

EDIT2:

我玩了一下,事实证明getmembers函数以某种方式“吃掉”默认参数。从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',)

您是否知道来自检查模块的任何替代方案?

EDIT3:

  

请注意,如果给SAME类两次,则issubclass()返回True。 (Tryptich)

那是恶人

3 个答案:

答案 0 :(得分:2)

我运行了你的测试代码,它对我来说很好。

该错误表明您实际上并未在所有类上设置默认的“name”参数。

我要仔细检查。

编辑:

请注意,如果给SAME课程两次,issubclass()会返回True

>>> class Foo: pass
>>> issubclass(Foo, Foo)
True

因此,您的代码尝试在没有名称参数的情况下实例化Player并死亡。

好像你过分复杂了一些事情。我建议您更详细地解释一下您的程序,也许是另一个问题,以获得关于这种方法是否有必要(可能不是)的反馈。

答案 1 :(得分:1)

ai_players = inspect.getmembers(players, inspect.isclass())

此行尝试调用不带参数的inspect.isclass,并将getmembers的调用结果作为第二个参数。你想传递函数inspect.isclass,所以就这样做(编辑:更清楚,你想要inspect.getmembers(players, inspect.isclass))。你觉得这些括号怎么办?

答案 2 :(得分:0)

以下是做你想做的更好的方法。

player.py:

class Player:
    def __init__(self, name):
        self.name = name

main.py:

import player

names = ['Homer', 'Barney', 'Moe']
players = [player.Player(name) for name in names]

请注意,您要定义一个类来保存播放器的详细信息,然后您可以根据需要创建此类的多个实例。