[python] 누군가 파이썬에서 __all__을 설명 할 수 있습니까?

파이썬을 점점 더 많이 사용하고 __all__있으며 다른 __init__.py파일에 변수 세트가 계속 표시 됩니다. 누군가 이것이 무엇을하는지 설명 할 수 있습니까?



답변

로 해석되는 해당 모듈의 공용 객체 목록입니다 import *. 밑줄로 시작하는 모든 항목을 숨기는 기본값을 무시합니다.


답변

여기에 명시 적으로 언급되지 않은 링크는 정확히 언제 __all__사용 되는지 입니다. 모듈에서 from <module> import *사용될 때 어떤 심볼을 내보낼 지 정의하는 문자열 목록입니다 .

예를 들어, 다음 코드 foo.py를 명시 적으로는 기호 수출 barbaz:

__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__을 설명 하시겠습니까?

__all__다른 __init__.py파일에 변수 세트가 계속 표시 됩니다.

이것은 무엇을 하는가?

무엇을 __all__합니까?

모듈에서 의미 적으로 “공용”이름을 선언합니다. 에 이름이 있으면 __all__사용자 는 해당 이름 을 사용해야하며 변경되지 않을 것으로 기대할 수 있습니다.

또한 프로그래밍 방식으로 영향을 미칩니다.

import *

__all__모듈에서, 예 module.py:

__all__ = ['foo', 'Bar']

수단이 때 import *모듈에서의의 이름 만이 __all__가져올 수 있습니다 :

from module import *               # imports foo and Bar

문서화 도구

문서 및 코드 자동 완성 도구는 __all__모듈에서 사용 가능한 것으로 표시되는 이름을 결정하기 위해 실제로 검사해야 할 수도 있습니다.

__init__.py 디렉토리를 파이썬 패키지로 만든다

로부터 문서 :

__init__.py파일은 파이썬 치료 패키지가 들어있는 같은 디렉토리를 만들기 위해 필요합니다; 이는 문자열과 같은 공통 이름을 가진 디렉토리가 나중에 모듈 검색 경로에서 발생하는 유효한 모듈을 실수로 숨기지 않도록하기 위해 수행됩니다.

가장 간단한 경우 __init__.py빈 파일 일 수 있지만 패키지의 초기화 코드를 실행하거나 __all__변수를 설정할 수도 있습니다.

그래서이 __init__.py을 선언 할 수 있습니다 __all__A의 패키지 .

API 관리

패키지는 일반적으로 서로 가져올 수 있지만 반드시 __init__.py파일 과 함께 묶인 모듈로 구성 됩니다. 이 파일은 디렉토리를 실제 파이썬 패키지로 만듭니다. 예를 들어, 패키지에 다음 파일이 있다고 가정하십시오.

package
├── __init__.py
├── module_1.py
└── module_2.py

파이썬으로이 파일들을 만들어 보도록하겠습니다. 다음을 파이썬 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__.py선언하는 서브 패키지에 대해 s를 작성 하십시오 __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은로 시작하지 않는 모든 이름을 내 보냅니다 _. 확실히이 메커니즘에 의존 할 수 있습니다. 파이썬 표준 라이브러리의 일부 패키지는, 사실, 이에 의존하지만, 예를 들어, 자신의 수입 별명 그래서 그들은을 위해 ctypes/__init__.py:

import os as _os, sys as _sys

_규칙을 사용하면 이름을 다시 지정하는 중복성이 제거되므로 더 우아 할 수 있습니다. 그러나 수입에 대한 중복성을 추가하고 (많은 수입이있는 경우) 이 일관된 작업을 잊어 버리기 쉽습니다 . 마지막으로 원하는 것은 구현 세부 사항 만 의도 한 것을 무기한으로 지원해야합니다. _함수 이름 을 지정할 때 접두사를 잊어 버렸기 때문 입니다.

나는 __all__내 코드를 사용하는 다른 사람들이 사용하고 사용하지 않아야 할 것을 알 수 있도록 개발 라이프 사이클 초기에 모듈에 대한 초기 글을 씁니다 .

표준 라이브러리에있는 대부분의 패키지도 사용 __all__합니다.

피하면 __all__차종의 의미

다음 _대신에 접두사 규칙 을 따르는 것이 합리적 입니다 __all__.

  • 아직 초기 개발 모드에 있으며 사용자가 없으며 지속적으로 API를 조정하고 있습니다.
  • 어쩌면 사용자가 있지만 API를 다루는 단위 테스트가 있으며 여전히 API에 적극적으로 추가하고 개발을 조정하고 있습니다.

