Может кто-нибудь объяснить __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.

Другие вопросы по тегам