IT이야기

Python 3의 상대적 가져오기

cyworld 2022. 4. 9. 09:08
반응형

Python 3의 상대적 가져오기

같은 디렉토리의 다른 파일에서 함수를 가져오려고 한다.

때때로 그것은 나와 함께 일한다.from .mymodule import myfunction하지만 가끔 나는 다음과 같은 말을 듣는다.

SystemError: Parent module '' not loaded, cannot perform relative import

가끔 와도 통할 때가 있다.from mymodule import myfunction, 그러나 때때로 나는 또한 다음과 같은 것을 얻는다.

SystemError: Parent module '' not loaded, cannot perform relative import

나는 이곳의 논리를 이해할 수 없고 아무런 설명도 찾을 수 없었다.이건 완전히 무작위로 보여

이 모든 것의 이면에 어떤 논리가 있는지 누가 설명해 주시겠습니까?

불행히도, 이 모듈은 패키지 안에 있어야 하고, 또한 때때로 스크립트로서 실행될 수 있어야 한다.내가 그걸 어떻게 할 수 있는지 알기나 해?

이런 레이아웃이 있는 건 꽤 흔해...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

...와 함께mymodule.py이렇게...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

...amyothermodule.py이렇게...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

...그리고 amain.py이렇게...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

때 잘 하는...달릴 때 잘 되는 것.main.py또는mypackage/mymodule.py로 실패하다.mypackage/myothermodule.py, 상대적인 가져오기 때문에...

from .mymodule import as_int

당신이 그걸 운영해야 하는 방법은...

python3 -m mypackage.myothermodule

...과 잘 하지만 다소 장황하고, 셰방 라인과도 잘 어울리지 않는다.#!/usr/bin/env python3.

이 경우에 가장 간단한 해결 방법(이름을 가정할 경우mymodule전 세계적으로 독특하고, 상대적인 수입품 사용을 피하고, 단지...

from mymodule import as_int

더하는 디렉터리를 ...에해야 할 하지만, 고유하지 않거나 패키지 구조가 더 복잡할 경우 패키지 디렉터리를 포함하는 디렉터리를PYTHONPATH그리고 이렇게 하면...

from mypackage.mymodule import as_int

" ...을을 내도 .아니면 "즉각"으로 작동하고 싶다면, 당신은 그 일을 할 수 있다.PYTHONPATH이것과 첫번째로...

import sys
import os

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(SCRIPT_DIR))

from mypackage.mymodule import as_int

좀 고통스럽긴 하지만, 왜 어떤 귀도 판 로섬이 쓴 이메일에서...

나는 이것과 다른 어떤 제안된 쌍둥이에 대해 -1이다.__main__기보장 는 내가 으로 보인다유일한 사용 사례는 내가 항상 대척점으로 보아왔던 모듈의 디렉토리 안에 살고 있는 스크립트를 실행하는 것 같다.내 마음을 바꾸려면 그렇지 않다고 설득해야 할 거야.

패키지 내에서 스크립트를 실행하는 것이 대척점인지 아닌지는 주관적이지만, 개인적으로는 사용자 정의 wxPython 위젯이 포함된 패키지에서 매우 유용하다는 것을 알게 되므로 소스 파일에 대해 스크립트를 실행하여wx.Frame테스트용 위젯만 포함.

설명

PEP 328부터

