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

983

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

varikin
источник

Ответы:

566

Это список открытых объектов этого модуля в интерпретации import *. Он отменяет стандартное скрытие всего, что начинается с подчеркивания.

Джимми
источник
146
Объекты, которые начинаются с подчеркивания или не упоминаются в том __all__случае, если они __all__присутствуют, не совсем скрыты; они могут быть замечены и доступны совершенно нормально, если вы знаете их имена. Только в случае «импорта *», который в любом случае не рекомендуется, различие имеет какой-либо вес.
Брэндон Роудс
28
@BrandonRhodes: это тоже не совсем так: рекомендуется импортировать только те модули, для которых, как вы знаете, они предназначены import *(например, например tk). Хорошей подсказкой, если это так, является наличие __all__или имена, начинающиеся с подчеркивания в коде модуля.
летающие овцы
12
Открытый и внутренний интерфейсы - python.org/dev/peps/pep-0008/#id50 , чтобы лучше поддерживать самоанализ, модули должны явно объявлять имена в своем публичном API с помощью атрибута __all__. Установка __all__ в пустой список означает, что модуль не имеет публичного API.
отладка
Я не уверен, что, если бы они tkбыли выпущены сегодня (или даже в 2012 году), рекомендуемой практикой будет использование from tk import *. Я думаю, что практика принята из-за инерции, а не преднамеренного дизайна.
Chepner
Как указывает BrandonRhodes, это действительно не правильно
duhaime
947

Связано, но здесь явно не упомянуто, именно тогда, когда __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/tutorial/modules.html#importing-from-a-package

ПРИМЕЧАНИЕ: __all__ влияет from <module> import *только на поведение. Члены, которые не упомянуты, по- __all__прежнему доступны извне модуля и могут быть импортированы с помощью from <module> import <member>.

Алек Томас
источник
1
мы не должны печатать базу как print(baz())?
Джон Коул
@JohnCole baz - это объект функции, а baz () запустит объект функции
Bhanu Tez
@BhanuTez точно. Так print(baz)печатает что-то вроде <function baz at 0x7f32bc363c10>тогдашних print(baz())печатных изданийbaz
Джон Коул
225

Объясните __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
├── module_1.py
└── module_2.py

Давайте создадим эти файлы с помощью Python, чтобы вы могли следить за ними - вы можете вставить следующее в оболочку Python 3:

from pathlib import Path

package = Path('package')
package.mkdir()

(package / '__init__.py').write_text("""
from .module_1 import *
from .module_2 import *
""")

package_module_1 = package / 'module_1.py'
package_module_1.write_text("""
__all__ = ['foo']
imp_detail1 = imp_detail2 = imp_detail3 = None
def foo(): pass
""")

package_module_2 = package / 'module_2.py'
package_module_2.write_text("""
__all__ = ['Bar']
imp_detail1 = imp_detail2 = imp_detail3 = None
class Bar: pass
""")

И теперь вы представили полный API, который кто-то другой может использовать при импорте вашего пакета, например:

import package
package.foo()
package.Bar()

И в пакете не будет всех других деталей реализации, которые вы использовали при создании ваших модулей, загромождающих packageпространство имен.

__all__ в __init__.py

После дополнительной работы, может быть, вы решили, что модули слишком большие (например, много тысяч строк?) И должны быть разделены. Итак, вы делаете следующее:

package
├── __init__.py
├── module_1
│   ├── foo_implementation.py
│   └── __init__.py
└── module_2
    ├── Bar_implementation.py
    └── __init__.py

Сначала создайте каталоги подпакетов с теми же именами, что и у модулей:

subpackage_1 = package / 'module_1'
subpackage_1.mkdir()
subpackage_2 = package / 'module_2'
subpackage_2.mkdir()

Переместите реализации:

package_module_1.rename(subpackage_1 / 'foo_implementation.py')
package_module_2.rename(subpackage_2 / 'Bar_implementation.py')

создать __init__.pys для подпакетов, которые объявляют __all__для каждого:

(subpackage_1 / '__init__.py').write_text("""
from .foo_implementation import *
__all__ = ['foo']
""")
(subpackage_2 / '__init__.py').write_text("""
from .Bar_implementation import *
__all__ = ['Bar']
""")

И теперь у вас все еще есть API, предоставляемый на уровне пакета:

>>> import package
>>> package.foo()
>>> package.Bar()
<package.module_2.Bar_implementation.Bar object at 0x7f0c2349d210>

И вы можете легко добавлять вещи в свой 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
Аарон Холл
источник
1
Перекрестная ссылка: Я упомянул ваш декоратор в этом CW-ответе на вопрос, как написать @exportдекоратор.
MvG
13
В одиночку это был самый полезный ответ, который я видел в том, чтобы помочь относительно новому разработчику на python понять процесс импорта модулей / пакетов __init__.pyи использования__all__
Бретт Рейнхард
Это мне очень помогает. Моя проблема заключается в том, что все подмодули, которые я хочу импортировать, представляют собой сгенерированные файлы с большим количеством ошибок в их символах, которые я хотел бы вырезать, без необходимости вручную проверять, что __all__это правильно.
Майк C
@MikeC тогда, может быть, вы __all__тоже должны сгенерировать свой - но тогда я бы сказал, что у вас нестабильный API ... Это было бы что-то для проведения всесторонних приемочных тестов.
Аарон Холл
@AaronHall "у них не будет всех других имен ... загромождающих пространство имен пакета" Но у них будут имена module_1и module_2; нормально ли включать явное del module_1в __init__.py? Я ошибаюсь, если думаю, что это стоит того?
Майк C
176

