파이썬 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()
... myothermodule.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()
... 그리고 main.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
...하지만 다소 장황하고 같은 shebang 라인과 잘 섞이지 않습니다 #!/usr/bin/env python3
.
이름 mymodule
이 전역 적으로 고유 하다고 가정하면이 경우 가장 간단한 수정은 상대 가져 오기 사용을 피하고 사용하는 것입니다 ...
from mymodule import as_int
... 독특하지 않거나 패키지 구조가 더 복잡한 경우에는 패키지 디렉토리가 포함 된 디렉토리를에 포함시켜야합니다 PYTHONPATH
.
from mypackage.mymodule import as_int
... 또는 "즉시"작동하게하려면 PYTHONPATH
먼저 코드에서 코드를 제거 할 수 있습니다 .
import sys
import os
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)))
from mypackage.mymodule import as_int
그것은 일종의 고통이지만 , 특정 Guido van Rossum이 작성한 전자 메일 에 대한 단서가 있습니다 ...
나는 이것과 다른 제안 된
__main__
기계류 에 -1입니다 . 유일한 유스 케이스는 모듈의 디렉토리 안에있는 스크립트를 실행하는 것 같습니다. 항상 반 패턴으로 보았습니다. 내가 마음을 바꾸게하려면 그렇지 않다는 것을 저에게 납득시켜야합니다.
패키지 내에서 스크립트를 실행하는 것이 반 패턴인지 아닌지는 주관적이지만 개인적으로 일부 사용자 정의 wxPython 위젯을 포함하는 패키지에서 실제로 유용하다는 것을 알았습니다. 따라서 소스 파일 중 하나에 대해 스크립트를 실행하여 wx.Frame
포함 만 표시 할 수 있습니다 테스트 목적으로 해당 위젯.
설명
에서 PEP (328)
상대적 가져 오기는 모듈의 __name__ 속성을 사용하여 패키지 계층에서 해당 모듈의 위치를 결정합니다. 모듈 이름에 패키지 정보가 포함되어 있지 않은 경우 (예 : '__main__'로 설정) 모듈이 실제로 파일 시스템의 위치에 관계없이 모듈이 최상위 모듈 인 것처럼 상대 가져 오기가 해결됩니다 .
어느 시점에서 PEP 338 은 PEP 328 과 충돌했습니다 .
... 상대 가져 오기는 __name__ 을 사용하여 패키지 계층에서 현재 모듈의 위치를 결정합니다. 기본 모듈에서 __name__ 의 값 은 항상 '__main__' 이므로 명시 적 상대 가져 오기는 항상 실패합니다 (패키지 내의 모듈에 대해서만 작동하므로)
이 문제를 해결하기 위해 PEP 366 은 최상위 변수를 도입했습니다 __package__
.
새로운 모듈 레벨 속성을 추가하여이 PEP를 사용하면 -m 스위치를 사용하여 모듈을 실행할 경우 상대 가져 오기가 자동으로 작동 합니다. 모듈 자체에 소량의 상용구가 있으면 파일을 이름으로 실행할 때 상대 가져 오기가 작동합니다. [...] [속성]이 있으면 상대 가져 오기는 모듈 __name__ 속성이 아니라이 속성을 기반으로 합니다. [...] 메인 모듈이 파일 이름으로 지정되면 __package__ 속성이 None 으로 설정됩니다 . [...] 가져 오기 시스템에서 __package__가 설정되지 않은 (또는 None으로 설정된) 모듈에서 명시 적 상대 가져 오기가 발생하면 올바른 값을 계산하여 저장합니다 (__name __. rpartition를 ( '.') [0] 일반 모듈 과 __name__ 패키지 초기화 모듈)
(강조 광산)
(가) 경우 __name__
이며 '__main__'
, __name__.rpartition('.')[0]
빈 문자열을 반환합니다. 이것이 오류 설명에 빈 문자열 리터럴이있는 이유입니다.
SystemError: Parent module '' not loaded, cannot perform relative import
CPython PyImport_ImportModuleLevelObject
함수 의 관련 부분 :
if (PyDict_GetItem(interp->modules, package) == NULL) {
PyErr_Format(PyExc_SystemError,
"Parent module %R not loaded, cannot perform relative "
"import", package);
goto error;
}
CPython은 (로 액세스 가능 package
)에서 interp->modules
( 패키지 이름) 을 찾을 수없는 경우이 예외를 발생 sys.modules
시킵니다. 이후 sys.modules
입니다 "이미로드 된 모듈에 모듈 이름을 매핑하는 사전" , 이제 것이 분명 부모 모듈이 명시 적으로 상대 가져 오기를 수행하기 전에-절대 가져와야합니다 .
참고 로부터 패치 문제 18018은 추가 한 다른 if
블록 이 실행되고, 이전 코드 위 :
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
그러나 Python 3.6 이상에서만 이것을 볼 수 있습니다.
해결 방법 # 1 : -m을 사용하여 스크립트 실행
디렉토리 (Python 패키지 ) 를 고려하십시오 .
.
├── package
│ ├── __init__.py
│ ├── module.py
│ └── standalone.py
패키지의 모든 파일 은 동일한 두 줄의 코드로 시작합니다.
from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())
나는이 두 줄을 포함하고 있습니다 만 분명 작업의 순서를 확인합니다. 실행에 영향을 미치지 않으므로 완전히 무시할 수 있습니다.
__init__.py 및 module.py 에는 두 줄만 포함됩니다 (즉, 실제로 비어 있음).
standalone.py는 또한 상대 가져 오기를 통해 module.py 를 가져 오려고 시도합니다 .
from . import module # explicit relative import
우리는 그것이 /path/to/python/interpreter package/standalone.py
실패 할 것이라는 것을 잘 알고 있습니다. 그러나, 우리가 가진 모듈을 실행할 수있는 -m
명령 줄 옵션 것이다 "검색 sys.path
명명 된 모듈과 같은 내용을 실행 __main__
모듈을" :
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 은 가져올 모듈을 검색 할 상위 디렉토리 (스크립트의 디렉토리에 해당)의 수입니다.
그러므로,
현재 모듈 의 N 번째 선행 작업의 상위 디렉토리를 추가하십시오.
sys.path
에서 현재 파일의 디렉토리를 제거하십시오
sys.path
완전한 이름을 사용하여 현재 모듈의 상위 모듈을 가져 오십시오.
2
__package__
에서 정규화 된 이름으로 설정상대적 가져 오기 수행
솔루션 # 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
파일 이름으로 standalone.py 를 실행할 수 있습니다 .
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 : 절대 가져 오기 및 설정 도구 사용
단계는-
명시 적 상대 수입을 동등한 절대 수입으로 교체
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 에서 빌 렸습니다 .
설치하면 작업 디렉토리에 관계없이 패키지를 가져올 수 있습니다 (이름 지정 문제가 없다고 가정).
이 장점을 사용하기 위해 standalone.py 를 수정할 수 있습니다 (1 단계).
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 에서 파일을 빌리고 standalone.py를 변경 하겠습니다 .
의 상위 디렉토리 추가 패키지 에
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
상대 가져 오기를 절대 가져 오기로 바꾸십시오.
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()
바라건대, 이것은 누군가에게 가치가있을 것입니다-나는 위에 게시 된 것과 비슷한 상대적 수입을 알아 내려고하는 6 개의 스택 오버 플로우 게시물을 겪었습니다. 나는 제안대로 모든 것을 설정했지만 여전히 타격을 받고 있었다ModuleNotFoundError: No module named 'my_module_name'
로컬에서 개발하고 놀았 기 때문에 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
따라서 누군가가 몇 가지 제안을 시도한 경우 코드가 올바르게 구성되어 있다고 생각하고 현재 디렉토리를 PYTHONPATH로 내 보내지 않으면 다음 중 하나를 시도해보십시오.
- 코드를 실행하고 다음과 같이 경로를 명시 적으로 포함하십시오.
$ PYTHONPATH=. python3 test/my_module/module_test.py
- 를 호출하지 않으
PYTHONPATH=.
려면setup.py
다음과 같은 내용 으로 파일을 작성python setup.py development
하고 경로에 패키지를 추가하기 위해 실행 하십시오.
# setup.py from setuptools import setup, find_packages setup( name='sample', packages=find_packages() )
이 문제를 피하기 위해 리 패키지 패키지 로 솔루션을 고안 했습니다. lib 경로에 상위 디렉토리를 추가합니다.
import repackage
repackage.up()
from mypackage.mymodule import myfunction
리 패키지는 지능형 전략 (콜 스택 검사)을 사용하여 광범위한 사례에서 작동하는 상대적 가져 오기를 수행 할 수 있습니다.
메인 프로젝트 디렉토리에서 python3을 실행해야 작동했습니다.
예를 들어 프로젝트의 구조가 다음과 같은 경우
project_demo/
├── main.py
├── some_package/
│ ├── __init__.py
│ └── project_configs.py
└── test/
└── test_project_configs.py
해결책
project_demo / 폴더 내에서 python3을 실행 한 다음
from some_package import project_configs
두 패키지가 모두 가져 오기 경로 (sys.path)에 있고 원하는 모듈 / 클래스가 example / example.py 인 경우 상대 가져 오기없이 클래스에 액세스하려면 다음을 시도하십시오.
from example.example import fkt
참고 URL : https://stackoverflow.com/questions/16981921/relative-imports-in-python-3
'IT story' 카테고리의 다른 글
Node.js로 명령 행 바이너리 실행 (0) | 2020.02.09 |
---|---|
JavaScript / jQuery에서 배열에 특정 문자열이 포함되어 있는지 확인하는 방법은 무엇입니까? (0) | 2020.02.09 |
Math.round (0.49999999999999994)가 1을 반환하는 이유는 무엇입니까? (0) | 2020.02.09 |
Visual Studio의 출력 창에 쓰기 (0) | 2020.02.09 |
C # 목록 (0) | 2020.02.09 |