Я вижу __all__ в __init__.py файлах. Что это делает?
Переведено автоматически
Ответ 1
Ссылка на, но здесь явно не указано, это именно то, когда используется __all__. Это список строк, определяющих, какие символы в модуле будут экспортированы, когда from <module> import * используется в модуле.
Например, следующий код в a foo.py явно экспортирует символы bar и baz:
__all__ = ['bar', 'baz']
waz = 5 bar = 10 defbaz(): return'baz'
Затем эти символы можно импортировать следующим образом:
from foo import *
print(bar) print(baz)
# The following will trigger an exception, as "waz" is not exported by the module print(waz)
Если закомментировать __all__ приведенное выше, этот код будет выполнен до завершения, поскольку поведение import * по умолчанию заключается в импорте всех символов, которые не начинаются с подчеркивания, из данного пространства имен.
ПРИМЕЧАНИЕ:__all__ влияет только на from <module> import * поведение. Элементы, которые не упомянуты в __all__, по-прежнему доступны извне модуля и могут быть импортированы с помощью from <module> import <member>.
Ответ 2
Это список общедоступных объектов этого модуля, интерпретируемый import *. Он переопределяет стандарт скрытия всего, что начинается с подчеркивания.
Ответ 3
Объясните все на Python?
Я продолжаю видеть переменную, __all__ установленную в разных __init__.py файлах.
Что это делает?
Что __all__ делает?
Он объявляет семантически "общедоступные" имена из модуля. Если в __all__ есть имя, ожидается, что пользователи будут использовать его, и они могут ожидать, что оно не изменится.
Это также будет иметь программные эффекты:
import *
__all__ в модуле, например, module.py:
__all__ = ['foo', 'Bar']
означает, что когда вы import * из модуля импортируете только те имена, которые есть в __all__:
from module import * # imports foo and Bar
Инструменты документации
Документация и инструменты автозаполнения кода могут (фактически, должны) также проверять __all__, чтобы определить, какие имена отображать как доступные из модуля.
Файлы __init__.py необходимы для того, чтобы Python обрабатывал каталоги как содержащие пакеты; это сделано для предотвращения непреднамеренного скрытия допустимых модулей, которые позже появятся в пути поиска модуля, каталогами с общим именем, такими как string.
В простейшем случае, __init__.py может быть просто пустым файлом, но он также может выполнять код инициализации для пакета или устанавливать __all__ переменную.
Таким образом, __init__.py можно объявить __all__ для пакета.
Управление API:
A package is typically made up of modules that may import one another, but that are necessarily tied together with an __init__.py file. That file is what makes the directory an actual Python package. For example, say you have the following files in a package:
package_module_2 = package / 'module_2.py' package_module_2.write_text(""" __all__ = ['Bar'] imp_detail1 = imp_detail2 = imp_detail3 = None class Bar: pass """)
And now you have presented a complete api that someone else can use when they import your package, like so:
import package package.foo() package.Bar()
And the package won't have all the other implementation details you used when creating your modules cluttering up the package namespace.
__all__ in __init__.py
После дополнительной работы, возможно, вы решили, что модули слишком большие (например, многие тысячи строк?) и их нужно разделить. Итак, вы делаете следующее:
И вы можете легко добавлять в свой API элементы, которыми вы можете управлять на уровне подпакета, а не на уровне модуля подпакета. Если вы хотите добавить новое имя в API, вы просто обновляете __init__.py, например, в module_2:
from .Bar_implementation import * from .Baz_implementation import * __all__ = ['Bar', 'Baz']
И если вы не готовы публиковать Baz в API верхнего уровня, на вашем верхнем уровне __init__.py вы могли бы:
from .module_1 import * # also constrained by __all__'s from .module_2 import * # in the __init__.py's __all__ = ['foo', 'Bar'] # further constraining the names advertised
и если ваши пользователи знают о доступности Baz, они могут его использовать:
import package package.Baz()
но если они не знают об этом, другие инструменты (например, pydoc) не будут их информировать.
Вы можете позже изменить это, когда Baz будет готово к прайм-тайму:
from .module_1 import * from .module_2 import * __all__ = ['foo', 'Bar', 'Baz']
Использование префикса _ против __all__:
По умолчанию Python экспортирует все имена, которые не начинаются с _ при импорте с помощью import *. Как продемонстрировал здесь сеанс командной строки, import * не вводит _us_non_public имя из us.py модуля:
$ cat us.py USALLCAPS = "all caps" us_snake_case = "snake_case" _us_non_public = "shouldn't import" $ python Python 3.10.0 (default, Oct 42021, 17:55:55) [GCC 10.3.0] on linux Type"help", "copyright", "credits"or"license"for more information. >>> from us import * >>> dir() ['USALLCAPS', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'us_snake_case']
Вы, конечно, могли бы положиться на этот механизм. Некоторые пакеты в стандартной библиотеке Python, на самом деле, действительно полагаются на это, но для этого они используют псевдонимы для своего импорта, например, в ctypes/__init__.py:
import os as _os, sys as _sys
Использование _ соглашения может быть более элегантным, потому что оно устраняет избыточность повторного присвоения имен. Но это добавляет избыточность для импорта (если у вас их много), и легко забыть делать это последовательно - и последнее, чего вы хотите, это бесконечно поддерживать то, что вы намеревались сделать всего лишь деталью реализации, только потому, что вы забыли поставить префикс _ при именовании функции.
Я лично пишу __all__ на ранних этапах своего жизненного цикла разработки для модулей, чтобы другие, кто может использовать мой код, знали, что им следует использовать, а что нет.
Большинство пакетов стандартной библиотеки также используют __all__.
Когда имеет смысл избегать __all__
Имеет смысл придерживаться _ соглашения о префиксе вместо __all__ когда:
Вы все еще находитесь в режиме ранней разработки, у вас нет пользователей, и вы постоянно дорабатываете свой API.
Возможно, у вас действительно есть пользователи, но у вас есть unittests, которые охватывают API, и вы все еще активно дополняете API и дорабатываете его в процессе разработки.
export Декоратор
Недостатком использования __all__ является то, что вам приходится писать имена экспортируемых функций и классов дважды - и информация хранится отдельно от определений. Мы могли бы использовать декоратор для решения этой проблемы.
Идею такого декоратора экспорта я почерпнул из доклада Дэвида Бизли об упаковке. Похоже, эта реализация хорошо работает в традиционном импортере CPython. Если у вас есть специальный хук импорта или система, я не гарантирую это, но если вы примете его, отказаться от него довольно тривиально - вам просто нужно будет вручную добавить имена обратно в __all__
Итак, например, в библиотеке утилит вы должны определить декоратор:
и затем, где вы определили бы __all__, вы делаете это:
$ cat > main.py from lib import export __all__ = [] # optional - we create a list if __all__ is not there.
@export deffoo(): pass
@export defbar(): 'bar'
defmain(): print('main')
if __name__ == '__main__': main()
И это прекрасно работает независимо от того, выполняется ли как main или импортируется другой функцией.
$ cat > run.py import main main.main()
$ python run.py main
И подготовка API с помощью import * тоже будут работать:
$ cat > run.py from main import * foo() bar() main() # expected to error here, not exported
$ python run.py Traceback (most recent call last): File "run.py", line 4, in <module> main() # expected to error here, not exported NameError: name 'main'isnot defined
Ответ 4
Я просто добавляю это для точности:
Все остальные ответы относятся к модулям. Исходный вопрос явно упоминался __all__ в __init__.py файлах, так что речь идет о пакетах python.
Как правило, __all__ вступает в игру только тогда, когда используется from xxx import * вариант import инструкции. Это относится как к пакетам, так и к модулям.
Поведение модулей объясняется в других ответах. Точное поведение пакетов подробно описано здесь.
Короче говоря, __all__ на уровне пакета выполняет примерно то же самое, что и для модулей, за исключением того, что имеет дело с модулями внутри пакета (в отличие от указания имен внутри модуля). Итак , __all__ определяет все модули, которые должны быть загружены и импортированы в текущее пространство имен при использовании from package import *.
Большая разница в том, что когда вы опускаете объявление __all__ в пакете __init__.py, оператор from package import * вообще ничего не импортирует (за исключениями, описанными в документации, см. Ссылку выше).
С другой стороны, если вы опустите __all__ в модуле, "звездчатый импорт" импортирует все имена (не начинающиеся с подчеркивания), определенные в модуле.