Сделать импортируемыми только определенные символы в модуле, например, с помощью `__all__`

#python #python-import

#python #python-импорт

Вопрос:

tl; dr:

Мне нравятся чистые API, поэтому мне не нравится, когда «внутренний» импорт, такой import numpy as np как внутри моих модулей, отображается в module API.

В моем пакете (устанавливаемом с setup.py ) У меня есть несколько подмодулей, каждый из которых импортирует набор модулей, например, numpy или подобных им встроенных sys .
Допустим, в моем пакете mytools у меня есть подмодуль mytools.mysubtools , в котором я определяю функцию foo , которая требует numpy (импортируется с помощью import numpy as np ). foo добавляется в __all__ = ['foo'] .
Теперь я импортирую этот подмодуль с помощью import mytools.mysubtools as mst . При доступе mst с помощью завершения кода (например, с помощью Spyder IDE, PyCharm, Kite …) и т.д., Он всегда показывает все символы и импортирует в mst , в данном случае foo и np .
Могу ли я избежать появления импорта, подобного np , в импортированном модуле, чтобы он отображался только foo ?

подробное описание

Допустим, структура моего пакета выглядит следующим образом:

 /mytools/
 |-- __init__.py  # "mytools-init"
 |-- my_subtools.py
 |-- some_other_subtools.py
  

__init__.py Файл выглядит следующим образом:

 from . import my_subtools as mysubtools
__all__ = ['mysubtools']
  

И my_subtools.py :

 import numpy as np

__all__ = ['foo']

def foo():
    return np.sqrt(4**3)
  

Теперь я импортирую my_subtools с

 import mytools.mysubtools as mst
  

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

С помощью __all__ можно избежать импорта всех символов для импорта wild / star, например from mytools.mysubtools import * . Можно __all__ каким-то образом также использовать для искажения явного импорта?

Или есть какой-либо рекомендуемый способ избежать импорта всех символов модуля?
Для функций я использую искажение имен, например def _baz(): return 4**4 .
Но целесообразно ли это также для импорта, например import numpy as _np ? Какие-либо недостатки?

Что я ищу:
явное решение, поскольку: Явное лучше неявного.
Например, что-то вроде __all__ , но для явного импорта. Или что-то подобное __except__ = ['np'] , что исключает символы из импорта.

Комментарии:

1. «При доступе mst с помощью code-completition и т.д. Всегда отображаются все символы и импорт в mst, в данном случае foo и np». Сам Python не имеет завершения кода. Что такое «это»?

2. Вы правы. Я, конечно, говорил о завершении кода в таких редакторах, как Spyder, PyCharm и т.д.

Ответ №1:

__all__ = [...] это хороший намек на «экспортируемые» имена, но это всего лишь намек. (Хотя некоторые IDE, такие как PyCharm, имеют тенденцию получать подсказку.)

Я бы определенно не стал этого делать import numpy as _np ; вы бы просто усложнили себе жизнь.

Вы не можете запретить кому-либо импортировать элементы из вашего модуля, если они там есть … и если бы вы действительно действительно этого хотели, вы, конечно, могли бы постфиксить все ваши модули такими мантрами, как del np, sys, os, quux, bar ; таким образом, эти имена не были бы импортируемыми, но… Я действительно не думаю, что это стоит усилий.

Комментарии:

1. Это больше, чем подсказка. Это определяет, что from foo import * означает. Кроме того, import foo as _foo это распространенная идиома, используемая в самой стандартной библиотеке (например, github.com/python/cpython/blob /… )

2. Спасибо за вашу помощь! Я надеялся на какой-нибудь явный стиль, __all__ специфичный для явного импорта, или обратный __all__ подобный __except__ = ['np'] , который исключает символы из импорта…

3. @chepner Таким образом, вы бы сказали, что импорт с искаженным именем удобен в использовании?

4.В документации для import обсуждается семантика __all__ . В нем явно упоминается, что имена с _ префиксами не включаются в общедоступный интерфейс модуля, когда __all__ он не определен (и, конечно, когда __all__ он определен, ничто не является неявной частью общедоступного интерфейса).

5. Спасибо! Читаем строку «Это предназначено для предотвращения случайного экспорта элементов, которые не являются частью API (таких как библиотечные модули, которые были импортированы и использовались в модуле)». в документации создается впечатление, что они неявно поощряют пользователя также использовать изменение имен при импорте (по крайней мере, с точки зрения интегрального подхода к API)…

Ответ №2:

Благодаря комментариям @chepner я изучил связанную реализацию в стандартной библиотеке python.

Вывод таков, что искажение имен импортируемых модулей часто используется в стандартной библиотеке. На мой взгляд, это довольно четкий признак того, что это считается pythonic и / или не имеет серьезных недостатков.
Можно найти несколько примеров импорта с искажением имен, например.:

Кажется, что имена модулей искажаются от файла к файлу. Кажется, что чем более «релевантен системе» модуль, тем меньше вероятность того, что его имя будет искажено. Например, импортирует такие

 import re as _re
import math as _math
  

часто имя искажается, в то время как import os as _os оно менее распространено. Это всего лишь мое впечатление, поэтому в целом оно может быть неверным.