상대적 가져오기는 모듈의 __name_ 속성을 사용하여 패키지 계층 구조에서 해당 모듈의 위치를 결정한다.모듈의 이름에 패키지 정보가 포함되어 있지 않으면(예: '_main__'로 설정된 경우, 모듈이 실제로 파일 시스템에 위치한 위치에 관계없이 모듈이 최상위 모듈인 것처럼 상대적 가져오기가 해결된다.

어느 순간 PEP 338이 PEP 328과 충돌했다.

...상대적인 수입은 패키지 계층 구조에서 현재 모듈의 위치를 결정하기 위해 _name__에 의존한다.주 모듈에서 _name__의 값은 항상 '_main_'이므로 명시적 상대적 가져오기는 항상 실패한다(패키지 내의 모듈에서만 작동하므로).

그리고 이 문제를 해결하기 위해, PEP 366은 최상위 변수를 도입했다.

이 PEP는 새로운 모듈 레벨 속성을 추가함으로써 모듈이 -m 스위치를 사용하여 실행될 경우 상대적 가져오기가 자동으로 작동하도록 한다.모듈 자체의 소량의 보일러플레이트는 파일을 이름으로 실행할 때 상대적 가져오기가 동작할 수 있게 된다.[...] [속성]이 존재할 때 상대적 가져오기는 __name__ 속성보다는 이 속성에 기초하게 된다.[...] 주 모듈을 파일 이름에 의해 지정할 때 __package_ 속성 w.none으로 설정될 수 없다. [...] 수입 시스템이 __package__가 설정되지 않은 모듈(또는 없음으로 설정됨)에서 명시적인 상대적 가져오기와 마주칠 때, 정확한 값(정상 모듈의 경우 _name____rpartition('.').[0]과 패키지 초기화 모듈의 경우 ___name__]을 계산하여 저장한다.

(내 것)

만약__name__이다'__main__'__name__.rpartition('.')[0]빈 문자열을 반환한다.오류 설명에 빈 문자열 리터럴이 있는 이유:

SystemError: Parent module '' not loaded, cannot perform relative import

CPython 함수의 관련 부분:

if (PyDict_GetItem(interp->modules, package) == NULL) {
    PyErr_Format(PyExc_SystemError,
            "Parent module %R not loaded, cannot perform relative "
            "import", package);
    goto error;
}

을 찾을 수 없는 경우 이 를 한다.package에 ~에interp->modules(으 로 액세스 가능)이후sys.modules"모듈 이름을 이미 로드된 모듈에 매핑하는 사전"으로, 이제 상대적 가져오기를 수행하기 전에 상위 모듈이 명시적으로 절대적으로 조정되어야 한다는 것이 명백해졌다.

참고: 18018호의 패치는 코드 이전에 실행될 다른 블록을 추가했다.

if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
    PyErr_SetString(PyExc_ImportError,
            "attempted relative import with no known parent package");
    goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
    ...
*/

만약package(위에서와 같이 표시됨)는 빈 문자열이며, 오류 메시지는

ImportError: attempted relative import with no known parent package

그러나, 당신은 이것을 파이썬 3.6 이상에서만 볼 수 있을 것이다.

솔루션 #1: -m을 사용하여 스크립트 실행

Python 패키지인 디렉터리를 고려하십시오.

.
├── package
│   ├── __init__.py
│   ├── module.py
│   └── standalone.py

패키지의 모든 파일은 동일한 2줄의 코드로 시작한다.

from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())

는 단지 작전 순서를 분명히 하기 위해 이 두 줄을 포함시킬 것이다.사형 집행에 영향을 주지 않기 때문에 우리는 그들을 완전히 무시할 수 있다.

__init__py와 module.py에는 이 두 줄만 들어 있다(즉, 그것들은 사실상 비어 있다).

아마존닷컴은 상대적인 수입을 통해 module.py을 추가적으로 수입하려고 시도한다.

from . import module  # explicit relative import

우리는 잘 알고 있다./path/to/python/interpreter package/standalone.py실패할 것이다.그러나 "지정된 모듈을 검색하고 모듈로서 내용을 실행하는" 명령줄 옵션으로 모듈을 실행할 수 있다.

vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>

-m모든 가져오기 작업을 수행하고 자동으로 설정__package__하지만 당신은 그것을 할 수 있다.

솔루션 #2: 수동으로 설정 __package__

실제 해결책이 아닌 개념 증명서로 취급해 달라.그것은 실제 코드에 사용하기에 적합하지 않다.

그러나 PEP 366은 설정 때문에 불완전하다.__package__혼자만으로는 충분하지 않다.적어도 N개의 선행 패키지를 모듈 계층 구조에서 가져와야 하며, 여기서 N은 가져올 모듈을 검색할 상위 디렉토리 수(스크립트의 디렉토리와 관련)이다.

