开发者

Returning an instance of a class from a file in python

In my program I have a package filled with various .py files each containing a class definition. I want to make a list where each entry is an instance of one of those classes. In addition, my program doesn't know how many files are in the package or what the files or classes are called, so I can't just import each file. Ideally, I should be able to modify the contents of the package (take out files, put new ones in, etc.) without having to rewrite other parts of the program. Is there a way to do this?

Originally, I had a 'if __name__ == '__main__': return foo()' line in each file and tried to append to the list using execfile(), but obviously this doesn't work. Any ideas?

Sorry if this is kinda vague. I'll try to clarify if needed. I'm using Python 2.5.4.

EDIT:

My program is a random character generator for Dungeons and Dragons. I made a package for every major data type the program needs. I have a package for Classes, Races, Items, etc. and when making a character, my program makes a list of each data type that it can sort through when making a character. For example, when equipping a character, the program can look at the Weapon list and filter out all the weapons that are unsuitable for that character and then randomly choose from the ones that remain.

I don't want to specify file开发者_运维问答 names because I would like the ability to easily add to this program later. If later on down the road I wanted to add more weapon types to the program, I could just write a few new class descriptions and drop them in the Weapons package, and the program could use them without me needing to edit any other code.


This sounds like a bit of a bad design. It would probably be better if you elaborate on the problem and we can help you to solve it some other way. However, what you want isn't hard:

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

EDIT: Simplified the code a bit.


To acheive this you are going to need to do the following things:

  • Have your code enumerate the source files containing your code.
  • For each source file, import the code specified in the file into a new module.
  • For each module, locate all the classes contained, instantiate each one and add it to your final list.

To take each part in turn:

  • To enumerate the source files, use os.walk and os.path to find the files and build full paths to the source.
  • To import code from a given source file dynamically, you can do execfile(my_file) in my_dict where my_file is the full path to your source file and my_dict is a dictionary to return the resulting code in (any classes declared in the source file would become members of this dict for example). Note you only need to use this method if the files you are importing are not part of a valid python module/package hierarchy (with an init.py file in the package) - if they are you can use import() instead.
  • To enumerate the classes declared in a given module you could use inspect.getmembers().


If you're willing to do a bit more work, you can use pkg_resource's entry points to advertise and discover the relevant classes. The Fedora Account System uses this to provide plugin functionality.


Assuming, first, that all your modules exist as .py files in the package's directory:

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

Further assumptions: each module should have exactly 1 class (otherwise it's skipped with a warning) instantiable without arguments (ditto ditto); you don't want to look at __init__.py (if any; actually this code does not require the path to be an actual package, any directory will do, so __init__.py may or may not be present) nor any module whose name starts with an underscore ("private" modules of the package).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