programing

프로젝트에 동일한 이름의 모듈이 있는 경우 표준 라이브러리에서 가져오려면 어떻게 해야 합니까?(Python이 모듈을 찾는 위치를 어떻게 제어할 수 있습니까?)

css3 2023. 7. 29. 08:46

프로젝트에 동일한 이름의 모듈이 있는 경우 표준 라이브러리에서 가져오려면 어떻게 해야 합니까?(Python이 모듈을 찾는 위치를 어떻게 제어할 수 있습니까?)

, 이 은 제 프로젝트 폴더에 있습니다.calendar 라이브러리인 코드다곳에표준라싶사습다니용고하를리러이브를 하고 싶습니다.Calendarclass. 제가 이 할 는 클스래를 해서. 하지만 이 클래스를 가져오려고 할 때,from calendar import Calendar나중에 오류를 발생시키는 대신 내 모듈에서 가져옵니다.

어떻게 하면 피할 수 있을까요?모듈 이름을 변경해야 합니까?

모듈 이름을 변경할 필요가 없습니다. 2에서는 Python 2.5를 합니다.absolute_import가져오기 동작을 변경합니다.

라이브러리를 다음과 같이 .socket, socket.py프로젝트:

from __future__ import absolute_import
import socket

Python 3.x에서는 이 동작이 기본값입니다.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

가능하면 표준 라이브러리 또는 기본 제공 모듈 이름과 동일한 이름으로 모듈 이름을 지정하지 않는 것이 가장 좋습니다.

이 문제를 해결할 수 있는 유일한 방법은 내부 수입 기계를 직접 납치하는 것입니다.이것은 쉽지 않고 위험으로 가득 차 있습니다.위험이 너무 크므로 당신은 무슨 수를 써서라도 석재 모양의 봉화를 피해야 합니다.

대신 모듈 이름을 변경합니다.

내부 가져오기 시스템을 하이잭킹하는 방법에 대해 알아보려면 다음을 참조하십시오.

때때로 이런 위험에 처해야 할 좋은 이유가 있습니다.당신이 주는 이유는 그들 중에 없습니다.모듈 이름을 변경합니다.

만약 당신이 위험한 길을 택한다면, 당신이 마주치게 될 한 가지 문제는 모듈을 로드할 때, Python이 그 모듈의 내용을 다시는 구문 분석하지 않아도 되도록 '공식 이름'으로 끝나는 것입니다. 이름은 모의 '공이과름' 모자매의다확수다있에서 할 수 .sys.modules.

이것은 만약 당신이import calendar한 곳에서 어떤 모듈을 가져오든 공식 이름을 가진 모듈로 간주됩니다.calendar 다른 은 고리다모시은들도든.import calendar메인 Python 라이브러리의 일부인 다른 코드를 포함하여 다른 곳에서도 해당 달력을 받을 수 있습니다.

Python 2.x의 속성 모듈을 사용하여 고객 임포터를 설계할 수 있으며, 이로 인해 특정 경로에서 로드된 모듈이 가져올 모듈을 검색할 수 있습니다.sys.modules이나 ▁but. 하지만 3하지 않을 것입니다하지만 이는 매우 어려운 작업이며, 어쨌든 Python 3.x에서는 작동하지 않습니다.

수입 메커니즘을 잠그지 않는 극도로 추악하고 끔찍한 일이 있습니다.이것은 아마도 여러분이 하지 말아야 할 것이지만, 효과가 있을 것입니다.그것은 당신의 것입니다.calendar모듈을 시스템 일정관리 모듈과 사용자 일정관리 모듈의 하이브리드로 만듭니다.가 사용하는 기능의 골격에 대해 보아즈 야니브에게 감사드립니다.이것을 당신의 시작에 놓으세요.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__])

Python 3.5 이상에서는 표준 라이브러리 모듈을 사용하여 지정된 경로에서 직접 가져오기, 바이패스import의 룩업 메커니즘 다음과 같습니다.

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)

