Я использую Python все больше и больше вижу переменную __all__
в разных файлах __init__.py
. Может кто-нибудь объяснить, что это делает?
Может кто-нибудь объяснить __all__ в Python?
Ответ 1
Это список общедоступных объектов этого модуля, интерпретируемый import *
. Он отменяет умолчание, скрывая все, что начинается с подчеркивания.
Ответ 2
Связано, но явно не упомянуто здесь, именно тогда, когда используется __all__
. Это список строк, определяющих, какие символы в модуле будут экспортироваться, когда from <module> import *
используется from <module> import *
.
Например, следующий код в foo.py
явно экспортирует bar
символов и baz
:
__all__ = ['bar', 'baz']
waz = 5
bar = 10
def baz(): 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 *
состоит в том, чтобы импортировать все символы, которые не начинаются с подчеркивания, из заданного пространства имен.
Ссылка: https://docs.python.org/tutorial/modules.html#importing-from-a-package
ПРИМЕЧАНИЕ. __all__
влияет только на поведение from <module> import *
. Члены, которые не упомянуты в __all__
, по-прежнему доступны извне модуля и могут быть импортированы from <module> import <member>
.
Ответ 3
Я просто добавляю это, чтобы быть точным:
Все остальные ответы относятся к модулю. Исходный вопрос явно упоминается __all__
в __init__.py
файлах, так что это касается пакетов python.
Как правило, __all__
включается только тогда, когда используется from xxx import *
вариант оператора import
. Это относится как к пакетам, так и к модулям.
Поведение для модулей объясняется в других ответах. Точное поведение для пакетов описано здесь.
Короче говоря, __all__
на уровне пакета делает примерно то же самое, что и для модулей, за исключением того, что он касается модулей внутри пакета (в отличие от указания имен в модуле). Таким образом, __all__
указывает все модули, которые должны быть загружены и импортированы в текущее пространство имен, когда мы используем from package import *
.
Большая разница в том, что когда вы опускаете объявление __all__
в пакете __init__.py
, оператор from package import *
ничего не будет импортировать (с исключениями, описанными в документации, см. ссылку выше).
С другой стороны, если вы опускаете __all__
в модуле, "избранный импорт" будет импортировать все имена (не начиная с подчеркивания), определенные в модуле.
Ответ 4
Объяснить __all__ в 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
Из docs:
Файлы
__init__.py
необходимы, чтобы Python рассматривал каталоги как содержащие пакеты; это делается для того, чтобы предотвратить каталоги с общим именем, такие как строка, от непреднамеренного скрытия допустимых модулей, которые появляются позже на пути поиска модуля.В простейшем случае
__init__.py
может быть просто пустым файлом, но он также может выполнять код инициализации для пакета или устанавливать переменную__all__
.
Таким образом, __init__.py
может объявить __all__
для пакета.
Управление API:
Пакет обычно состоит из модулей, которые могут импортировать друг друга, но которые обязательно связаны с файлом __init__.py
. Этот файл делает каталог актуальным пакетом Python. Например, скажем, что у вас есть следующее:
package/
|-__init__.py # makes directory a Python package
|-module_1.py
|-module_2.py
в __init__.py
вы пишете:
from module_1 import *
from module_2 import *
и в module_1
у вас есть:
__all__ = ['foo',]
и в module_2
у вас есть:
__all__ = ['Bar',]
И теперь вы представили полный api, который может использовать кто-то другой, когда они импортируют ваш пакет, например:
import package
package.foo()
package.Bar()
И у них не будет всех других имен, которые вы использовали при создании ваших модулей, загромождающих пространство имен package
.
__all__
в __init__.py
После дополнительной работы, возможно, вы решили, что модули слишком большие и их нужно разделить. Итак, вы делаете следующее:
package/
|-__init__.py
|-module_1/
| |-__init__.py
| |-foo_implementation.py
|-module_2/
|-__init__.py
|-Bar_implementation.py
И в каждом __init__.py
вы объявляете __all__
, например. в модуле_1:
from foo_implementation import *
__all__ = ['foo']
И module_2 __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
И вы можете легко добавить вещи в свой API, которые вы можете управлять на уровне подпакетов, а не на уровне модуля подпакета. Если вы хотите добавить новое имя в API, вы просто обновите __init__.py
, например. в модуле_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 будет экспортировать все имена, которые не начинаются с _
. Вы, конечно, могли бы положиться на этот механизм. Некоторые пакеты в стандартной библиотеке Python, на самом деле, полагаются на это, но для этого они, как и их импорт, например, в ctypes/__init__.py
:
import os as _os, sys as _sys
Использование соглашения _
может быть более элегантным, поскольку оно удаляет избыточность именования имен еще раз. Но он добавляет избыточность для импорта (если их у них их много), и легко забыть сделать это последовательно - и последнее, что вы хотите, - это бесконечно поддерживать то, что вы намеревались только быть детализацией реализации, просто потому что вы забыли префикс _
при именовании функции.
Я лично пишу __all__
в начале жизненного цикла разработки для модулей, чтобы другие, которые могли использовать мой код, знали, что они должны использовать и не использовать.
В большинстве пакетов стандартной библиотеки также используется __all__
.
Если избегать __all__
имеет смысл
Имеет смысл придерживаться условного обозначения _
вместо __all__
, когда:
- Вы все еще находитесь в раннем режиме разработки и не имеете пользователей, и постоянно настраиваете свой API.
- Возможно, у вас есть пользователи, но у вас есть unittests, которые охватывают API, и вы по-прежнему активно добавляете API и настраиваете его в разработке.
Декодер export
Недостатком использования __all__
является то, что вам нужно дважды записывать имена функций и классов, экспортируемых дважды, и информация хранится отдельно от определений. Мы могли бы использовать декоратор для решения этой проблемы.
У меня появилась идея, что такой декоратор-декор из Дэвида Бэзли говорит на упаковке. Эта реализация, похоже, хорошо работает в традиционном импортере CPython. Если у вас есть специальный крючок или система импорта, я не гарантирую этого, но если вы его усыновите, довольно просто вернуться - вам просто нужно вручную добавить имена обратно в __all__
Так, например, в библиотеке утилиты вы должны определить декоратор:
import sys
def export(fn):
mod = sys.modules[fn.__module__]
if hasattr(mod, '__all__'):
mod.__all__.append(fn.__name__)
else:
mod.__all__ = [fn.__name__]
return fn
а затем, где вы определяете __all__
, вы делаете это:
$ cat > main.py
from lib import export
__all__ = [] # optional - we create a list if __all__ is not there.
@export
def foo(): pass
@export
def bar():
'bar'
def main():
print('main')
if __name__ == '__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' is not defined
$ 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' is not defined
Ответ 5
Он также изменяет то, что pydoc будет показывать:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$pydoc module1
Help on module module1: NAME module1 FILE module1.py DATA a = 'A' b = 'B' c = 'C'
$pydoc module2
Help on module module2: NAME module2 FILE module2.py DATA __all__ = ['a', 'b'] a = 'A' b = 'B'
Объявляю __all__
во всех моих модулях, а также подчеркивая внутренние детали, это действительно помогает при использовании вещей, которые вы никогда не использовали до этого в сеансах живого интерпретатора.
Ответ 6
Из (Неофициальный) Справочник по видам Python:
Публичные имена, определенные модулем, определяются путем проверки пространства имен модулей для переменной с именем
__all__
; если определено, это должна быть последовательность строк, которые являются именами, определенными или импортированными этим модулем. Имена, указанные в__all__
, считаются общедоступными и должны существовать. Если__all__
не определен, набор открытых имен включает все имена, найденные в пространстве имен модулей, которые не начинаются с символа подчеркивания ( "_" ).__all__
должен содержать весь открытый API. Он предназначен для того, чтобы избежать случайного экспорта элементов, которые не являются частью API (например, библиотечные модули, которые были импортированы и использованы в модуле).
Ответ 7
__all__
настраивает звездочку from <module> import *
__all__
настраивает звездочку from <package> import *
Модуль - это файл .py
предназначенный для импорта.
Пакет - это каталог с файлом __init__.py
. Пакет обычно содержит модули.
МОДУЛИ
""" cheese.py - an example module """
__all__ = ['swiss', 'cheddar']
swiss = 4.99
cheddar = 3.99
gouda = 10.99
__all__
позволяет людям знать "общедоступные" функции модуля. [ @AaronHall ] Кроме того, Pydoc распознает их. [ @Longpoke ]
из модуля импорта *
Посмотрите, как swiss
и cheddar
попадают в локальное пространство имен, но не gouda
:
>>> from cheese import *
>>> swiss, cheddar
(4.99, 3.99)
>>> gouda
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'gouda' is not defined
Без __all__
любой символ (который не начинается с подчеркивания) был бы доступен.
На импорт без *
не влияет __all__
модуль импорта
>>> import cheese
>>> cheese.swiss, cheese.cheddar, cheese.gouda
(4.99, 3.99, 10.99)
из имени модуля импорта
>>> from cheese import swiss, cheddar, gouda
>>> swiss, cheddar, gouda
(4.99, 3.99, 10.99)
импортировать модуль как локальное имя
>>> import cheese as ch
>>> ch.swiss, ch.cheddar, ch.gouda
(4.99, 3.99, 10.99)
ПАКЕТЫ
В файле __init__.py
пакета __all__
представляет собой список строк с именами открытых модулей или других объектов. Эти функции доступны для импорта по шаблону. Как и в случае с модулями, __all__
настраивает *
при импорте шаблона из пакета. [ @MartinStettner ]
Вот выдержка из Python MySQL Connector __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
Случай по умолчанию, звездочка без __all__
для пакета, сложен, потому что очевидное поведение будет дорого: использовать файловую систему для поиска всех модулей в пакете. Вместо этого при чтении документов импортируются только объекты, определенные в __init__.py
:
Если
__all__
не определено, операторfrom sound.effects import *
не импортирует все подмодули из пакетаsound.effects
в текущее пространство имен; он только гарантирует, что пакетsound.effects
был импортирован (возможно, выполняется любой код инициализации в__init__.py
), а затем импортирует любые имена, определенные в пакете. Это включает в себя любые имена, определенные (и явно загруженные подмодули) с помощью__init__.py
. Он также включает любые подмодули пакета, которые были явно загружены предыдущими операторами импорта.
Следует избегать импорта символов подстановки... поскольку они [запутывают] читателей и многие автоматизированные инструменты.
[ PEP 8, @ToolmakerSteve]
Ответ 8
Короткий ответ
__all__
влияет на __all__
from <module> import *
.
Длинный ответ
Рассмотрим этот пример:
foo
├── bar.py
└── __init__.py
В foo/__init__.py
:
-
(Неявный) Если мы не определим
__all__
, тоfrom foo import *
будет импортироваться только имена, определенные вfoo/__init__.py
. -
(Явный) Если мы определим
__all__ = []
, тоfrom foo import *
ничего не импортирует. -
(Явный) Если мы определим
__all__ = [ <name1>,... ]
, тоfrom foo import *
будут импортированы только эти имена.
Обратите внимание, что в неявном случае python не будет импортировать имена, начинающиеся с _
. Однако вы можете принудительно импортировать такие имена, используя __all__
.
Вы можете просмотреть документ Python здесь.
Ответ 9
__all__
используется для документирования открытого API модуля Python. Хотя это необязательно, следует использовать __all__
.
Вот соответствующий отрывок из ссылка на язык Python:
Публичные имена, определенные модулем, определяются путем проверки пространства имен модулей для переменной с именем
__all__
; если определено, это должна быть последовательность строк, которые являются именами, определенными или импортированными этим модулем. Имена, указанные в__all__
, считаются общедоступными и должны существовать. Если__all__
не определен, набор общедоступных имен включает все имена, найденные в пространстве имен модулей, которые не начинаются с символа подчеркивания ('_').__all__
должен содержать весь открытый API. Он предназначен для того, чтобы избежать случайного экспорта элементов, которые не являются частью API (например, библиотечные модули, которые были импортированы и использованы в модуле).
PEP 8 использует аналогичную формулировку, хотя он также дает понять, что импортированные имена не являются частью общедоступного API, когда __all__
отсутствует
Чтобы лучше поддерживать интроспекцию, модули должны явно объявлять имена в своем открытом API с помощью атрибута
__all__
. Установка__all__
в пустой список указывает, что модуль не имеет открытого API.[...]
Импортированные имена всегда должны рассматриваться как детализация реализации. Другие модули не должны полагаться на косвенный доступ к таким импортированным именам, если они не являются явно документированной частью содержащего API модуля, такого как
os.path
или модуль пакета__init__
, который предоставляет функции из подмодулей.
Кроме того, как указано в других ответах, __all__
используется для включения подстановочного импорта для пакетов:
Оператор import использует следующее соглашение: если код пакета
__init__.py
определяет список с именем__all__
, он считается списком имен модулей, которые должны быть импортированы, когда встречаетсяfrom package import *
.
Ответ 10
__all__
влияет на работу from ... import *
.
Код, который находится внутри тела модуля (не в теле функции или класса), может использовать звездочку (*
) в выражении from
:
from foo import *
*
требует, чтобы все атрибуты модуля foo
были связаны как глобальные переменные в импортирующем модуле. Когда foo
имеет атрибут __all__
, значением атрибута является список имен, связанных этим типом оператора from
. В противном случае этот тип оператора from
связывает все атрибуты foo
, кроме тех, которые начинаются с подчеркивания.
Обратите внимание, что __all__
не обязательно должен быть списком. Согласно документации по оператору import
, если он определен, __all__
должна быть последовательностью строк, которые являются именами, определенными или импортированными модулем. Таким образом, вы также можете использовать кортеж для сохранения некоторой памяти и циклов ЦП. Не забывайте запятую, если модуль определяет одно публичное имя:
__all__ = ('some_name',)
См. также . Почему "импорт *" плох?
Ответ 11
Это определено в PEP8 здесь:
Имена глобальных переменных
(Давайте надеяться, что эти переменные предназначены для использования только внутри одного модуля.) Соглашения примерно такие же, как и для функций.
Модули, разработанные для использования через
from M import *
, должны использовать механизм__all__
, чтобы предотвратить экспорт глобалов, или использовать старое соглашение о добавлении префиксов таких глобалов с подчеркиванием (что может потребоваться для указания того, что эти глобалы являются "модулем, не общественности ").
PEP8 предоставляет соглашения о кодировании для кода Python, включающего стандартную библиотеку в основном дистрибутиве Python. Чем больше вы будете следовать этому, тем ближе вы будете к первоначальному замыслу.