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



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

622   5  

5 ответов:

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

Примечание:__all__ влияет

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

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

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

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

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

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

С другой стороны, если не указывать __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 делает a каталог пакета Python

с docs:

The __init__.py файлы необходимы для того, чтобы Python рассматривал каталоги как содержащие пакеты; это делается для предотвращения непреднамеренного скрытия допустимых модулей, которые встречаются позже в пути поиска модуля, в каталогах с общим именем, например string.

в простейшем случае __init__.py может быть просто пустой файл, но он также может выполнять код инициализации упакуйте или установите __all__ переменной.

так __init__.py может объявить __all__ на пакета.

управление API:

пакет обычно состоит из модулей, которые могут импортировать друг друга, но которые обязательно связаны вместе с . Этот файл-это то, что делает каталог фактическим пакетом 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__ in __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']

и module_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, по сути, do полагайтесь на это, но для этого они псевдонимы их импорта, например, в ctypes/__init__.py:

import os as _os, sys as _sys

с помощью _ конвенция может быть более элегантным потому что это устраняет избыточность именования имен снова. Но он добавляет избыточность для импорта (если у вас их много) и это легко чтобы забыть сделать это последовательно - и последнее, что вы хотите, чтобы бесконечно поддерживать то, что вы намеревались быть только деталь реализации, только потому, что вы забыли префикс _ при вводе имени функции.

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

большинство пакетов в стандартной библиотеке также использовать __all__.

во избежание __all__ смысл

имеет смысл придерживаться _ префикс конвенции вместо __all__ когда:

  • вы все еще находитесь в режиме ранней разработки и не имеете пользователей, и постоянно настраиваете свой API.
  • может быть, у вас есть пользователи, но у вас есть unittests, которые охватывают API, и вы все еще активно добавляете в API и настраиваете в разработке.

An 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 provisioning с 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 покажет:

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

Comments

    Ничего не найдено.