Я просто добавляю это, чтобы быть точным:

Все остальные ответы относятся к модулям . Оригинальный вопрос явно упоминается __all__в __init__.pyфайлах, так что речь идет о пакетах Python .

Как правило, __all__вступает в действие только при использовании from xxx import *варианта importоператора. Это относится как к пакетам, так и к модулям.

Поведение модулей объясняется в других ответах. Точное поведение для пакетов подробно описано здесь .

Короче говоря, __all__на уровне пакета выполняется примерно то же самое, что и для модулей, за исключением того, что он работает с модулями в пакете (в отличие от указания имен внутри модуля ). Так __all__указывает все модули, которые должны быть загружены и импортированы в текущее пространство имен при использовании from package import *.

Большая разница в том, что когда вы опускаете объявление __all__в пакете __init__.py, оператор from package import *вообще ничего не импортирует (с исключениями, объясненными в документации, см. Ссылку выше).

С другой стороны, если вы опустите __all__в модуле, «импортированный по звездам» будет импортировать все имена (не начинающиеся с подчеркивания), определенные в модуле.

MartinStettner
источник
29
from package import *будет по-прежнему импортировать все, что определено в __init__.py, даже если нет all. Важным отличием является то, что без __all__него не будут автоматически импортироваться какие-либо модули, определенные в каталоге пакета.
Никратио
Когда все содержит [foo, bar] и в файле test.py, если мы используем: из импорта пакета *, тогда импортируются ли foo и bar в локальное пространство имен test.py или в собственное пространство имен foo и bars?
переменная
87

Это также меняет то, что 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__во всех моих модулях, а также подчеркиваю внутренние детали, они действительно помогают при использовании вещей, которые вы никогда раньше не использовали в сеансах прямого перевода.

L̳o̳̳n̳̳g̳̳p̳o̳̳k̳̳e̳̳
источник
55

__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]

Боб Стейн
источник
2
Мне очень нравится этот ответ, но я не хватает информации о том , что это поведение по умолчанию , from <package> import *не __all__в __init__.pyтом , что это не импортировать любой из модулей .
Радзак
Спасибо @Jatimir, я уточнил как мог без проведения экспериментов. Я почти хотел сказать, что этот случай (звездочка без всего для пакета) ведет себя так же, как если бы __init__.pyэто был модуль . Но я не уверен, что это точно, или, в частности, если объекты с префиксом подчеркивания исключены. Кроме того, я более четко разделил разделы на МОДУЛИ и ПАКЕТЫ. Твои мысли?
Боб Стейн
49

Из (Неофициального) Python Reference Wiki :

Публичные имена, определенные модулем, определяются путем проверки пространства имен модуля на наличие имени __all__; если определено, это должна быть последовательность строк, имена которых определены или импортированы этим модулем. Приведенные имена __all__являются общедоступными и должны существовать. Если __all__не определено, набор открытых имен включает все имена, найденные в пространстве имен модуля, которые не начинаются с символа подчеркивания («_»). __all__должен содержать весь публичный API. Он предназначен для предотвращения случайного экспорта элементов, которые не являются частью API (таких как библиотечные модули, которые были импортированы и использованы внутри модуля).

Лассе В. Карлсен
источник
Указанная ссылка не работает. но дословно нашел текст на vdocuments.net/… & здесь: dokumen.tips/documents/reference-567bab8d6118a.html
JayRizzo
8

__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 *обнаружении.

Михай Капотэ
источник
8

Короткий ответ

__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 здесь .

Cyker
источник
6

__all__влияет как from foo import *работает.

Код, который находится внутри тела модуля (но не в теле функции или класса), может использовать звездочку ( *) в fromвыражении:

from foo import *

Эти *запросы , что все атрибуты модуля foo( за исключением тех , кто начинает с подчеркиванием) быть связаны как глобальные переменные в импортирующей модуле. Когда fooесть атрибут __all__, значением атрибута является список имен, которые связаны с этим типом fromоператора.

Если fooэто пакет, и он __init__.pyопределяет список __all__имен, он считается списком имен подмодулей, которые следует импортировать при from foo import *обнаружении. Если __all__не определено, оператор from foo import *импортирует любые имена, определенные в пакете. Это включает любые имена, определенные (и явно загруженные подмодули) __init__.py.

Обратите внимание, что __all__это не обязательно должен быть список. Согласно документации по importинструкции , если она определена, __all__должна быть последовательность строк , имена которых определены или импортированы модулем. Таким образом, вы также можете использовать кортеж для экономии памяти и циклов ЦП. Не забывайте запятую, если модуль определяет одно публичное имя:

__all__ = ('some_name',)

Смотрите также Почему «импорт *» плох?

Евгений Ярмаш
источник
1

Это определено в PEP8 здесь :

Глобальные имена переменных

(Будем надеяться, что эти переменные предназначены для использования только внутри одного модуля.) Соглашения примерно такие же, как и для функций.

Модули, разработанные для использования через, from M import *должны использовать __all__механизм, предотвращающий экспорт глобальных файлов, или использовать более старую конвенцию, заключающуюся в добавлении префиксов для таких глобальных символов с подчеркиванием (что может потребоваться для указания того, что эти глобальные переменные являются «непубличными модулями»).

PEP8 предоставляет соглашения о кодировании для кода Python, включающего стандартную библиотеку в основном дистрибутиве Python. Чем больше вы будете следовать этому, тем ближе вы будете к первоначальному замыслу.

прости
источник