그러므로,

  1. 현재 모듈의 N번째 이전 버전의 상위 디렉토리 추가sys.path

  2. sys.path

  3. 전체 이름을 사용하여 현재 모듈의 상위 모듈을 가져오십시오.

  4. 세트__package__2부터 정식 이름까지

  5. 상대 가져오기 수행

솔루션 #1에서 파일을 빌리고 하위 패키지를 추가하십시오.

package
├── __init__.py
├── module.py
└── subpackage
    ├── __init__.py
    └── subsubpackage
        ├── __init__.py
        └── standalone.py

이번에 standalone.py은 다음과 같은 상대적 가져오기를 사용하여 패키지에서 module.py을 가져올 것이다.

from ... import module  # N = 3

보일러 번호로 그 선에 선행해야 제대로 될 거야

import sys
from pathlib import Path

if __name__ == '__main__' and __package__ is None:
    file = Path(__file__).resolve()
    parent, top = file.parent, file.parents[3]

    sys.path.append(str(top))
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass

    import package.subpackage.subsubpackage
    __package__ = 'package.subpackage.subsubpackage'

from ... import module # N = 3

그것은 우리가 독립적으로 실행할 수 있게 해준다.파일 이름:

vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py

함수로 포장된 보다 일반적인 용액은 여기에서 찾을 수 있다.사용 예:

if __name__ == '__main__' and __package__ is None:
    import_parents(level=3) # N = 3

from ... import module
from ...module.submodule import thing

솔루션 #3: 절대 가져오기 및 setuptools 사용

단계는 -

  1. 명시적 상대적 가져오기를 등가 절대 가져오기로 바꾸기

  2. 설치package그것을 수입할 수 있게 하기 위해.

예를 들어, 디렉토리 구조는 다음과 같을 수 있다.

.
├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module.py
│   │   └── standalone.py
│   └── setup.py

여기서 setup.py

from setuptools import setup, find_packages
setup(
    name = 'your_package_name',
    packages = find_packages(),
)

나머지 파일은 솔루션 #1에서 빌렸다.

설치하면 작업 디렉터리에 관계없이 패키지를 가져올 수 있다(이름 지정 문제가 없을 것으로 가정).

이러한 이점(1단계)을 사용하도록 standalone.py을 수정할 수 있다.

from package import module  # absolute import

