Примечание: Мартино в основном ответил на мой вопрос, и здесь есть изощренное. Однако у меня было небольшое дополнительное требование (в моем вопросе), но оно было не очень ясным. Я использовал ответ Мартино для создания полного ответа, и я поделился им здесь для всех, кто хочет его увидеть.
Дополнительные требования заключались в том, что я мог использовать любой factory_method (не просто класс '* 1005). * function) и что я хотел явно зарегистрировать те, которые я хотел, в моем реестре.
Так вот моя окончательная версия ...
Я использую ту же структуру каталогов, что и Martineau:
use_things.py
things/
__init__.py
thing1.py
thing2.py
Чтобы продемонстрировать другой тип factory_method, я расширил use_things.py на пару строк:
import things # Import package.
print("The things in the package are: %r" % things.show_things())
print("Getting a Thing1")
thing = things.get_thing("Thing1")
print(f"It has message {thing.message()!r}")
print("Getting a Thing2")
thing = things.get_thing("Thing2", "kite", on_string="Mothers new gown")
print(f"It has message {thing.message()!r}")
print("Getting a Thing2 in a net")
thing = things.get_thing("Thing2_in_net", "kite", on_string="Mothers new gown")
print(f"It has message {thing.message()!r}")
Обратите внимание, что получение Thing2_in_net
создает объект типа Thing2
, но с некоторыми предварительными вычислениями.
thing1.py
теперь явно регистрирует конструктор Thing1
(__init__
), объявляя кортеж с именем, начинающимся с _register_<something>
. Другой класс UnregisteredThing
не зарегистрирован.
class Thing1(object):
def __init__(self):
pass
def message(self):
return f'This is a {type(self).__name__}'
_register_thing1 = ('Thing1', Thing1)
class UnregisteredThing(object):
def __init__(self):
pass
def message(self):
return f'This is an unregistered thing'
И thing2.py
регистрирует двух создателей, один из которых является базовым c конструктором Thing2 и один из фабричного метода:
class Thing2(object):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def message(self):
return (f"This is a different thing with args {self.args}"
f" and kwargs {self.kwargs}")
def build_thing2_in_net(*args, **kwargs):
return Thing2(*args, located='in net', **kwargs)
_register_thing2 = ('Thing2', Thing2)
_register_thing2_in_net = ('Thing2_in_net', build_thing2_in_net)
Наконец, сценарий __init__.py
, модифицированный так, чтобы специально искать атрибуты модуля с именем _register_<something>
, будет обрабатывать их как пару ключ / создатель для регистрации:
def build_registry():
""" Dynamically imports all modules in this package directory. """
import traceback
import os
globals_, locals_ = globals(), locals()
registry = {}
for filename in os.listdir(__name__):
# Process all python files in directory that don't start with an underscore
# (which also prevents this module from importing itself).
if filename[0] != '_' and filename.split('.')[-1] in ('py', 'pyw'):
modulename = filename.split('.')[0] # Filename sans extension.
package_module = '.'.join([__name__, modulename])
try:
module = __import__(
package_module, globals_, locals_, [modulename])
except:
traceback.print_exc()
raise
for name in module.__dict__:
## look for attributes of module starting in _register_
if name.startswith('_register_'):
# if so assume they are key-maker pair and register them
key, maker = module.__dict__[name]
registry[key] = maker
return registry
_registry = build_registry()
def get_thing(description, *args, **kwargs):
thingmaker = _registry[description]
return thingmaker(*args, **kwargs)
def show_things():
return list(_registry.keys())
Полученный результат показывает, что только зарегистрированные вещи появляются в реестре, и это может быть любой метод, который создает объект:
The things in the package are: ['Thing2', 'Thing2_in_net', 'Thing1']
Getting a Thing1
It has message 'This is a Thing1'
Getting a Thing2
It has message "This is a different thing with args ('kite',) and kwargs {'on_string': 'Mothers new gown'}"
Getting a Thing2 in a net
It has message "This is a different thing with args ('kite',) and kwargs {'located': 'in net', 'on_string': 'Mothers new gown'}"