Может кто-нибудь объяснить __all__ в Python?

Я использую Python все больше и больше вижу переменную __all__ в разных файлах __init__.py. Может кто-нибудь объяснить, что это делает?

Ответ 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. Чем больше вы будете следовать этому, тем ближе вы будете к первоначальному замыслу.