를 작업리리리리 로 project뛰어가다니다/path/to/python/interpreter setup.py install --user(--user사이트 검색 디렉토리에 패키지 설치(2단계):

vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user

이제 standalone.py을 스크립트로 실행할 수 있는지 확인하십시오.

vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>

참고: 이 경로로 이동하려면 가상 환경을 사용하여 패키지를 분리하여 설치하는 것이 좋다.

솔루션 #4: 절대 수입 및 일부 보일러 판 코드 사용

솔직히, 설치는 필요하지 않다 - 당신은 절대 수입품이 작동하도록 하기 위해 당신의 대본에 약간의 보일러플레이트 코드를 추가할 수 있다.

솔루션 #1에서 파일을 빌려서 독립 실행형으로 변경하려고 한다.py:

  1. 패키지의 상위 디렉터리 추가 위치sys.path 절대 가져오기를 사용하여 패키지에서 가져오기를 시도하기 에:

    import sys
    from pathlib import Path # if you haven't already done so
    file = Path(__file__).resolve()
    parent, root = file.parent, file.parents[1]
    sys.path.append(str(root))
    
    # Additionally remove the current file's directory from sys.path
    try:
        sys.path.remove(str(parent))
    except ValueError: # Already removed
        pass
    
  2. 절대 가져오기로 상대 가져오기를 대체하십시오.

    from package import module  # absolute import
    

standalone.py은 문제 없이 실행된다.

vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>

나는 당신에게 경고해야 한다고 생각한다: 특히 당신의 프로젝트가 복잡한 구조를 가지고 있다면, 이것을 하지 않도록 노력하라.


참고로, PEP 8은 절대 수입품의 사용을 권고하지만, 일부 시나리오에서는 명시적으로 상대 수입품이 허용될 수 있다고 명시한다.

절대 수입품은 대개 가독성이 더 높고 더 잘 처신하는 경향이 있기 때문에(또는 최소한 오류 메시지를 더 잘 전달) 권장된다.[...] 그러나, 절대 수입을 사용하는 것이 불필요하게 장황한 복잡한 패키지 레이아웃을 다룰 때, 명시적 상대적 수입은 절대 수입에 대한 허용 가능한 대안이다.

이 파일을 패키지의 __init__py 파일에 넣으십시오.

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

패키지가 다음과 같은 경우:

├── project
│   ├── package
│   │   ├── __init__.py
│   │   ├── module1.py
│   │   └── module2.py
│   └── setup.py

이제 다음과 같이 패키지에 정기적인 가져오기를 사용하십시오.

# in module2.py
from module1 import class1

이것은 비단뱀 2와 3 둘 다에서 효과가 있다.

나는 이 문제에 부딪쳤다.해킹 해결 방법은 다음과 같은 if/else 블록을 통해 가져오는 것이다.

#!/usr/bin/env python3
#myothermodule

if __name__ == '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

SystemError: 상위 모듈 ''이(가) 로드되지 않아 상대 가져오기를 수행할 수 없음

즉, 패키지 내에서 모듈을 스크립트로 실행 중임.패키지 내부에 스크립트를 혼합하는 것은 까다롭기 때문에 가능한 한 피해야 한다.패키지를 가져와서 실행하는 래퍼 스크립트 사용scripty대신 기능하다

최상위 디렉터리가 호출된 경우foo, 그것은 당신의 것이다.PYTHONPATH모듈 검색 경로, 그리고 패키지가 있는 경우bar(그것은 당신이 예상할 수 있는 디렉토리 입니다.__init__.pyfile in), 스크립트는 내부에 배치하지 않고 에서 실행되어야 한다.foo 기껏해야

스크립트는 에 대한 파일 이름 인수로 사용된다는 점에서 여기의 모듈과 다르다는 점에 유의하십시오.python사)를python <filename>또는 a를 통해#!(shebang) 줄.모듈로서 직접 로딩된다(이 때문에if __name__ == "__main__":스크립트에서 작동됨) 및 상대적인 가져오기에 사용할 패키지 컨텍스트가 없음.

선택사항

  • 가능하다면, 프로젝트를 다음 주소로 패키징하십시오.setuptools(또는)poetry또는flit패키징을 간소화하는 데 도움이 될 수 있음), 콘솔 스크립트 진입점 작성, 프로젝트 설치:pip그런 다음 패키지를 제대로 가져오는 방법을 알고 있는 스크립트를 생성하십시오.패키지를 로컬로 설치할 수 있음pip install -e ., 그래서 그것은 여전히 그 자리에서 편집될 수 있다.

  • 그렇지 않으면, 절대로, 절대로, 절대로, 사용하지 않는다.python path/to/packagename/file.py, 항상 사용python path/to/script.py그리고script.py사용할 수 있다from packagename import ....

  • 대체적으로 명령행 스위치를 사용하여 Python에게 모듈을 가져오라고 지시하고, 그 모듈을__main__ 스크립트 그러나 더 이상 스크립트 파일이 없기 때문에 셰방 라인에서는 작동하지 않는다.

    사용한다면python -m foo.bar그리고foo/bar.py에서 발견되다sys.path디렉토리(다음으로 가져오기 및 실행됨)__main__올바른 패키지 컨텍스트와 함께.만약bar또한 포장되어 있다, 안에foo/, 그것은 반드시 a를 가지고 있어야 한다.__main__.py철하다 (그래서)foo/bar/__main__.py으로부터 오는 길로서sys.path디렉터리).

  • 극단적인 상황에서는 Python이 상대적인 가져오기를 해결하기 위해 사용하는 메타데이터를 설정하여 추가하십시오.__package__ 직접; 일foo/bar/spam.py, 가져올 수 있는 위치foo.bar.spam는 전지구적으로 주어진다.__package__ = "foo.bar"그것은 단지 또 하나의 세계일 뿐이다.__file__그리고__name__할 때 한 , Pythone 에어로 서서

