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

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

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

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



답변

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

여기에 있는 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


답변

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

from __future__ import absolute_import
import socket

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


답변

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

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

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.

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__])


답변

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)


답변

가져 오기 경로 변경 :

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


답변