Подводные камни модуля утиной печати?

Я только что начал экспериментировать с новой техникой, которую я называю (по крайней мере, на данный момент) «модульная утиная печать».

Пример:

Основной модуль

import somepackage.req  ## module required by all others
import abc
import Xyz

Модуль abc

__all__=[]

def getBus():
    """ Locates the `req` for this application """
    for mod_name in sys.modules:
        if mod_name.find("req") > 0:
            return sys.modules[mod_name].__dict__["Bus"]
    raise RuntimeError("cannot find `req` module")

Bus=getBus()

В модуле abc мне не нужно явно указывать import req: он может быть где угодно в иерархии пакетов. Конечно, это требует некоторой дисциплины...

С помощью этого метода легко перемещать пакеты в иерархии.

Меня ждут подводные камни? например переход на Python 3K

Обновлено: после еще нескольких тестов я решил вернуться к вставке зависимостей пакетов непосредственно в sys.path.


person jldupont    schedule 23.02.2010    source источник
comment
mod_name.find("req") > 0 на самом деле не то, что вы имеете в виду. Что делать, если у меня есть модуль с именем frequency?   -  person Mike Graham    schedule 24.02.2010
comment
sys.modules[mod_name].__dict__["Bus"] пишется sys.modules[mod_name].Bus.   -  person Mike Graham    schedule 24.02.2010
comment
@Mike Graham: да ... работа в процессе, как я уже прокомментировал. Спасибо.   -  person jldupont    schedule 24.02.2010


Ответы (3)


Могут быть импортированы все виды модулей, которые содержат «req», и вы не знаете, действительно ли это тот модуль, который вы ищете:

>>> import urllib.request
>>> import tst
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tst.py", line 12, in <module>
    Bus=getBus()
  File "tst.py", line 9, in getBus
    return sys.modules[mod_name].__dict__["Bus"]
KeyError: 'Bus'

Весь смысл пакетов в том, что существуют пространства имен для иерархий модулей. Поиск имен модулей «из любого пакета» просто приводит к случайному сбою кода, если пользователь импортирует какую-либо библиотеку, которая содержит модуль с конфликтующим именем.

person sth    schedule 23.02.2010
comment
+1: хороший момент: мне нужно быть более точным в сопоставлении с образцом. Считайте, что эта работа в процессе ;-) - person jldupont; 24.02.2010

Этот метод опасен и подвержен ошибкам. Это может работать с вашими тестами до тех пор, пока кто-нибудь не импортирует новый something.req и не получит запутанную, далекую ошибку. (Это в лучшем случае; текущая реализация перескочила бы на многие другие модули.) Если вы реструктурируете пакеты, то в это время достаточно легко изменить свой код автоматическим способом без использования магии. Python позволяет делать всевозможные волшебные, динамические вещи, но это не значит, что мы должны это делать.

person Mike Graham    schedule 23.02.2010

Я думаю, что это больше похоже на утиную печать. Я бы также рекомендовал использовать более уникальный идентификатор, чем «Автобус».

def getBus():
    """ Locates the Bus for this application """
    for mod in sys.modules.values():
        if hasattr(mod, 'Bus') and type(mod.Bus) is...: # check other stuff about mod.Bus
            return mod.Bus
    raise RuntimeError("cannot find Bus")
person John La Rooy    schedule 24.02.2010