위에sys.path

위의 내용을 모두 보려면 패키지를 가져올 수 있어야 하며, 이는 에 나열된 디렉토리(또는 zip 파일) 중 하나에서 패키지를 찾을 수 있어야 함을 의미한다.여기에는 몇 가지 옵션도 있다.

  • .path/to/script.py발견되었다(그러다)path/to)가 자동으로 에 추가됨sys.path. 실행 중python path/to/foo.py덧붙이다path/tosys.path.

  • 프로젝트를 패키징한 경우(와 함께)setuptoolspoetryflit또는 다른 Python 포장 도구)를 설치하고, 패키지가 이미 올바른 위치에 추가되었다.

  • 마지막으로 올바른 디렉터리를 다음에 추가하십시오.sys.path네 자신.패키지를 스크립트 파일에 상대적으로 배치할 수 있는 경우__file__스크립트 전역 네임스페이스의 변수(: 개체 사용),HERE = Path(__file__).resolve().parent파일이 상주하는 디렉터리에 대한 참조(절대 경로).

PyCharm 사용자의 경우:

나 또한 점점 더 많은 것을 얻고 있었다.ImportError: attempted relative import with no known parent package왜냐하면 나는 그 말을 덧붙이고 있었기 때문이다..PyCharm 구문 분석 오류를 소거하기 위한 표기법.PyCharm은 정확히 다음을 찾을 수 없다고 보고한다.

lib.thing import function

이 옵션을 다음 항목으로 변경하는 경우:

.lib.thing import function

그것은 실수를 잠재우지만 당신은 앞서 말한 것을 얻는다.ImportError: attempted relative import with no known parent package파이샴의 파서는 무시해.틀렸고 코드는 말에도 불구하고 잘 작동한다.

이 문제를 없애기 위해, 나는 리패키지 패키지로 해결책을 고안해 냈는데, 그것은 얼마 전부터 나에게 효과가 있었다.lib 경로에 상위 디렉토리를 추가한다.

import repackage
repackage.up()
from mypackage.mymodule import myfunction

리패키지(repackage)는 지능적인 전략(콜 스택에 대한 조사)을 사용하여 광범위한 사례에서 작동하는 상대적인 수입을 할 수 있다.

바라건대, 이것은 저 밖의 누군가에게 가치가 있는 것이기를 바란다 - 나는 여기 위에 게시된 것과 유사한 상대적인 수입을 알아내기 위해 여섯 개의 스택 오버플로우 게시물을 훑어보았다.나는 제안대로 모든 것을 설정했지만 여전히 때리고 있었다.ModuleNotFoundError: No module named 'my_module_name'

나는 단지 지역적으로 발전하고 놀고 있을 뿐이었기 때문에, 나는 a를 만들거나 운영하지 않았었다.setup.py 일요일을 . 나도 분명히 내 계획을 세우지 못했어.PYTHONPATH.

나는 테스트가 모듈과 같은 디렉토리에 있을 때와 같이 코드를 실행했을 때 다음과 같은 모듈을 찾을 수 없다는 것을 깨달았다.

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

그러나 내가 명시적으로 경로를 지정했을 때 모든 것이 작동하기 시작했다.

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

따라서 누구든지 몇 가지 제안을 시도해 본 적이 있는 경우, 자신의 코드가 올바르게 구성되었다고 믿고 현재 디렉터리를 PYSONPATH로 내보내지 않을 경우 다음 중 하나를 시도해 보는 것과 유사한 상황에 처해 있는 자신을 발견한다.

  1. 코드를 실행하고 다음과 같은 경로를 명시적으로 포함하십시오.$ PYTHONPATH=. python3 test/my_module/module_test.py
  2. 호출을 피하기 위해PYTHONPATH=. , , , a a를 .setup.py다음과 같은 내용을 철하고 실행하다.python setup.py development경로에 패키지를 추가하려면:
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)

