Маскарадный реальный модуль класса - PullRequest
3 голосов
/ 27 ноября 2009

Предположим, у вас есть следующий макет для пакета Python

./a
./a/__init__.py
./a/_b.py

внутри __init__.py у вас есть

from _b import *

и внутри _b.py у вас есть

class B(object): pass

При импорте из интерактивного приглашения

>>> import a
>>> a.B
<class 'a._b.B'>
>>> 

Как я могу полностью скрыть существование _b?

Проблема, которую я пытаюсь решить, заключается в следующем: я хочу, чтобы фасадный пакет импортировал «скрытые» модули и классы. Классы, доступные на фасаде (в моем случае a), остаются стабильными и гарантированными на будущее. Я хочу, однако, свободу перемещать классы "под капотом", отсюда и скрытые модули. Это все хорошо, но если какой-то клиентский код выбирает объект, предоставленный фасадом, эти данные будут ссылаться на вложенность скрытого модуля, а не на фасад. Другими словами, если я перемещу класс B в модуле _c.py, клиентские коды не смогут распаковать, потому что выбранные классы ссылаются на a._b.B, который был перемещен. Если бы они ссылались на a.B, я мог бы переместить класс B столько, сколько я хочу, под капот, не разрушая маринованные данные.

Ответы [ 2 ]

5 голосов
/ 27 ноября 2009

попробовать:

B.__module__= 'a'

Кстати, вы, вероятно, хотите абсолютный импорт:

from a._b import *

, поскольку относительный импорт без нового явного точечного синтаксиса исчезает ( см. PEP 328 ).

ETA re comment:

Мне бы пришлось установить модуль явно для каждого класса

Да, я не думаю, что есть способ обойти это, но вы могли бы по крайней мере автоматизировать его, в конце __init__:

for value in globals().values():
    if inspect.isclass(value) and value.__module__.startswith('a.'):
        value.__module__= 'a'

(Только для классов нового стиля вы можете использовать isinstance(value, type) вместо inspect. Если модуль не должен работать как __main__, вы можете использовать __name__ вместо жесткого кодирования 'a'.)

2 голосов
/ 27 ноября 2009

Вы можете установить переменную __module__ для Class B

class B(object): pass
B.__module__ = 'a'

Для классов, функций и методов этот атрибут содержит имя модуля, в котором был определен объект.

Или определите его один раз в __init__.py:

from a._b import B # change this line, when required, e.g. from a._c import B
B.__module__ = 'a'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...