코드에서는, 제코에는서드실,,file_path의 모든 경로로 설정할 수 있습니다..py 올가파일져;module_name할 때 )이어야.import문이 시도됨).에서는 다음코서사용을 합니다.module모듈의 이름으로, 변수 이름 변경 module다른 이름을 사용합니다.

파일 하려면,file_path패키루경야합니다여로트의의 합니다.__init__.py.

프롤로그: 일부 용어

절대적인 중요성은 Python이 모듈이 포함된 폴더를 찾을 때까지 시스템 모듈 경로(SMP)라고 부르는 폴더를 한 번에 하나씩 검색하는 입니다.

SMP는 시작 시 다음을 기반으로 생성됩니다.PYTHONPATH환경 변수와 다른 것들.문자열 목록으로 Python 내에 표시됩니다.을 후sys로, 사용 합니다.sys.path그러나 해당 모듈을 가져오든 말든 존재합니다. (보통 파이썬 프로그래머는 이 목록을 "sys.path"라고 부릅니다.)하지만, 우리는 수입 메커니즘에 대해 깊은 기술적 세부 사항을 논의하고 있고, 이름이 붙은 모듈을 사용하고 있기 때문입니다.sys예를 들어, 별도의 용어를 설정하는 것이 적절해 보입니다.)

반면에 상대 가져오기는 현재 모듈을 기준으로 모듈의 코드가 현재 패키지의 컨텍스트 내에 있어야 하는 위치를 직접 지정합니다.


요약

현재 패키지 대신 표준 라이브러리에서 어떻게 가져올 수 있습니까?

해당 표준 라이브러리 모듈이 기본 제공으로 구현되지 않은 경우 Python을 시작하기 전에 환경 변수를 설정해야 할 수 있습니다.이것은 Python이 메인 스크립트의 디렉토리를 배치하는 것을 방지합니다 (다음과 같이 시작할 때).python script.py시 현재 디렉토리 또는 SMP 시작 시 현재 작업 디렉터리(그렇지 않으면 기본적으로 수행됩니다.

3.11에서는 설정 대신 -P 옵션을 사용할 수 있습니다.3.4 이상에서는 -I도 사용할 수 있지만 다른 Python별 환경 변수도 무시하고 사용자별 사이트 패키지 디렉터리를 SMP에 추가하지 않습니다. (물론 수정할 수도 있습니다.)sys.path하기 위해 방식으로.는 SMP를 가져오려고 것이 물론 문제는 가져오기를 시도하는 것에 있지 않는 한.sys.)

일단 처리가 완료되면 절대 가져오기를 사용합니다.

3.x에서:

import sys # or
from sys import version # or
from sys import * # but beware namespace pollution

~ 2.5 ~ 2.7µa__future__가져오기가 먼저 필요합니다.

from __future__ import absolute_import
# proceed as above

2.4 이하에서는 암묵적인 상대 가져오기를 방지하기 위해 해킹이 필요합니다.

sys = __import__("sys", {})

이것은 다른 버전에서도 작동해야 하지만 필요 이상으로 훨씬 못생기고 복잡합니다.

또는 Omnifaries의 답변, Boaz Yaniv의 답변 또는 케이시의 답변에 설명된 대로 가져오기 기계를 이용하여 조회 동작을 변경해 보십시오.이러한 모든 접근 방식은 모듈을 검색하기 위한 내부 알고리즘을 에뮬레이트(일부)함으로써 작동하지만, 상대 가져오기 단계를 건너뛰고 의 첫 번째 요소도 건너뜁니다. (이 전제는 다음과 같습니다.)sys.path위의 논의에 따라 "유효"한 으로 가정합니다.PYTHONSAFEPATH따라서 이 경로를 사용하면 프로젝트 내부를 살펴볼 수 있습니다.)

표준 라이브러리가 현재 패키지에서 가져오는 것을 방지하고 자체에서 가져오도록 하려면 어떻게 해야 합니까?