나는 python3를 메인 프로젝트 디렉토리에서 실행해야 했다.

예를 들어 프로젝트 구조가 다음과 같은 경우:

project_demo/
├── main.py
├── some_package/
│   ├── __init__.py
│   └── project_configs.py
└── test/
    └── test_project_configs.py

해결책

python3를 folder project_demo/에서 실행한 다음

from some_package import project_configs

ImportError: 알려진 상위 패키지가 없는 상대적 가져오기 시도 중

내 프로그램에서 나는 현재 경로에서 파일을 사용하여 그 기능을 가져오고 있었다.

from .filename import function

그리고 현재 경로(Dot)를 패키지 이름으로 수정했다.그래서 내 문제가 해결됐어

from package_name.filename import function

위의 답변이 너에게 도움이 되었으면 좋겠어.

나는 위의 모든 것을 헛되이 해 보았지만, 다만 내가 잘못 알고 있다는 것을 깨달았다.-내 소포 이름으로.

간단히 말해서, 가지고 있지 않다.-…의__init__.py그런 말도 안 되는 걸 알고는 의기양양해 본 적이 없어

판은 보월일 은...module상대적인 수입으로package독립적으로 달릴 수 있는

package/module.py

## Standalone boilerplate before relative imports
if __package__ is None:                  
    DIR = Path(__file__).resolve().parent
    sys.path.insert(0, str(DIR.parent))
    __package__ = DIR.name

from . import variable_in__init__py
from . import other_module_in_package
...

