Вопрос

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

Это было полезно?

Решение

Это список общедоступных объектов этого модуля, как его интерпретирует 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/tutorial/modules.html#importing-from-a-package

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

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

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

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

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

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

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

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

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

В простейшем случае __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']

И модуль_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()

но если они об этом не знают, другие инструменты (например, пидок) не сообщит им.

Вы можете позже изменить это, когда 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

Это также меняет то, что будет показывать pydoc:

модуль1.py

a = "A"
b = "B"
c = "C"

модуль2.py

__all__ = ['a', 'b']

a = "A"
b = "B"
c = "C"

$ pydoc модуль1

Help on module module1:

ИМЯ
    module1

ФАЙЛ
    module1.py

ДАННЫЕ
    а = 'A'
    б = 'B'
    с = 'C'

$pydoc модуль2

Help on module module2:

ИМЯ
    module2

ФАЙЛ
    module2.py

ДАННЫЕ
    __все__ = ['a', 'b']
    а = 'A'
    б = 'B'

Я заявляю __all__ во всех моих модулях, а также подчеркивают внутренние детали, они действительно помогают при использовании вещей, которые вы никогда раньше не использовали в сеансах живого устного перевода.

От (Неофициальная) Справочная вики по Python:

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

__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__ позволяет людям узнать «публичные» особенности модуль.[@АаронХолл] Кроме того, pydoc их распознает.[@Лонгпоук]

от модуль Импортировать *

Смотри как 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__ настраивает * при импорте подстановочных знаков из пакета.[@МартинСтеттнер]

Вот отрывок из MySQL-коннектор Python __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.Он также включает в себя все подмодули пакета, которые были явно загружены предыдущими операторами импорта.


Импорт подстановочных знаков...следует избегать, поскольку они [сбивают с толку] читателей и многие автоматизированные инструменты.

[ПКП 8, @ToolmakerSteve]

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

__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 (например, библиотечных модулей, которые были импортированы и использованы внутри модуля).

ПКП 8 использует аналогичную формулировку, хотя также ясно дает понять, что импортированные имена не являются частью общедоступного API, когда __all__ отсутствует:

Чтобы лучше поддерживать самоанализ, модули должны явно объявлять имена в своем общедоступном API, используя метод __all__ атрибут.Параметр __all__ пустой список означает, что у модуля нет общедоступного API.

[...]

Импортированные имена всегда следует рассматривать как деталь реализации.Другие модули не должны полагаться на косвенный доступ к таким импортированным именам, если только они не являются явно документированной частью API содержащего модуля, например: os.path или посылка __init__ модуль, который предоставляет функциональность подмодулей.

Кроме того, как указано в других ответах, __all__ используется для включения импорт подстановочных знаков для пакетов:

В операторе импорта используется следующее соглашение:если посылка __init__.py код определяет список с именем __all__, это список имен модулей, которые должны быть импортированы при from package import * встречается.

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

__all__ = ('some_name',)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top