Python 中的动态类实例化
我的模块中有很多类。比方说:
'''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 不允许我在列表推导/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
Then,我不会收到错误,但玩家的名字始终是“Maggie”。
编辑2:
我玩了一下,结果发现 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',)
您知道从检查模块获取成员的任何替代方法吗?
编辑3:
请注意,如果两次给出相同的类,issubclass() 将返回 True。 (色霉)
那是作恶者
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我运行了你的测试代码,它对我来说效果很好。
该错误表明您实际上并未在所有类上设置默认的“name”参数。
我会仔细检查。
编辑:
请注意,如果两次给出相同的类,则
issubclass()
返回True
。因此,您的代码尝试实例化没有名称参数的
Player
并死亡。看来你把事情变得有点过于复杂了。我建议更详细地解释您的程序,也许在另一个问题中,以获得有关这种方法是否必要(可能不需要)的一些反馈。
I ran your test code and it worked fine for me.
That error is indicating that you are not actually setting the default "name" parameter on ALL of your classes.
I'd double check.
Edit:
Note that
issubclass()
returnsTrue
if given the SAME class twice.So your code tries to instantiate
Player
with no name argument and dies.Seems like you're over-complicating things a bit. I'd suggest explaining your program in a bit more detail, perhaps in a different question, to get some feedback on whether this approach is even necessary (probably not).
此行尝试不带参数调用 inform.isclass ,并将调用 getmembers 的结果作为第二个参数。您想要传递函数
inspect.isclass
,所以就这样做(编辑:为了更清楚,您需要inspect.getmembers(players, inform.isclass)
)。你认为这些括号有什么作用?This line tries to calls inspect.isclass with no arguments and gives the result of the call to getmembers as second argument. You want to pass the function
inspect.isclass
, so just do that (edit: to be clearer, you wantinspect.getmembers(players, inspect.isclass)
). What do you think these parentheses do?以下是做你想做的事的更好方法。
player.py:
main.py:
请注意,您想要定义一个类来保存玩家的详细信息,然后您可以根据需要创建该类的任意多个实例。
The following is a better way of doing what you want.
player.py:
main.py:
Note that you want to define a single class to hold the details of a player, then you can create as many instances of this class as required.