Python에서 ImportError 발생 시 (ImportError: cannot import name 'a' from 'b')

bolero2·2023년 5월 5일
0

development

목록 보기
4/7

0. Intro

만약, 이러한 구조의 폴더와 파일이 있다고 해봅시다:

(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 인지 알 수 없습니다.

좀 더 자세히 알아봅시다.


1. main.py

우선, 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

아주 단순한 코드입니다.

  1. DETECTION_MODEL_PATHSEGMENTATION_MODEL_PATH 를 적어준다.
    (실제 업무에서는 저렇게 단순한 위치가 아닌, ~~/github/yolov5 라던가, ~~/repos/Deeplab-v3 라던가 복잡한 경로가 들어갑니다.)
  2. sys.path.append 코드로 해당 MODEL_PATH를 path 정보에 삽입한다.
  3. 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)

2. 해결법

해결법은 매우 단순했습니다.

처음에는 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 $

아무 오류 없이 성공한 것을 확인할 수 있습니다!

profile
AI Engineer 입장에서 작성하는 여러 기록들

0개의 댓글