이제 어떤 방식으로든 모듈을 사용할 수 있다.

  1. 평소와 같이 모듈을 실행하십시오.python -m package.module
  2. 모듈로 사용:python -c 'from package import module'
  3. 독립 실행형 실행:python package/module.py
  4. 또는 쉐뱅과 함께 (#!/bin/env python) just:package/module.py

NB! 사용sys.path.append대신에sys.path.insert만약 당신의 실수를 추적하기 힘들 것이다.module너의 이름과 같은 이름을 가지고 있다.package예시my_script/my_script.py

물론 패키지 계층의 상위 수준에서 상대적인 가져오기가 있는 경우, 이 정도로는 충분하지 않지만, 대부분의 경우 괜찮다.

에 대한 TL;DR:DR: 로 업데이트된 @Aya의 답변pathlib도서관, 그리고 주피터 노트북에서 일하면서__file__정의되지 않음:

가져오려는 경우my_function에 의거하여 정의한.../my_Folder_where_the_package_lives/my_package.py암호를 쓰는 곳에 대한 존중

그런 다음 다음을 수행하십시오.

import os
import sys
import pathlib

PACKAGE_PARENT = pathlib.Path(__file__).parent
#PACKAGE_PARENT = pathlib.Path.cwd().parent # if on jupyter notebook
SCRIPT_DIR = PACKAGE_PARENT / "my_Folder_where_the_package_lives"
sys.path.append(str(SCRIPT_DIR))

from my_package import my_function

동일한 디렉토리에서 가져오기

첫째, 같은 디렉토리에서 가져올 수 있다.

여기 파일 구조...

Folder
 |
 ├─ Scripts
 |   ├─ module123.py
 |
 ├─ main.py
 ├─ script123.py

여기 메인.파이를 치다

from . import script123
from Scripts import module123

보시다시피 다음 위치에서 가져오기.현재 디렉터리에서 가져오기

참고: INDLE을 제외한 다른 항목을 사용하여 실행 중인 경우 터미널이 다음 디렉토리와 동일한 디렉토리로 이동하십시오.main.py실행하기 전에 파일을 정리하십시오.

또한 로컬 폴더에서 가져오기도 작동한다.

상위 디렉터리에서 가져오는 중

여기서 나의 GitHub 요지에서 보듯이 다음과 같은 방법이 있다.

다음 파일 트리 가져오기...

ParentDirectory
 ├─ Folder
 |   |
 |   ├─ Scripts
 |   |   ├─ module123.py
 |   |
 |   ├─ main.py
 |   ├─ script123.py
 |
 ├─ parentModule.py

그런 다음 이 코드를 맨 위에 추가하십시오.main.py파일

import inspect
import os
import sys

current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parent_dir = os.path.dirname(current_dir)
sys.path.insert(0, parent_dir)

from ParentDirectory import Stuff

두 패키지가 모두 가져오기 경로(sys.path)에 있고 원하는 모듈/클래스가 예/example.py인 경우, 상대적인 가져오기 없이 클래스에 액세스하려면:

from example.example import fkt

위의 항목 중 어떤 항목도 제대로 작동하지 않는 경우 모듈을 명시적으로 지정하십시오.

디렉토리:

├── Project
│     ├── Dir
│     │    ├── __init__.py
│     │    ├── module.py
│     │    └── standalone.py

해결책:

#in standalone.py
from Project.Dir.module import ...

모듈 - 가져올 모듈

모듈용 패키지를 만드는 것이 가장 좋은 해결책이라고 생각한다.이것은 어떻게 하는지에 대한 더 많은 정보를 가지고 있다.

상대적인 수입에 대해 걱정할 필요가 없는 패키지가 있으면 무조건 수입만 하면 된다.

나는 짱오랑 같이 일할 때 이런 것을 많이 접하게 되는데, 왜냐하면 짱오랑 같이 일할 때 많은 기능성이 발휘되기 때문이다.manage.py하지만 나는 또한 일부 모듈을 스크립트로 직접 실행할 수 있게 하고 싶다. (기본적으로 당신은 그것들을 만들 것이다.)manage.py지시사항, 그러나 우리는 아직 거기에 있지 않다.

이것은 그러한 프로젝트가 어떻게 보일지 모의한 것이다.

├── dj_app
│   ├── models.py
│   ├── ops
│   │   ├── bar.py
│   │   └── foo.py
│   ├── script.py
│   ├── tests.py
│   ├── utils.py
│   └── views.py
└── manage.py

여기서 중요한 부분은manage.pydj_app/script.py그리고dj_app/tests.py우리는 또한 하위조항도 가지고 있다.dj_app/ops/bar.py그리고dj_app/ops/foo.py프로젝트 전체에 걸쳐 사용하고자 하는 항목이 더 많이 포함되어 있다.

이 문제의 근원은 일반적으로 귀사를 원하는 데서 비롯된다.dj_app/script.py테스트 케이스를 포함하는 스크립트 방법dj_app/tests.py실행 시 호출되는 기능manage.py test.

이렇게 해서 프로젝트와 프로젝트를 꾸몄다.imports;s;

# dj_app/ops/foo.py
# Foo operation methods and classes
foo_val = "foo123"

.

# dj_app/ops/bar.py
# Bar operations methods and classes
bar_val = "bar123"

.

# dj_app/script.py
# script to run app methods from CLI

# if run directly from command line
if __name__ == '__main__':
    from ops.bar import bar_val
    from ops.foo import foo_val

# otherwise
else:
    from .ops.bar import bar_val
    from .ops.foo import foo_val

def script_method1():
    print("this is script_method1")
    print("bar_val: {}".format(bar_val))
    print("foo_val: {}".format(foo_val))


if __name__ == '__main__':
    print("running from the script")
    script_method1()

.

# dj_app/tests.py
# test cases for the app
# do not run this directly from CLI or the imports will break
from .script import script_method1
from .ops.bar import bar_val
from .ops.foo import foo_val 

def main():
    print("Running the test case")
    print("testing script method")
    script_method1()

if __name__ == '__main__':
    print("running tests from command line")
    main()

.

# manage.py
# just run the test cases for this example
import dj_app.tests
dj_app.tests.main()

.

테스트 사례 실행 위치manage.py;

$ python3 manage.py
Running the test case
testing script method
this is script_method1
bar_val: bar123
foo_val: foo123

스크립트 자체 실행;

$ python3 dj_app/script.py
running from the script
this is script_method1
bar_val: bar123
foo_val: foo123

실행하려고 하면 오류가 발생한다는 점에 유의하십시오.test.py그러나 직접적으로는 그렇게 하지 마십시오.

$ python3 dj_app/tests.py
Traceback (most recent call last):
  File "dj_app/tests.py", line 5, in <module>
    from .script import script_method1
ModuleNotFoundError: No module named '__main__.script'; '__main__' is not a package

만약 내가 수입에 대해 더 복잡한 상황에 부딪히게 된다면, 나는 대개 그것을 해킹하기 위해 이와 같은 것을 실행하게 된다.

import os
import sys
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, THIS_DIR)
from script import script_method1
sys.path.pop(0)

