Python как псевдоним имени модуля (переименовать с сохранением обратной совместимости)

#python #python-2.7 #import #python-importlib

#python #python-2.7 #импорт #python-importlib

Вопрос:

У меня есть пакет python с именем foo , который я использую при импорте:

 import foo.conf
from foo.core import Something
 

Теперь мне нужно переименовать foo модуль во что-то другое, скажем bar , поэтому я хочу сделать:

 import bar.conf
from bar.core import Something
 

но я хочу поддерживать обратную совместимость с существующим кодом, поэтому импорт old ( foo. ) должен работать так же хорошо и выполнять то же самое bar. , что и импорт.

Как это можно сделать в python 2.7?

Ответ №1:

Это заставляет вас сохранять foo каталог, но я думаю, что это лучший способ заставить это работать.

Настройка каталога:

 bar
├── __init__.py
└── baz.py
foo
└── __init__.py

foo_bar.py
 

bar/__init__.py пусто.
bar/baz.py : worked = True

foo/__init__.py :

 import sys

# make sure bar is in sys.modules
import bar
# link this module to bar
sys.modules[__name__] = sys.modules['bar']

# Or simply
sys.modules[__name__] = __import__('bar')
 

foo_bar.py :

 import foo.baz

assert(hasattr(foo, 'baz') and hasattr(foo.baz, 'worked'))
assert(foo.baz.worked)

import bar
assert(foo is bar)
 

Комментарии:

1. Это не работает должным образом. Если модуль имеет подмодули, то они не совпадают.

2. Для надежного решения, включающего подмодули, вам также необходимо явно добавить псевдонимы для каждого подмодуля в sys.modules . Это работает (я пробовал это на Python 3.6 / 3.7, так что даже в последних версиях) и успешно устраняет тонкую ошибку, из-за которой вы получаете несколько экземпляров одного и того же модуля с разными именами (что может вызвать кошмары, если у вас есть статическое состояние для каждого модуля или проверки isinstance (), которые путаютсяесли существует несколько версий одного и того же класса с разными именами). Работает лучше, чем подход MetaPathFinder.

3. Интересно, есть ли какое-либо решение, которое не предполагает создания отдельного foo каталога ().

Ответ №2:

Вы имеете в виду что-то подобное?

 import foo as bar
 

вы можете использовать ярлыки для импорта модулей
Нравится:

 from numpy import array as arr

in: arr([1,2,3])
out: array([1, 2, 3])
 

и вы также можете использовать более одного псевдонима одновременно

 from numpy import array as foo
in: foo([1,2,3])
out: array([1, 2, 3])
 

если ваш foo класс, вы можете сделать:

 bar=foo()
 

и вызовите его подфункцию
с помощью:

 bar.conf()
 

Это вам помогает?

Комментарии:

1. Нет, я не хочу изменять существующие обычаи. Я хочу import foo работать точно так же, как import bar .

2. Использование псевдонима, т.Е. as Дал мне submodule not found error .

Ответ №3:

Этот ответ работает с подмодулями:

 import sys
import os
from importlib.abc import MetaPathFinder, Loader
import importlib
from MainModule.SubModule import *

class MyLoader(Loader):
    def module_repr(self, module):
        return repr(module)

    def load_module(self, fullname):
        old_name = fullname
        names = fullname.split(".")
        names[1] = "SubModule"
        fullname = ".".join(names)
        module = importlib.import_module(fullname)
        sys.modules[old_name] = module
        return module


class MyImport(MetaPathFinder):
    def find_module(self, fullname, path=None):
        names = fullname.split(".")
        if len(names) >= 2 and names[0] == "Module" and names[1] == "LegacySubModule":
            return MyLoader()


sys.meta_path.append(MyImport())
 

Комментарии:

1. Хотя этот пример немного надуманный, он действительно показывает, как переопределить импорт, и просто лучше, чем вся importlib.abc неприятная документация. Спасибо!

2. У меня возникла проблема, когда я попытался загрузить pickle, для которого требовался модуль, реорганизованный под новым именем. Этот ответ отлично сработал, чтобы автоматически переназначить старый импорт на новый