표준 라이브러리는 일반적으로 절대 가져오기를 사용합니다.이로 인해 현재 패키지의 섀도잉으로 인해 문제가 발생하는 경우 현재 패키지의 모듈 이름을 변경하는 것이 좋습니다.그렇지 않으면 동일한 환경 변수와 명령줄 플래그 트릭이 작동해야 합니다.

표준 라이브러리 대신 현재 패키지에서 어떻게 가져올 수 있습니까?

2.4 이전 버전에서는 기본적으로 이 작업이 수행됩니다.

2.5 이상에서는 패키지가 올바르게 설정되었는지 확인한 후 상대 가져오기를 사용합니다.

이는 대부분 패키지의 루트가 SMP에 있는지 확인하는 것을 의미합니다. 가장 강력한 방법은 가상 환경을 활성화하고 해당 가상 환경에 패키지를 설치하는 것입니다.그렇지 않은 경우, 프로그램이 일부 "드라이버" 스크립트에서 시작된다고 가정하고, 프로그램이 패키지 루트와 동일한 폴더에 있는지 확인합니다.또는 다음을 사용하여 패키지(또는 적절한 점선 경로 이름을 가진 하위 패키지)를 모듈로 실행합니다.-m플래그, 패키지 루트가 포함된 디렉터리에서.

패키지가 올바르게 설정되어 있다고 가정하면 상대적 가져오기는 다음과 같습니다.

from . import sys # sys.py in the same folder as this source file
from .sys import something # from our source, not the standard library
from .sys import * # again, beware namespace pollution
from .child import sys # child/sys.py
from .. import sys # ../sys.py, IF .. is still within the package root
from ..sibling import sys # ../sibling/sys.py
# Use more .'s to go up more levels first.
# This will not work beyond the package root.

참고로 이 모든 것은 다음을 사용합니다.from통사론PEP에서 설명한 바와 같이:

상대가는항사합니야다해를 해야 합니다.from <> import;import <> 항상절입다니 뒤에 있기 입니다. 왜냐하면 그 후에import XXX.YYY.ZZZ...XXX.YYY.ZZZ표현에 사용할 수 있습니다. 하지만..moduleY식에서 사용할 수 없습니다.

절대적인 최후의 수단으로서 SMP는 다음을 통해 의도적으로 수정될 수 있습니다.sys.path절대 가져오기가 작동하도록 현재 파일의 경로를 결정하고 패키지 루트의 경로를 계산한 후.일반적인 상황에서는 이 작업이 필요하지 않습니다. 많은 인기 있는 강력한 Python 라이브러리는 수십만 줄의 코드를 처리할 수 있지만, 어떤 식으로든 언급되지는 않습니다.

표준 라이브러리가 의도한 대로 자체에서 가져오는 것이 아니라 현재 패키지에서 가져오도록 하려면 어떻게 해야 합니까?

Python 표준 라이브러리가 내부적으로 상대적인 가져오기를 사용하는 곳이 어디인지 즉시 알지 못하며, 이를 시도할 좋은 이유를 알 수 없습니다.그렇긴 하지만, 아마도 수입 기계를 해킹하면 가능할 것 같습니다.쉽거나 재미있을 것 같지도 않고 여기서 시도할 생각도 없습니다.

다른 곳에서 어떻게 수입할 수 있습니까?

전체 경로가 지정된 모듈을 동적으로 가져오려면 어떻게 해야 합니까?를 참조하십시오.Brandon Squizzato의 대답은 또한 일반적인 기술을 요약합니다.


Python이 모듈을 찾는 위치

2.4 이전 버전

원래는 상대 가져오기를 위한 특별한 구문이 없었습니다.Python은 먼저 상대적인 가져오기를 시도한 다음 실패할 경우 절대 가져오기를 시도합니다.예를 들어 코드는 다음과 같습니다.import sys를 가져올 입니다.sys.py표준 라이브러리 모듈이 아닌 현재 모듈과 동일한 폴더에서 사용할 수 있습니다.

이러한 상대적인 수입은 현재 모듈이 아닌 패키지 루트에 관련된 것으로 알고 있습니다. 하지만 현재로서는 쉽게 확인할 수 없습니다.

