Импорт из встроенной библиотеки, когда существует модуль с таким же именем - PullRequest
102 голосов
/ 17 мая 2011

Ситуация: - В моей папке проекта есть модуль с именем calendar. Я хотел бы использовать встроенный класс Calendar из библиотек Python.

Я провел несколько поисков и не могу найти решение своей проблемы.

Есть идеи без переименования моего модуля?

Ответы [ 5 ]

131 голосов
/ 26 ноября 2011

Смена названия вашего модуля не обязательна. Скорее, вы можете использовать absolute_import, чтобы изменить поведение импорта. Например, с stem / socket.py я импортирую модуль сокета следующим образом:

from __future__ import absolute_import
import socket

Это работает только с Python 2.5 и выше; это включающее поведение по умолчанию в Python 3.0 и выше. Pylint будет жаловаться на код, но он совершенно действителен.

35 голосов
/ 17 мая 2011

На самом деле, решить это довольно легко, но реализация всегда будет немного хрупкой, потому что это зависит от внутренних механизмов механизма импорта Python и может быть изменено в будущих версиях.

(следующий код показывает, какзагружать как локальные, так и нелокальные модули и как они могут сосуществовать)

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

Лучшее решение, если это возможно, - избегать именования ваших модулей с тем же именем, что и у стандартной библиотеки или встроенного модуля.имена.

14 голосов
/ 17 мая 2011

Единственный способ решить эту проблему - захватить внутреннюю импортную технику самостоятельно.Это не просто, а чревато опасностью.Вы должны избегать маяка в форме грааля любой ценой, потому что опасность слишком опасна.

Вместо этого переименуйте ваш модуль.

Если вы хотите научиться угонять внутреннюю импортную технику, вот гдевам нужно узнать, как это сделать:

Иногда есть веские причины, чтобы попасть в эту опасность.Причина, которую вы приводите, не среди них.Переименуйте ваш модуль.

Если вы выберете опасный путь, одна из проблем, с которой вы столкнетесь, заключается в том, что при загрузке модуля он заканчивается «официальным именем», так что Python может избежать необходимости разбирать содержимоеэтот модуль когда-либо снова.Сопоставление «официального имени» модуля с самим объектом модуля можно найти в sys.modules.

. Это означает, что если вы import calendar в одном месте, любой импортируемый модуль будет рассматриваться какпоскольку модуль с официальным именем calendar и всеми другими попытками import calendar где-либо еще, в том числе в другом коде, который является частью основной библиотеки Python, получит этот календарь.

Возможно, будет возможно спроектироватьклиентский импортер, использующий модуль imputil в Python 2.x, который заставлял модули, загруженные по определенным путям, сначала искать модули, которые они импортировали, чем-то отличным от sys.modules или чем-то в этом роде.Но это очень сложная вещь, и в 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__])
1 голос
/ 29 августа 2013

Я хотел бы предложить свою версию, которая представляет собой комбинацию решения Боаза Янива и 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)
0 голосов
/ 17 мая 2011

Изменить путь импорта:

import sys
save_path = sys.path[:]
sys.path.remove('')
import calendar
sys.path = save_path
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...