2016-08-29 4 views
1

У меня есть пакеты, содержащие классы C1, C2, CInvalid3, C4, которые определены в разных подблоках.Сбор некоторых конкретных классов в модуле python

my_package/ 
    __init__.py 
    sub_package1/ 
     __init__.py 
     sub_module1.py # contains C1, C2 
    sub_package2/ 
     __init__.py 
     sub_module2.py # contains CInvalid3, C4 

Для строки, содержащей имя какого-либо класса, я хочу инициализировать соответствующий класс. Например,

some_valid_class_name = 'C1' 
some_valid_class = all_valid_classes[some_valid_class_name]() 

В настоящее время я создаю модуль all_classes.py под my_package, который содержит

all_valid_classes = {} # I want the dict contain C1, C2 and C4 

def this_is_a_valid_class(cls): 
    all_valid_classes[cls.__name__] = cls 
    return cls 

И в sub_module1.py и sub_module2.py, я использую

from my_package.all_classes import this_is_a_valid_class 

@this_is_a_valid_class 
class C1(object): 
    pass 

и добавить

from .sub_package1 import sub_module1 
from .sub_package2 import sub_module2 

до my_package/__init__.py.

Есть ли лучший способ справиться с этим? Я импортирую все вещи в my_package/__init__.py. Когда я добавляю новый sub_package3.sub_module3.C99, мне нужно помнить, чтобы добавить from .sub_package3 import sub_module3 в my_package/__init__.py. Я не думаю, что это хорошие идеи.

ответ

1

Чтобы получить то, что вы ищете, что вы можете сделать, это знак допустимых классов с атрибутом класса:

class C1(object): 
    _valid = True 

А затем динамически импортировать все модули в вашем пакете, хранения допустимых классов отдельно в словаре all_valid_classes во время процесса.

Главное, что вам нужно импортировать модули всех классов, которые вы хотите использовать, независимо от того, что вы можете сделать это вручную (как вы это делаете сейчас) или динамически (поиск имен файлов модулей внутри пакета и импортировать их).

Этот фрагмент показывает концепцию (обратите внимание, что это действительно уродливо, а предназначен только для доказательства концепции, я собрал его в спешке!). Он будет идти в файле my_module/__init__.py:

import os 
import fnmatch 
from importlib import import_module 
from types import TypeType 

# dictionary with all valid classes 
all_valid_classes = {} 

# walk directory and load modules dynamically 
this_path = os.path.dirname(__file__) 
modules = [] 
for root, dirnames, filenames in os.walk(this_path): 
    for filename in fnmatch.filter(filenames, '*.py'): 

     # avoid this file importing itself 
     full_path = os.path.join(root, filename) 
     if full_path == os.path.join(this_path, '__init__.py'): 
      continue 

     # convert the filename to the module name 
     trimmed_path = full_path[len(this_path) + 1:-3] 
     module_name = trimmed_path.replace('/', '.') 

     # import the module 
     module = import_module('%s.%s' % (__name__, module_name)) 

     # find valid classes (classes with a '_valid = True' attribute 
     for item_name in dir(module): 
      item = getattr(module, item_name) 
      if type(item) == TypeType: 
       try: 
        valid_attr = getattr(item, '_valid') 
        if valid_attr is True: 
         all_valid_classes[item_name] = item 
       except AttributeError: 
        pass 

my_module После импорта, all_valid_classes будет содержать C1, C2 и C4 (при условии, что они помечены _valid = True).

ps. Обратите внимание, однако, что я лично сделал бы импорт вручную, как вы делаете, он, по-моему, более питонов и чист.

+0

FYI, '__init __. Py' может содержать важный код (' import foo' примерно эквивалентен, если верхний уровень каталога импорта содержит 'foo.py' или каталог с именем' foo', содержащий '__init __. Py'; если содержимое 'foo.py' и' foo/__ init __. py' одинаково, 'import foo' будет вести себя одинаково). Вы хотите обрабатывать '__init __. Py' тоже, не пропустите его. – ShadowRanger

+0

@ShadowRanger при импорте модулей внутри каталога файл '__init __. Py' импортируется автоматически, поэтому мы не пропускаем их в этом случае. – pacha

+1

Вы импортируете их, но вы не сканируете их для классов, поэтому любой класс, определенный исключительно в '__init __. Py', не будет найден. Пример, который хранит определения классов только в '__init__.py', см. собственный модуль 'collections' Python; определения Python вещей, которые не находятся в 'collection.abc', находятся только в' collection/__ init __. py'. – ShadowRanger

Смежные вопросы