동일한 이름의 모듈이있는 경우 내장 라이브러리에서 가져 오기


121

상황 :-내 project_folder에 calendar라는 모듈이 있습니다.-Python 라이브러리에서 내장 된 Calendar 클래스를 사용하고 싶습니다.-Calendar import Calendar에서 사용할 때 내 모듈에서로드하려고하기 때문에 불평합니다.

몇 번 검색했지만 내 문제에 대한 해결책을 찾을 수없는 것 같습니다.

내 모듈의 이름을 바꿀 필요없이 아이디어가 있습니까?


24
내장 모듈을 숨기기 위해 모듈 이름을 지정하지 않는 것이 가장 좋습니다.
the_drow

3
해결책은 "다른 이름 선택"입니다. 이름을 바꾸지 않는 방법은 나쁜 생각입니다. 모듈 이름을 바꿀 수없는 이유는 무엇입니까? 이름을 바꾸면 무엇이 문제입니까?
S.Lott

과연. 가 없기 때문이다 에는 다음 stdlib 모듈을 미행하는 것은 강하게 권장하지 않습니다 것을이 질문에 좋은 답변.
ncoghlan

솔루션이 가치가있는 것보다 더 많은 문제로 보였기 때문에 동일한 모듈 이름을 사용하지 않았습니다. 감사!
twig

9
@the_drow이 조언은 순수하고 단순하지 않습니다. PEP328은이를 쉽게 인정합니다.
Konrad Rudolph

답변:


4

허용되는 솔루션에는 현재 사용되지 않는 접근 방식이 포함되어 있습니다.

여기에 있는 importlib 문서 는 파이썬> = 3.5의 파일 경로에서 직접 모듈을로드하는 더 적절한 방법의 좋은 예를 제공합니다.

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__  # returns "/path/to/tokenize.py"
module_name = tokenize.__name__  # returns "tokenize"

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)

따라서 경로에서 .py 파일을로드하고 모듈 이름을 원하는대로 설정할 수 있습니다. 따라서 module_name모듈을 가져올 때 가질 사용자 정의 이름으로 조정하십시오 .

단일 파일 대신 패키지를로드하려면 file_path패키지의 루트 경로 여야합니다.__init__.py


매력처럼 작동합니다 ... 라이브러리를 개발하는 동안 테스트에 이것을 사용했기 때문에 내 테스트는 항상 게시 된 (설치된) 버전이 아닌 개발중인 버전을 사용했습니다. Windows 10에서는 다음과 같이 모듈 경로를 작성해야했습니다 file_path=r"C:\Users\My User\My Path\Module File.py". 그럼 난라는 module_name난이 조각 벗겨, 다른 PC에서 사용할 수 coud 것을 전체 작업 스크립트를하였습니다, 그래서 그냥 발표 모듈처럼
누가 복음 Savefrogs

141

모듈 이름을 변경할 필요가 없습니다. 대신 absolute_import를 사용하여 가져 오기 동작을 변경할 수 있습니다. 예를 들어 stem / socket.py를 사용 하여 다음과 같이 소켓 모듈을 가져옵니다.

from __future__ import absolute_import
import socket

이것은 Python 2.5 이상에서만 작동합니다. Python 3.0 이상에서 기본값 인 동작을 활성화합니다. Pylint는 코드에 대해 불평하지만 완벽하게 유효합니다.


4
이것은 나에게 정답 인 것 같습니다. 자세한 내용은 2.5 변경 로그 또는 PEP328 을 참조하십시오.
Pieter Ennes

5
이것이 올바른 해결책입니다. 안타깝게도 패키지 내부의 코드가 실행되면 작동하지 않습니다. 패키지가 인식되지 않고 로컬 경로가 PYTHONPATH. 또 다른 질문 은 그것을 해결하는 방법을 보여줍니다.
Konrad Rudolph

5
이것이 해결책입니다. Python 2.7.6을 확인했는데 이것이 필수이지만 여전히 기본값이 아닙니다.
Havok


1
그런 다음 기본 모듈을 내장 모듈과 충돌하는 이름으로 지정하지 마십시오 .
안티 Haapala

38

실제로이 문제를 해결하는 것은 다소 쉽지만 구현은 파이썬 가져 오기 메커니즘의 내부에 의존하고 향후 버전에서 변경 될 수 있기 때문에 항상 약간 취약합니다.

(다음 코드는 로컬 및 비 로컬 모듈을로드하는 방법과 이들이 공존하는 방법을 보여줍니다)

def import_non_local(name, custom_name=None):
    import imp, sys

    custom_name = custom_name or name

    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(custom_name, f, pathname, desc)
    f.close()

    return module

