Может кто-нибудь объяснить __all__ в Python?
Я использую Python все больше и больше, и я постоянно вижу переменную __all__
установить в разных __init__.py
файлы. Может кто-нибудь объяснить, что это делает?
10 ответов
Это список открытых объектов этого модуля, как интерпретируется import *
, Он отменяет стандартное скрытие всего, что начинается с подчеркивания.
Связано, но явно не упомянуто здесь, именно тогда, когда __all__
используется. Это список строк, определяющих, какие символы в модуле будут экспортированы, когда 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/3.5/tutorial/modules.html
НОТА: __all__
влияет на from <module> import *
только поведение Члены, которые не упомянуты в __all__
все еще доступны снаружи модуля и могут быть импортированы с from <module> import <member>
,
Объясните __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
Из документов:
__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__
например, в module_1:
from foo_implementation import *
__all__ = ['foo']
И модуль_2 __init__.py
:
from Bar_implementation import *
__all__ = ['Bar']
И вы можете легко добавлять вещи в свой 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 экспортирует все имена, которые не начинаются с _
, Вы, конечно, можете положиться на этот механизм. Некоторые пакеты в стандартной библиотеке Python фактически полагаются на это, но для этого они используют псевдонимы, например, в импорте ctypes/__init__.py
:
import os as _os, sys as _sys
С использованием _
соглашение может быть более изящным, потому что оно удаляет избыточность именования имен снова. Но это добавляет избыточность для импорта (если у вас их много), и легко забыть делать это последовательно - и последнее, что вы хотите, - это бесконечно поддерживать то, что вы намеревались сделать только деталями реализации, просто потому что вы забыли поставить префикс _
при именовании функции.
Я лично пишу __all__
в начале моего жизненного цикла разработки модулей, чтобы другие, кто мог использовать мой код, знали, что им следует использовать, а не использовать.
Большинство пакетов в стандартной библиотеке также используют __all__
,
Избегая __all__
имеет смысл
Имеет смысл придерживаться _
префикс соглашения вместо __all__
когда:
- Вы все еще находитесь в режиме ранней разработки, у вас нет пользователей, и вы постоянно настраиваете свой API.
- Возможно, у вас есть пользователи, но у вас есть юнит-тесты, которые охватывают 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
Я просто добавляю это, чтобы быть точным:
Все остальные ответы относятся к модулям. Оригинальный вопрос явно упоминается __all__
в __init__.py
файлы, так что это о пакетах Python.
В общем-то, __all__
вступает в игру только тогда, когда from xxx import *
вариант import
заявление используется. Это относится как к пакетам, так и к модулям.
Поведение для модулей объясняется в других ответах. Точное поведение для пакетов подробно описано здесь.
Короче, __all__
на уровне пакета делает примерно то же самое, что и для модулей, за исключением того, что он работает с модулями в пакете (в отличие от указания имен внутри модуля). Так __all__
определяет все модули, которые должны быть загружены и импортированы в текущее пространство имен при использовании from package import *
,
Большая разница в том, что когда вы опускаете объявление __all__
в упаковке __init__.py
, заявление from package import *
вообще ничего не импортирует (с исключениями, объясненными в документации, см. ссылку выше).
С другой стороны, если вы опустите __all__
в модуле "импортированный по звездам" будет импортировать все имена (не начинающиеся с подчеркивания), определенные в модуле.
Это также меняет то, что покажет pydoc:
module1.py
a = "A"
b = "B"
c = "C"
module2.py
__all__ = ['a', 'b']
a = "A"
b = "B"
c = "C"
$ pydoc module1
Помощь по модулю module1:НАЗВАНИЕ module1ФАЙЛ module1.pyДАННЫЕ a = 'A' b = 'B' c = 'C'
$ pydoc module2
Помощь по модулю module2:НАЗВАНИЕ module2ФАЙЛ module2.pyДАННЫЕ __all__ = ['a', 'b'] a = 'A' b = 'B'
Я заявляю __all__
во всех моих модулях, а также подчеркивают внутренние детали, они действительно помогают при использовании вещей, которые вы никогда не использовали в сеансах прямого перевода.
__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 __init__.py
:
__all__ = [
'MySQLConnection', 'Connect', 'custom_error_exception',
# Some useful constants
'FieldType', 'FieldFlag', 'ClientFlag', 'CharacterSet', 'RefreshOption',
'HAVE_CEXT',
# Error handling
'Error', 'Warning',
...etc...
]
Следует избегать импорта подстановочных знаков, поскольку они [запутывают] читателей и многие автоматизированные инструменты.
[ PEP 8, @ToolmakerSteve]
Из (Неофициального) Python Reference Wiki:
Публичные имена, определенные модулем, определяются путем проверки пространства имен модуля на наличие переменной с именем
__all__
; если определено, это должна быть последовательность строк, имена которых определены или импортированы этим модулем. Имена даны в__all__
все считаются публичными и должны существовать. Если__all__
не определен, набор открытых имен включает в себя все имена, найденные в пространстве имен модуля, которые не начинаются с символа подчеркивания ("_").__all__
должен содержать весь публичный API. Он предназначен для того, чтобы избежать случайного экспорта элементов, которые не являются частью API (таких как библиотечные модули, которые были импортированы и использованы внутри модуля).
Короткий ответ
__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 здесь.
__all__
используется для документирования публичного API модуля Python. Хотя это необязательно, __all__
должен быть использован.
Вот соответствующая выдержка из ссылки на язык Python:
Публичные имена, определенные модулем, определяются путем проверки пространства имен модуля на наличие переменной с именем
__all__
; если определено, это должна быть последовательность строк, имена которых определены или импортированы этим модулем. Имена даны в__all__
все считаются публичными и должны существовать. Если__all__
не определен, набор открытых имен включает в себя все имена, найденные в пространстве имен модуля, которые не начинаются с символа подчеркивания ('_').__all__
должен содержать весь публичный API. Он предназначен для того, чтобы избежать случайного экспорта элементов, которые не являются частью API (таких как библиотечные модули, которые были импортированы и использованы внутри модуля).
PEP 8 использует аналогичную формулировку, хотя также дает понять, что импортированные имена не являются частью общедоступного API, когда __all__
отсутствует:
Чтобы лучше поддерживать интроспекцию, модули должны явно объявлять имена в своем публичном API, используя
__all__
приписывать. настройка__all__
пустой список означает, что модуль не имеет публичного API.[...]
Импортированные имена всегда должны рассматриваться как детали реализации. Другие модули не должны полагаться на косвенный доступ к таким импортированным именам, если они не являются явно документированной частью API содержащего модуля, такой как
os.path
или пакет__init__
модуль, который предоставляет функциональность из подмодулей.
Кроме того, как указано в других ответах, __all__
используется для включения подстановочного импорта для пакетов:
Оператор импорта использует следующее соглашение: если пакет
__init__.py
Код определяет список с именем__all__
, он считается списком имен модулей, которые следует импортировать приfrom package import *
встречается.
В дополнение к существующим ответам, __all__
не должен быть списком. Согласно документации на import
заявление, если оно определено, __all__
должна быть последовательностью строк, имена которых определены или импортированы модулем. Таким образом, вы также можете использовать кортеж для экономии памяти и циклов ЦП. Только не забудьте запятую, если модуль определяет одно публичное имя:
__all__ = ('some_name',)
This is defined in PEP8 here:
Global Variable Names
(Let's hope that these variables are meant for use inside one module only.) The conventions are about the same as those for functions.
Modules that are designed for use via
from M import *
should use the__all__
mechanism to prevent exporting globals, or use the older convention of prefixing such globals with an underscore (which you might want to do to indicate these globals are "module non-public").
PEP8 provides coding conventions for the Python code comprising the standard library in the main Python distribution. The more you follow this, closer you are to the original intent.