이것이 나의 프로젝트 구조

├── folder
|   | 
│   ├── moduleA.py
|   |   |
|   |   └--function1()
|   |       └~~ uses function2()
|   | 
│   └── moduleB.py
|       | 
|       └--function2()
|   
└── main.py
     └~~ uses function1()

여기 내 것moduleA수입품moduleB그리고main수입품moduleA

나는 아래에 있는 작은 조각을 추가했다.moduleA수입하다moduleB

try:
    from .moduleB import function2 
except:
    from moduleB import function2 

이제 두 가지를 모두 실행할 수 있다.main.py게다가moduleA.py개별적으로

이것이 해결책인가?

아래 용액은 Python3에서 테스트한다.

├── classes
|   |
|   ├──__init__.py
|   | 
│   ├── userclass.py
|   |   |
|   |   └--viewDetails()
|   |       
|   | 
│   └── groupclass.py
|       | 
|       └--viewGroupDetails()
|   
└── start.py
     └~~ uses function1()

이제, 사용자 클래스의 viewDetails of userclass 또는 groupclass의 viewGroupDetails는 classess 디렉토리의 init_.py에서 먼저 정의한다.

Ex: in _ init _.py

from .userclasss import viewDetails

from .groupclass import viewGroupDetails

2단계: 이제 start.py에서 viewDetails를 직접 가져올 수 있다.

Ex: 시작.파이를 치다

from classes import viewDetails
from classes import viewGroupDetails

나도 비슷한 문제가 있었다.나는 협력하기 위해 공통 상수를 사용하는 리눅스 서비스와 cgi 플러그인이 필요했다.이렇게 하는 '자연적인' 방법은 패키지의 init.py에 그것들을 넣는 것이지만, 나는 -m 매개 변수로 cgi 플러그인을 시작할 수 없다.

나의 최종 솔루션은 위의 2번 솔루션과 비슷했다.

import sys
import pathlib as p
import importlib

pp = p.Path(sys.argv[0])
pack = pp.resolve().parent

pkg = importlib.import_module('__init__', package=str(pack))

단점은 pkg으로 상수(또는 공통 함수)를 접두사 앞에 붙여야 한다는 점이다.

print(pkg.Glob)

가져올 파일을 외부 디렉토리로 이동하면 도움이 된다.
이것은 당신의 기본 파일이 그것의 디렉토리에 다른 파일을 만들 때 더욱 유용하다.
Ex:
이전:
프로젝트
|---1971년
|--------------주요.파이를 치다
|--------------------------------------------------1파이를 치다
다음 이후:
프로젝트
|---1971.파이를 치다
|---1971년
|--------------주요.파이를 치다

TLDR; Python 스크립트의 진입점에 다음을 추가하여 시스템 경로에 스크립트 경로 추가.

import os.path
import sys
PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

이제 터미널에서뿐만 아니라 파이차르마에서도 프로젝트를 진행할 수 있어!!

나는 비슷한 문제를 가지고 있었고 작업 디렉토리에 있는 패키지에 상징적인 링크를 만들어 해결했다.

ln -s ../../../my_package my_package

그런 다음 평소와 같이 가져오기:

import my_package

나는 이것이 "피톤" 솔루션이라기 보다는 "리눅스" 솔루션에 가깝다는 것을 안다.그래도 유효한 접근법이야

참조URL: https://stackoverflow.com/questions/16981921/relative-imports-in-python-3

반응형