Чи може хтось пояснити __all__ в Python?


982

Я все більше і більше використовую Python, і я постійно бачу __all__набір змінних у різних __init__.pyфайлах. Хтось може пояснити, що це робить?

Відповіді:


565

Це список публічних об'єктів цього модуля, як їх інтерпретують 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 *. Я думаю, що практика прийнята через інерційність, а не навмисну ​​конструкцію.
чепнер

Як зазначає 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
Джон Коул

223

Поясніть __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']
""")

А тепер у вас все ще передбачена програма на рівні пакету:

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

Ви можете легко додати речі до свого 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.
  • Можливо, у вас є користувачі, але у вас є одиничні тести, що охоплюють 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
Перехресне посилання: У вашій відповіді на питання про те, як написати @exportдекоратор, я згадував вашого декоратора.
MvG

13
Це однозначно було найбільш корисною відповіддю, яку я бачив у тому, щоб допомогти відносно новому розробнику python зрозуміти процес імпорту модулів / пакетів __init__.pyта використання__all__
Brett Reinhard

Це мені дуже допомагає. Моя проблема, тхо, полягає в тому, що підмодулі, які я хочу імпортувати, - це всі генеровані файли з великою кількістю чіткостей у своїх символах, які я хотів би викреслити, не вручну переконуючись у __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__модуль, "імпорт із зіркою" імпортує всі імена (не починаючи з підкреслення), визначені в модулі.


29
from package import *буде імпортувати все, що визначено в __init__.py, навіть якщо його немає all. Важлива відмінність полягає в тому, що без __all__нього не буде автоматично імпортуватися жоден модуль, визначений в каталозі пакета.
Nikratio

Коли все містить [foo, bar] і у файлі test.py, якщо ми використовуємо: з імпорту пакета *, чи foo і bar імпортуються в локальний простір імен test.py або у власний простір імен?
змінна

87

Це також змінює те, що підок покаже:

module1.py

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

module2.py

__all__ = ['a', 'b']

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

$ pydoc модуль1

Довідка по модулю модуля1:

ІМ’Я
    модуль1

ФАЙЛ
    module1.py

DATA 
    a = 'A'
     b = 'B'
     c = 'C'

$ pydoc модуль2

Довідка по модулю module2:

ІМ’Я
    модуль2

ФАЙЛ
    module2.py

ДАНІ 
    __all__ = ['a', 'b']
     a = 'A'
     b = 'B'

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


54

__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 ] Також підок розпізнає їх. [ @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...

    ]

Випадок за замовчуванням, зірочка без __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тому , що це НЕ імпортувати будь-який з модулів .
radzak

Дякую @Jatimir, я уточнив як можна краще без експериментів. Я майже хотів сказати, що цей випадок (зірочка без усіх для пакета) поводиться так само, як якщо __init__.pyб модуль . Але я не впевнений, що це точно, або, особливо, якщо викреслені об'єкти з попереднім підкресленням виключені. Також я більш чітко розділив розділи про МОДУЛИ та ПАКЕТИ. Ваші думки?
Боб Штейн

49

Від (Неофіційний) довідник Python 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 тут .


5

__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. Чим більше ви дотримуєтесь цього, тим ближче до початкового наміру.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.