딕트를 제공함으로써 분명히 이 문제를 해결할 수 있었습니다.globals.__import__견적:

sys = __import__("sys", {})

import문은 글로벌 네임스페이스를 사용하여 호출할 패키지를 결정합니다. 빈 네임스페이스를 전달하면 패키지 정보를 유추할 수 없습니다.

그래서, 그것을 사용함으로써.__import__함수를 직접 사용하여 시도된 상대 가져오기를 건너뛸 수 있습니다.

2.5 ~ 2.7

PEP 328은 기본적으로 Python 가져오기를 절대적으로 만들고 명시적 구문에서만 상대적 가져오기를 사용하는 제안을 도입했습니다. (또한 Python 에코시스템에 "절대 가져오기"와 "상대적 가져오기"라는 용어를 도입했습니다.)이 구문을 사용하는 상대 가져오기는 가져오기를 수행하는 모듈에 상대적입니다. 추가 선행.s를 사용하여 상위 패키지를 지정할 수 있습니다.

Python 2.5의 첫 번째 릴리스부터 Import를 사용하여 새로운 동작을 사용할 수 있게 되었습니다.

from __future__ import absolute_import
import math # checks the SMP

반면에,

from __future__ import absolute_import
from . import math # imports from the same directory as this source file

또는

from __future__ import absolute_import
from .. import math # imports from the immediate parent directory,
                    # IF it is still within the package root

3.x 이후

. 2는 더 수 으로 절대 상대 를 지정하지 . PEP 328 없음.2.4 의는상더가이기수암져사절오코상아지또없는드대 -상명합니정니으요다적로시를기가오져대는며으할용명설대시작적된었본동되기 -습이니다값에이▁-▁the▁either▁explicit▁described지▁-아▁behaviour▁pep▁code▁2▁is명▁import▁or▁absolute▁2니합요니정다으적로__future__수입이 필요


절대 또는 상대 가져오기를 선택하는 것만으로는 충분하지 않습니다.

이 시점에서 나타나는 딸꾹질이

  1. 많은 사람들이 패키지 시스템의 미묘함 때문에 상대적인 수입품을 사용하기 어렵다고 생각하는 같습니다.중요한 점은.상대 가져오기 구문에서 사용되는 s는 디렉터리 트리의 수준을 의미하는 아니라 패키지 트리의 수준을 의미합니다. (결국, 관련되지 않은 모듈을 로드하는 방법이 있습니다.).py파일을 디스크에 저장할 수 있습니다!)
  2. 절대 가져오기가 사용되는 경우에도 현재 프로젝트의 코드가 예기치 않게 표준 라이브러리에 그림자를 드리울 수 있습니다.이는 SMP가 구성되는 방식 때문입니다.경우 " SMP에 있습니다. "Python", "Python", "SMP"는 SMP에 있습니다.import this표준 라이브러리가 절대적인 가져오기를 수행하고 있음에도 불구하고 표준 라이브러리를 찾지 못할 수 있습니다. (반면,import sys 표준 라이브러리를 찾을 수 있습니다. 마지막 섹션을 참조하십시오.)
  3. 한편, 표준 라이브러리는 패키징을 잘 활용하지 못합니다.자체 루트 패키지가 없습니다. 대부분의 경우 표준 라이브러리 모듈이 서로 의존할 때 절대 가져오기를 사용합니다.

마지막 두 지점은 놀라운 방식으로 상호 작용할 수 있습니다.

$ touch token.py
$ python
Python 3.8.10 (default, Nov 14 2022, 12:59:47) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>> help()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/_sitebuiltins.py", line 102, in __call__
    import pydoc
  File "/usr/lib/python3.8/pydoc.py", line 66, in <module>
    import inspect
  File "/usr/lib/python3.8/inspect.py", line 40, in <module>
    import linecache
  File "/usr/lib/python3.8/linecache.py", line 11, in <module>
    import tokenize
  File "/usr/lib/python3.8/tokenize.py", line 35, in <module>
    from token import EXACT_TOKEN_TYPES