export장식

사용의 단점은 __all__두 번 내보내는 함수 및 클래스 이름을 작성해야하며 정보는 정의와 별도로 유지된다는 것입니다. 우리는 이 문제를 해결하기 위해 장식을 사용합니다.

나는 David Beazley의 포장에 관한 이야기에서 그런 수출 데코레이터에 대한 아이디어를 얻었다. 이 구현은 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이므로 파이썬 패키지 에 관한 것 입니다.

일반적으로 명세서 __all__from xxx import *변형 import이 사용될 때만 작동합니다 . 이것은 패키지뿐만 아니라 모듈에도 적용됩니다.

모듈의 동작은 다른 답변에 설명되어 있습니다. 패키지의 정확한 동작은 여기 에 자세히 설명되어 있습니다 .

간단히 말해서 __all__패키지 레벨에서 모듈 내에서 이름 을 지정하는 것과 달리 패키지 내 모듈 을 처리한다는 점을 제외하면 모듈과 거의 동일합니다 . 따라서 __all__우리가 사용할 때 현재 네임 스페이스로로드 및 임포트 될 모든 모듈을 지정합니다 from package import *.

가장 큰 차이점은 패키지에서 의 선언 을 생략 하면 명령문 이 아무것도 가져 오지 않는다는 것입니다 (문서에 설명 된 예외는 위의 링크 참조).__all____init__.pyfrom package import *

반면에 __all__모듈에서 생략 하면 “별표 가져 오기”는 모듈에 정의 된 모든 이름 (밑줄로 시작하지 않음)을 가져옵니다.


답변

또한 pydoc이 표시 할 내용을 변경합니다.

module1.py

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

module2.py

__all__ = ['a', 'b']

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

pydoc 모듈 1

모듈 module1에 대한 도움말 :

이름
    모듈 1

파일
    module1.py

DATA
     = 'A'
     , B = 'B'
     C = 'C'

pydoc 모듈 2

모듈 module2에 대한 도움말 :

이름
    모듈 2

파일
    module2.py

DATA 
    __all__ = 'A', 'B']
     = 'A'
     , B = 'B'

나는 __all__내 모든 세부 사항뿐만 아니라 밑줄 내부 세부 사항을 선언 합니다. 이것은 실시간 통역 세션에서 전에 사용하지 않은 것을 사용할 때 실제로 도움이됩니다.


답변

__all__사용자 정의 *from <module> import *

__all__사용자 정의 *from <package> import *


모듈 A는 .py가져올 수 의미 파일.

패키지는 기호가있는 디렉토리 __init__.py파일입니다. 패키지는 일반적으로 모듈을 포함합니다.


모듈

""" cheese.py - an example module """

__all__ = ['swiss', 'cheddar']

swiss = 4.99
cheddar = 3.99
gouda = 10.99

__all__인간에게 모듈 의 “공개”기능을 알려줍니다 . [ @AaronHall ] 또한 pydoc 은이를 인식합니다. [ @ 롱 포크 ]

에서 모듈 가져 오기 *

방법 swisscheddar로컬 네임 스페이스로 가져 오는 방법을 참조하십시오 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)

수입 모듈의 localName

>>> 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. 또한 이전 import 문으로 명시 적으로로드 된 패키지의 하위 모듈도 포함합니다.


와일드 카드 가져 오기는 독자와 많은 자동화 도구를 혼동하기 때문에 피해야합니다.

[ PEP 8 , @ToolmakerSteve]


답변

에서 (비공식) 파이썬 레퍼런스 위키 :

모듈이 정의한 공개 이름은 모듈의 네임 스페이스를 변수 이름으로 확인하여 결정됩니다 __all__. 정의 된 경우 해당 모듈에서 정의하거나 가져온 이름 인 일련의 문자열이어야합니다. 이름 __all__은 모두 공개로 간주되며 존재해야합니다. __all__이 정의되지 않은 경우 공개 이름 세트에는 밑줄 문자 ( “_”)로 시작하지 않는 모듈 네임 스페이스에있는 모든 이름이 포함됩니다. __all__전체 공개 API를 포함해야합니다. API에 포함되지 않은 항목 (예 : 모듈 내에서 가져 와서 사용 된 라이브러리 모듈)을 실수로 내 보내지 않도록합니다.