Возврат экземпляра класса из файла в Python - PullRequest
1 голос
/ 28 января 2010

В моей программе есть пакет, заполненный различными .py файлами, каждый из которых содержит определение класса. Я хочу сделать список, где каждая запись является экземпляром одного из этих классов. Кроме того, моя программа не знает, сколько файлов находится в пакете или как называются файлы или классы, поэтому я не могу просто импортировать каждый файл. В идеале я должен иметь возможность изменять содержимое пакета (извлекать файлы, помещать новые и т. Д.) Без необходимости переписывать другие части программы. Есть ли способ сделать это?

Первоначально у меня была строка 'if __name__ ==' __main__ ': return foo ()' в каждом файле, и я пытался добавить в список, используя execfile (), но, очевидно, это не работает. Есть идеи?

Извините, если это немного расплывчато. Я постараюсь уточнить, если это необходимо. Я использую Python 2.5.4.

EDIT:

Моя программа - генератор случайных персонажей для Dungeons and Dragons. Я сделал пакет для всех основных типов данных, которые нужны программе. У меня есть пакет для классов, рас, предметов и т. Д., И при создании символа моя программа составляет список каждого типа данных, который можно отсортировать при создании символа. Например, при экипировке персонажа, программа может просмотреть список оружия и отфильтровать все оружие, которое не подходит для этого персонажа, а затем случайным образом выбрать из оставшихся.

Я не хочу указывать имена файлов, потому что мне бы хотелось иметь возможность легко добавить эту программу позже. Если позже в будущем я захочу добавить больше типов оружия в программу, я мог бы просто написать несколько новых описаний классов и поместить их в пакет Оружия, и программа могла бы использовать их без необходимости редактирования какого-либо другого кода.

Ответы [ 4 ]

2 голосов
/ 28 января 2010

Звучит как плохой дизайн. Возможно, было бы лучше, если бы вы подробно остановились на проблеме, и мы могли бы помочь вам решить ее другим способом. Однако то, что вы хотите, не сложно:

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

РЕДАКТИРОВАТЬ: немного упростил код.

0 голосов
/ 28 января 2010

Предполагая, во-первых, что все ваши модули существуют в виде .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

Дополнительные предположения: каждый модуль должен иметь ровно 1 класс (в противном случае он пропускается с предупреждением), инстанцируемый без аргументов (то же самое); Вы не хотите смотреть на __init__.py (если есть; на самом деле этот код не требует, чтобы путь был действительным пакетом, подойдет любой каталог, поэтому __init__.py может присутствовать или не присутствовать), а также ни на каком модуле, имя которого начинается с подчеркивания («приватные» модули пакета).

0 голосов
/ 28 января 2010

Если вы хотите сделать немного больше работы, вы можете использовать pkg_resource точки входа для рекламы и поиска соответствующих классов. Система учетных записей Fedora использует это для обеспечения функциональности плагина .

0 голосов
/ 28 января 2010

Чтобы достичь этого, вам нужно будет сделать следующее:

  • Попросите ваш код перечислить исходные файлы, содержащие ваш код.
  • Для каждого исходного файла импортируйте код, указанный в файле, в новый модуль.
  • Для каждого модуля найдите все содержащиеся в нем классы, создайте экземпляры каждого из них и добавьте его в окончательный список.

Чтобы принять каждую часть по очереди:

  • Чтобы перечислить исходные файлы, используйте os.walk и os.path, чтобы найти файлы и построить полные пути к источнику.
  • Чтобы динамически импортировать код из указанного исходного файла, вы можете сделать execfile(my_file) in my_dict, где my_file - полный путь к вашему исходному файлу, а my_dict - словарь для возврата полученного кода в (любые классы, объявленные в исходном файле, станут членами этой команды, например). Обратите внимание, что вам нужно использовать этот метод только в том случае, если импортируемые файлы не являются частью действительной иерархии модуля / пакета python (с файлом init .py в пакете) - если они есть, вы можете использовать import () вместо.
  • Чтобы перечислить классы, объявленные в данном модуле, вы можете использовать inspect.getmembers () .
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...