ImportError: cannot import name 'EXACT_TOKEN_TYPES' from 'token' (/current/working/directory/token.py)

이 문제는 요약의 기술 중 하나를 사용하여 SMP를 수정하면 방지할 수 있습니다.

$ touch token.py
$ python -I
Python 3.8.10 (default, Nov 14 2022, 12:59:47) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> help
Type help() for interactive help, or help(object) for help about object.
>>> help()

Welcome to Python 3.8's help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at https://docs.python.org/3.8/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics".  Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".

help>

이것은 여전히 모든 것을 말해주지 않습니다.

모듈 로딩 시스템은 실제로 위에서 설명한 것보다 훨씬 더 복잡합니다.행동을 수정할 수 있도록 의도적으로 후크가 채워져 있습니다.에도 "metapath", "metapath"로 가.sys.meta_path모듈을 로드하는 데 실제로 사용되는 모듈 로더를 포함합니다.

설명서는 프로세스에 가까운 레시피를 제공합니다. (물론 실제는 가져오기의 부트스트래핑 문제를 해결할 필요가 없습니다.)sys그리고.importlib.util수입 시스템을 구현하기 위해!)하지만, 그것은 각각이 무엇을 보여주지 않습니다.finder in sys.meta_path하고 있습니다.

기본적으로 절대 가져오기의 경우:

  • 첫째번._frozen_importlib.BuiltinImporter에서는 모듈 이름이 Python에 내장된 모듈과 일치하는지 확인하여 모듈을 직접 가져올 수 있도록 합니다.표준 라이브러리의 일부는 이러한 방식으로 구현되지만 일부는 그렇지 않습니다. 이 부분은 시간이 지남에 따라 변경되었습니다.

  • 그리고나서,_frozen_importlib.FrozenImporter에서는 지정된 이름을 사용하여 고정된 모듈을 로드하려고 합니다.

  • 둘한다면, 마막으로둘면, 약다실다한패만지,면▁finally만실다한,,_frozen_importlib_external.PathFinderSMP를 검색합니다.

이로 인해 사용자 코드는 일부 표준 라이브러리 모듈의 절대 가져오기를 그림자로 만들지 않고 다른 표준 라이브러리 모듈을 그림자로 만들지 않을 수 있습니다.이 테스트 스크립트가 있다고 가정합니다.import_test.py:

def test_import(name):
    module = __import__(name)
    return any(attr for attr in dir(module) if not attr.startswith('__'))

if __name__ == '__main__':
    print('imported this from standard library?', test_import('this'))
    print('imported sys from standard library?', test_import('sys'))

이러한 이름을 가진 빈 Python 파일이 먼저 CWD에 추가되면 어떻게 되는지 살펴보겠습니다.

$ touch this.py
$ touch sys.py
$ python import_test.py 
imported this from standard library? False
imported sys from standard library? True

2.0 이상부터는 Python에 내장된 모듈의 이름을 나열합니다.또한 3.10 이후 표준 라이브러리에 있는 모든 가능한 모듈 이름을 나열합니다(파이썬이 컴파일될 때 의도적으로 제외되었거나 현재 운영 체제에서 사용할 수 없는 이름도 포함됩니다).

저는 Boaz Yaniv와 Omnifaries의 솔루션을 결합한 제 버전을 제공하고 싶습니다.모듈의 시스템 버전을 가져오며, 이전 답변과 크게 다른 두 가지 사항이 있습니다.

  • package.module과 같은 'dot' 표기법을 지원합니다.
  • 시스템 모듈의 가져오기 문에 대한 드롭인 대체 기능입니다. 즉, 한 줄만 교체하면 되고 모듈에 대한 호출이 이미 있는 경우 그대로 작동합니다.

호출할 수 있도록 접근 가능한 위치에 놓습니다(__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)

언급URL : https://stackoverflow.com/questions/6031584/how-can-i-import-from-the-standard-library-when-my-project-has-a-module-with-th