etc./StackOverFlow

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

청렴결백한 만능 재주꾼 2023. 4. 26. 12:46
반응형

질문자 :varikin


저는 Python을 점점 더 많이 사용하고 __init__.py __all__ 변수가 계속 설정되어 있는 것을 볼 수 있습니다. 누군가 이것이 무엇을하는지 설명 할 수 있습니까?



링크되었지만 여기에 명시적으로 언급되지 않은 것은 정확히 __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> 로 가져올 수 있습니다.


Alec Thomas

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


Jimmy

파이썬으로 모든 것을 설명하시겠습니까?

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

이것은 무엇을합니까?

__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 네임스페이스를 어지럽히는 모듈을 만들 때 사용한 다른 모든 구현 세부 정보가 없습니다.

__init__.py __all__

더 많은 작업을 수행한 후 모듈이 너무 커서(예: 수천 줄?) 분할해야 한다고 결정했을 수 있습니다. 따라서 다음을 수행합니다.

 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')

각각에 대해 __all__ 을 선언하는 하위 패키지에 대해 __init__.py 만듭니다.

 (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']

그리고 최상위 API에 Baz 를 게시할 준비가 되지 않았다면 __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

import * 를 사용한 API 프로비저닝도 작동합니다.

 $ 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

Aaron Hall

나는 이것을 정확하게 추가하고 있습니다.

다른 모든 답변은 모듈을 참조합니다. 원래 질문은 __init__.py __all__ 을 명시적으로 언급했으므로 이것은 python packages 에 관한 것입니다.

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

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

간단히 말해서, __all__ 은 패키지 내의 모듈을 처리한다는 점을 제외하고는 모듈과 거의 동일한 작업을 수행합니다(모듈 내에서 이름 을 지정하는 것과 대조적으로). 따라서 __all__ from package import * 사용할 때 현재 네임스페이스로 로드 및 가져올 모든 모듈을 지정합니다.

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

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


MartinStettner

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

module1.py

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

모듈2.py

 __all__ = ['a', 'b'] a = "A" b = "B" c = "C"

$ 파이독 모듈1

모듈 module1에 대한 도움말:

이름
    모듈1

파일
    module1.py

데이터
    a = '아'
    b = '비'
    c = 'C'

$ 파이독 모듈2

모듈 module2에 대한 도움말:

이름
    모듈2

파일
    모듈2.py

데이터
    __all__ = ['a', 'b']
    a = '아'
    b = '비'

__all__ 을 선언하고 내부 세부 사항에 밑줄을 긋습니다. 이는 라이브 인터프리터 세션에서 이전에 사용하지 않은 것을 사용할 때 정말 도움이 됩니다.


L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

__all__ 지정 -은 *from <module> import *from <package> import * .


모듈 은 가져 .py 위한 .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)

모듈로컬 이름으로 가져오기

 >>> 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 문에 의해 명시적으로 로드된 패키지의 모든 하위 모듈도 포함됩니다.


그리고 마지막으로, 스택 오버플로 답변, 교수, 사방 mansplainers에 대한 숭배의 전통은, 처음에 질문을위한 비난의 봉 경구이다 :

와일드카드 가져오기는 ... 독자와 많은 자동화 도구를 [혼동]시키므로 피해야 합니다.

[ PEP 8 , @ToolmakerSteve ]


Bob Stein

(비공식) Python 참조 Wiki에서 :

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


Lasse V. Karlsen

짧은 답변

__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 * 는 해당 이름만 가져옵니다.

_ 시작하는 이름을 가져오지 않습니다. __all__ 사용하여 이러한 이름을 강제로 가져올 수 있습니다.

여기 에서 Python 문서를 볼 수 있습니다.


Cyker

__all__ 은 Python 모듈의 공개 API를 문서화하는 데 사용됩니다. 선택 사항이지만 __all__ 사용해야 합니다.

다음은 Python 언어 참조 에서 발췌한 관련 내용입니다.

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

PEP 8 __all__ 이 없을 때 가져온 이름이 공개 API의 일부가 아님을 분명히 합니다.

__all__ 속성을 사용하여 공개 API에서 이름을 명시적으로 선언해야 합니다. __all__ 을 빈 목록으로 설정하면 모듈에 공개 API가 없음을 나타냅니다.

[...]

가져온 이름은 항상 구현 세부 사항으로 간주되어야 합니다. os.path 또는 하위 모듈의 기능을 노출하는 패키지의 __init__ 모듈과 같이 포함하는 모듈 API의 명시적으로 문서화된 부분이 아닌 한 이러한 가져온 이름에 대한 간접 액세스에 의존해서는 안 됩니다.

또한 다른 답변에서 지적했듯이 __all__은 패키지에 대한 와일드 카드 가져오기 를 활성화하는 데 사용됩니다.

import 문은 다음 규칙을 사용합니다. 패키지의 __init__.py __all__ 이라는 목록을 정의 from package import * 가 발생할 때 가져와야 하는 모듈 이름 목록으로 간주됩니다.


Mihai Capotă

__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__ 은 모듈에서 정의하거나 가져온 이름 인 문자열 시퀀스 여야 합니다. 따라서 튜플을 사용하여 메모리와 CPU 주기 를 절약할 수도 있습니다. 모듈이 단일 공개 이름을 정의하는 경우 쉼표를 잊지 마십시오.

 __all__ = ('some_name',)

"가져오기 *"가 나쁜 이유는 무엇입니까? 도 참조하십시오.


Eugene Yarmash

이것은 PEP8에 정의되어있습니다 .

전역 변수 이름

(이 변수들이 하나의 모듈 내에서만 사용되기를 바랍니다.) 규칙은 함수에 대한 규칙과 거의 같습니다.

from M import * 를 통해 사용하도록 설계된 모듈 __all__ 메커니즘을 사용하여 전역을 내보내는 것을 방지하거나 이러한 전역에 밑줄을 접두사로 사용하는 이전 규칙을 사용해야 합니다(이러한 전역이 "module non-public ").

PEP8은 기본 Python 배포판의 표준 라이브러리를 구성하는 Python 코드에 대한 코딩 규칙을 제공합니다. 이것을 더 많이 따를수록 원래 의도에 더 가까워집니다.


prosti

출처 : http:www.stackoverflow.com/questions/44834/can-someone-explain-all-in-python

반응형