在Python中从文件返回类的实例

1 投票
4 回答
651 浏览
提问于 2025-04-15 18:34

在我的程序里,有一个包里面装了很多个.py文件,每个文件里都有一个类的定义。我想做一个列表,列表里的每一项都是这些类的一个实例。而且,我的程序并不知道包里有多少个文件,也不知道这些文件或类的名字,所以我不能直接导入每个文件。理想情况下,我希望能够修改这个包的内容(比如删除文件、添加新文件等),而不需要重写程序的其他部分。有没有办法做到这一点呢?

最开始,我在每个文件里都有一行'if __name__ == '__main__': return foo()',然后试着用execfile()来添加到列表里,但显然这样不行。有没有什么想法呢?

抱歉,如果这个描述有点模糊。如果需要的话,我会尽量解释得更清楚。我使用的是Python 2.5.4。

补充一下:

我的程序是一个随机角色生成器,用于《龙与地下城》。我为程序需要的每种主要数据类型都做了一个包。我有一个包专门放角色类、种族、物品等等。在创建角色时,我的程序会生成一个每种数据类型的列表,这样在制作角色时可以进行筛选。例如,在给角色装备时,程序可以查看武器列表,过滤掉所有不适合这个角色的武器,然后从剩下的武器中随机选择。

我不想指定文件名,因为我希望以后能方便地对这个程序进行扩展。如果将来我想添加更多的武器类型,我只需要写几个新的类描述,然后把它们放进武器包里,程序就能使用它们,而我不需要修改其他任何代码。

4 个回答

0

首先假设,你的所有模块都以 .py 文件的形式存在于包的目录里:

import inspect, glob, os, sys

def thelistyouwant(pathtothepackage):
  sys.path.insert(0, pathtothepackage)
  result = []
  for fn in glob.glob(os.path.join(pathtothepackage, '*.py')):
    if fn.startswith('_'): continue   # no __init__ or other private modules
    m = __import__(fn[:-3])
    classes = inspect.getmembers(m, inspect.isclass)
    if len(classes) != 1:
      print>>sys.stderr, "Skipping %s (%d != 1 classes!)" % (fn, len(classes))
      continue
    n, c = classes[0]
    try:
      result.append(c())
    except TypeError:
      print>>sys.stderr, "Skipping %s, can't build a %s()" % (fn, n)

  del sys.path[0]           
  return result

进一步假设:每个模块应该恰好有一个类(如果有多个类,就会跳过并发出警告),而且这个类可以不带参数地实例化(同样的道理);你不想去看 __init__.py 文件(如果有的话;实际上,这段代码并不要求路径一定是一个真正的包,任何目录都可以,所以 __init__.py 文件可能存在也可能不存在),也不想看任何名字以下划线开头的模块(这些是包的“私有”模块)。

0

要实现这个目标,你需要做以下几件事:

  • 让你的代码列出包含你代码的源文件。
  • 对于每个源文件,将文件中指定的代码导入到一个新的模块中。
  • 对于每个模块,找到里面所有的类,实例化每一个类,并把它们添加到你的最终列表中。

下面逐一解释每个步骤:

  • 要列出源文件,可以使用 os.walkos.path 来查找文件,并构建源文件的完整路径。
  • 要动态导入某个源文件中的代码,可以使用 execfile(my_file) in my_dict,其中 my_file 是你的源文件的完整路径,my_dict 是一个字典,用来存放导入的代码(比如,源文件中声明的任何类都会成为这个字典的成员)。注意,只有当你导入的文件不属于有效的 Python 模块/包结构(即包中没有 init.py 文件)时,才需要使用这种方法。如果它们属于有效的模块/包结构,你可以直接使用 import()
  • 要列出某个模块中声明的类,可以使用 inspect.getmembers()
2

这听起来有点设计不太好。如果你能详细说明一下问题,我们可以帮你想其他解决办法。不过,你想要的其实并不难:

import types
import my_package

my_package_members = [getattr(my_package, i) for i in dir(my_package)]
my_modules = [i for i in my_package_members if type(i) == types.ModuleType]

instances = []

for my_module in my_modules:
    my_module_members = [getattr(my_module, i) for i in dir(my_module)]
    my_classes = [i for i in my_module_members
                  if type(i) in (types.TypeType, types.ClassType)]
    for my_class in my_classes:
        instances.append(my_class())

编辑:把代码简化了一下。

撰写回答