# Import non-local module, use a custom name to differentiate it from local
# This name is only used internally for identifying the module. We decide
# the name in the local scope by assigning it to the variable calendar.
calendar = import_non_local('calendar','std_calendar')

# import local module normally, as calendar_local
import calendar as calendar_local

print calendar.Calendar
print calendar_local

가능한 경우 가장 좋은 해결책은 표준 라이브러리 또는 내장 모듈 이름과 동일한 이름으로 모듈 이름을 지정하지 않는 것입니다.


이것이 sys.modules로컬 모듈을로드하려는 시도 와 어떻게 상호 작용 합니까?
Omnifarious

@Omnifarious : 모듈을 sys.modules에 이름과 함께 추가하여 로컬 모듈을로드하지 못하게합니다. 이를 피하기 위해 항상 사용자 정의 이름을 사용할 수 있습니다.
Boaz Yaniv

@Boaz Yaniv : 표준 캘린더가 아닌 로컬 캘린더에 맞춤 이름을 사용해야합니다. 다른 Python 모듈은 표준 모듈을 가져 오려고 할 수 있습니다. 그렇게하면 기본적으로 파일의 이름을 바꾸지 않고 로컬 모듈의 이름을 바꾸는 것입니다.
Omnifarious

@Omnifarious : 어느 쪽이든 할 수 있습니다. 일부 다른 코드는 로컬 모듈을로드하려고 시도하고 동일한 오류가 발생할 수 있습니다. 타협해야하며 지원할 모듈을 결정하는 것은 사용자에게 달려 있습니다.
Boaz Yaniv

2
보아즈 감사합니다! 당신의 스 니펫은 더 짧지 만 (그리고 문서화되어), 미래에 사람들 (또는 나 자신)을 혼동시킬 수있는 해키 코드를 갖는 것보다 모듈의 이름을 바꾸는 것이 더 쉽다고 생각합니다.
twig

15

이 문제를 해결하는 유일한 방법은 내부 수입 기계를 직접 납치하는 것입니다. 이것은 쉽지 않고 위험이 가득합니다. 위험이 너무 위험하기 때문에 성배 모양의 비콘은 피해야합니다.

대신 모듈 이름을 변경하십시오.

내부 수입 기계를 탈취하는 방법을 배우고 싶다면 여기에서이를 수행하는 방법을 알아볼 수 있습니다.

때때로이 위험에 빠질만한 타당한 이유가 있습니다. 당신이주는 이유는 그들 중 하나가 아닙니다. 모듈의 이름을 바꿉니다.

위험한 길을 택한다면, 여러분이 마주하게 될 한 가지 문제는 모듈을로드 할 때 파이썬이 그 모듈의 내용을 다시 파싱 할 필요가 없도록 '공식적인 이름'으로 끝나는 것입니다. 모듈 객체 자체에 대한 모듈의 '공식 이름'매핑은에서 찾을 수 있습니다 sys.modules.

import calendar, 한곳에서 가져온 모듈이 공식 이름을 가진 모듈로 간주 되고 기본 Python 라이브러리의 일부인 다른 코드를 포함하여 다른 calendar모든 import calendar곳에서 시도 하면 해당 달력을 얻게됩니다.

Python 2.x 의 imputil 모듈 을 사용하여 특정 경로에서로드 된 모듈이 sys.modules처음이 아닌 다른 항목에서 가져온 모듈을 찾도록 하는 고객 가져 오기 도구를 설계 할 수 있습니다 . 그러나 그것은 매우 어려운 일이며 어쨌든 Python 3.x에서는 작동하지 않습니다.

가져 오기 메커니즘을 연결하지 않는 매우 추하고 끔찍한 일이 있습니다. 이것은 아마도하지 말아야 할 일이지만, 작동 할 것입니다. calendar모듈을 시스템 달력 모듈과 달력 모듈의 하이브리드로 바꿉니다 . 내가 사용하는 기능골격에 대해 Boaz Yaniv 에게 감사드립니다 . 파일 시작 부분에 넣으십시오 .calendar.py

import sys

def copy_in_standard_module_symbols(name, local_module):
    import imp

    for i in range(0, 100):
        random_name = 'random_name_%d' % (i,)
        if random_name not in sys.modules:
            break
        else:
            random_name = None
    if random_name is None:
        raise RuntimeError("Couldn't manufacture an unused module name.")
    f, pathname, desc = imp.find_module(name, sys.path[1:])
    module = imp.load_module(random_name, f, pathname, desc)
    f.close()
    del sys.modules[random_name]
    for key in module.__dict__:
        if not hasattr(local_module, key):
            setattr(local_module, key, getattr(module, key))

copy_in_standard_module_symbols('calendar', sys.modules[copy_in_standard_module_symbols.__module__])

