만약, 이러한 구조의 폴더와 파일이 있다고 해봅시다:
(base) bolero @ ~/test/importerror $ tree .
.
├── detection
│ ├── models
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-39.pyc
│ │ │ └── detnet.cpython-39.pyc
│ │ ├── collate_func.py
│ │ └── detnet.py
│ └── utils
│ ├── base.py
│ ├── coco.py
│ └── voc.py
├── main.py
└── segmentation
├── models
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-39.pyc
│ │ └── segnet.cpython-39.pyc
│ ├── model_utils.py
│ └── segnet.py
└── utils
├── base.py
└── customdataset.py
8 directories, 16 files
우리의 목표는 최 상단의 main.py
에서 detection 모델과 segmentation 모델을 둘 다 가져오고 싶습니다.
아마 대부분의 DL github repository에는 이러한 방식으로 코드들이 들어가 있을텐데요,
문제는 저기 detection, segmentation 디렉토리 아래에 models 가 중복된다는 것입니다..
그래서, sys.path.append
로 디렉토리를 추가하고, models
를 import 하게 되면, 파이썬 인터프리터 입장에서는 지금 import한 models
가 detection 디렉토리의 models
인지, segmentation 디렉토리의 models
인지 알 수 없습니다.
좀 더 자세히 알아봅시다.
우선, main.py
코드를 보겠습니다:
import os
import sys
DETECTION_MODEL_PATH = 'detection'
SEGMENTATION_MODEL_PATH = 'segmentation'
sys.path.append(DETECTION_MODEL_PATH)
sys.path.append(SEGMENTATION_MODEL_PATH)
from models import detnet
from models import segnet
아주 단순한 코드입니다.
DETECTION_MODEL_PATH
와 SEGMENTATION_MODEL_PATH
를 적어준다.~~/github/yolov5
라던가, ~~/repos/Deeplab-v3
라던가 복잡한 경로가 들어갑니다.)sys.path.append
코드로 해당 MODEL_PATH
를 path 정보에 삽입한다.from models import detnet
, from models import segnet
으로 detnet, segnet 을 가져온다.import models.detnet
역시 동일한 의미의 코드입니다.)이렇게 하고 python main.py
으로 실행하면, 오류가 발생합니다:
(base) bolero @ ~/test/importerror $ python main.py
Traceback (most recent call last):
File "/Users/bolero/test/importerror/main.py", line 11, in <module>
from models import segnet
ImportError: cannot import name 'segnet' from 'models' (/Users/bolero/test/importerror/detection/models/__init__.py)
해결법은 매우 단순했습니다.
처음에는 detection 혹은 segmentation의 models 라는 폴더를 바꿔볼까… 라고도 생각했지만, 그럴 경우 변경되는 models 를 참조하고 있는 모든 소스코드에서 models 라는 이름을 바꿔줘야 했습니다.
그래서, sys.path
에서 직접 지워주기로 했습니다.
import os
import sys
DETECTION_MODEL_PATH = 'detection'
SEGMENTATION_MODEL_PATH = 'segmentation'
print("init path:", sys.path)
sys.path.append(DETECTION_MODEL_PATH)
from models import detnet
print("After detection :", sys.path)
del sys.path[sys.path.index(DETECTION_MODEL_PATH)]
print("detection removed:", sys.path)
sys.path.append(SEGMENTATION_MODEL_PATH)
from models import segnet
main.py
를 이렇게 수정했습니다.
sys.path
의 값을 del
이라는 파이썬 내장 키워드로 지워버리려고 했는데…
(base) bolero @ ~/test/importerror $ python main.py
init path: ['/Users/bolero/test/importerror', '/Users/bolero/opt/anaconda3/lib/python39.zip', '/Users/bolero/opt/anaconda3/lib/python3.9', '/Users/bolero/opt/anaconda3/lib/python3.9/lib-dynload', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages/aeosa']
After detection : ['/Users/bolero/test/importerror', '/Users/bolero/opt/anaconda3/lib/python39.zip', '/Users/bolero/opt/anaconda3/lib/python3.9', '/Users/bolero/opt/anaconda3/lib/python3.9/lib-dynload', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages/aeosa', 'detection']
detection removed: ['/Users/bolero/test/importerror', '/Users/bolero/opt/anaconda3/lib/python39.zip', '/Users/bolero/opt/anaconda3/lib/python3.9', '/Users/bolero/opt/anaconda3/lib/python3.9/lib-dynload', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages', '/Users/bolero/opt/anaconda3/lib/python3.9/site-packages/aeosa']
Traceback (most recent call last):
File "/Users/bolero/test/importerror/main.py", line 15, in <module>
from models import segnet
ImportError: cannot import name 'segnet' from 'models' (/Users/bolero/test/importerror/detection/models/__init__.py)
또 똑같은 에러가 발생했습니다.
여기서 참 의문이었습니다.
왜 sys.path
에 detection 모델의 경로가 없는데 참조를 하려고 할까?
알고보니, sys.path를 지우는 방식이 잘못되었습니다.
del sys.path[sys.path.index(DETECTION_MODEL_PATH)]
로만 지우는 게 아니고,
del sys.modules['models']
도 같이 작성해줘야 했습니다.
import os
import sys
DETECTION_MODEL_PATH = 'detection'
SEGMENTATION_MODEL_PATH = 'segmentation'
sys.path.append(DETECTION_MODEL_PATH)
from models import detnet
del sys.path[sys.path.index(DETECTION_MODEL_PATH)]
del sys.modules['models']
sys.path.append(SEGMENTATION_MODEL_PATH)
from models import segnet
이렇게 처리해주면
(base) bolero @ ~/test/importerror $ python main.py
(base) bolero @ ~/test/importerror $
아무 오류 없이 성공한 것을 확인할 수 있습니다!