imputil은 더 이상 사용되지 않는 것으로 간주됩니다. imp 모듈을 사용해야합니다 .
Boaz Yaniv

그건 그렇고 파이썬 3과 완벽하게 호환됩니다. 그리고 전혀 사용하기에는 털이 없습니다. 그러나 파이썬에 의존하는 코드는 한 가지 방식으로 경로를 처리하거나 해당 순서로 모듈을 찾는 코드가 조만간 중단 될 수 있음을 항상 알고 있어야합니다.
Boaz Yaniv

1
맞습니다.하지만 이러한 분리 된 경우 (모듈 이름 충돌) 후킹은 가져 오기 메커니즘을 과도하게 사용합니다. 털이 많고 호환되지 않기 때문에 혼자 두는 것이 좋습니다.
Boaz Yaniv

1
@jspacek nope, 지금까지는 좋지만 충돌은 PyDev의 debbuger를 사용할 때만 발생하며 정기적으로 사용하지 않습니다. 그리고 위의 답변에서 약간 변경
되었으므로

1
@jspacek : 라이브러리가 아니라 게임이므로 제 경우에는 이전 버전과의 호환성이 전혀 문제가되지 않습니다. 그리고 네임 스페이스 충돌은 PyDev IDE (Python의 codestd 모듈 사용)를 통해 실행할 때만 발생합니다 . 이는 개발자 중 일부만 이 "병합 해킹"에 문제가있을 수 있음을 의미합니다 . 사용자는 전혀 영향을받지 않습니다.
MestreLion

1

Boaz Yaniv와 Omnifarious의 솔루션이 결합 된 버전을 제공하고 싶습니다. 이전 답변과 두 가지 주요 차이점이있는 모듈의 시스템 버전을 가져옵니다.

  • '점'표기법을 지원합니다. package.module
  • 시스템 모듈의 import 문에 대한 드롭 인 대체입니다. 즉, 해당 한 줄만 바꾸면되고 이미 모듈에 대한 호출이있는 경우 그대로 작동합니다.

이것을 액세스 할 수있는 곳에 두어 호출 할 수 있습니다 (내 __init__.py 파일에 내 것이 있습니다).

class SysModule(object):
    pass

def import_non_local(name, local_module=None, path=None, full_name=None, accessor=SysModule()):
    import imp, sys, os

    path = path or sys.path[1:]
    if isinstance(path, basestring):
        path = [path]

    if '.' in name:
        package_name = name.split('.')[0]
        f, pathname, desc = imp.find_module(package_name, path)
        if pathname not in __path__:
            __path__.insert(0, pathname)
        imp.load_module(package_name, f, pathname, desc)
        v = import_non_local('.'.join(name.split('.')[1:]), None, pathname, name, SysModule())
        setattr(accessor, package_name, v)
        if local_module:
            for key in accessor.__dict__.keys():
                setattr(local_module, key, getattr(accessor, key))
        return accessor
    try:
        f, pathname, desc = imp.find_module(name, path)
        if pathname not in __path__:
            __path__.insert(0, pathname)
        module = imp.load_module(name, f, pathname, desc)
        setattr(accessor, name, module)
        if local_module:
            for key in accessor.__dict__.keys():
                setattr(local_module, key, getattr(accessor, key))
            return module
        return accessor
    finally:
        try:
            if f:
                f.close()
        except:
            pass

mysql.connection을 가져오고 싶었지만 이미 mysql (공식 mysql 유틸리티)이라는 로컬 패키지가 있습니다. 따라서 시스템 mysql 패키지에서 커넥터를 가져 오기 위해 다음을 대체했습니다.

import mysql.connector

이것으로 :

import sys
from mysql.utilities import import_non_local         # where I put the above function (mysql/utilities/__init__.py)
import_non_local('mysql.connector', sys.modules[__name__])

결과

# This unmodified line further down in the file now works just fine because mysql.connector has actually become part of the namespace
self.db_conn = mysql.connector.connect(**parameters)

-2

가져 오기 경로 변경 :

import sys
save_path = sys.path[:]
sys.path.remove('')
import calendar
sys.path = save_path

이렇게 한 후에는 직접 가져 오기 기계를 조작하지 않고는 로컬 모듈을 가져올 방법이 없기 때문에 작동하지 않습니다.
Omnifarious

@Omnifarious : 캘린더 가져 오기 *를 수행하는 세 번째 모듈로 해결할 수있는 다른 문제입니다.
linuts

아니요, 파이썬이에서 모듈 이름을 캐시 sys.modules하고 같은 이름의 모듈을 다시 가져 오지 않기 때문에 이것은 작동하지 않을 것입니다 .
Boaz Yaniv
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.