питонский путь необязательного импорта

1

У меня есть пакет, который позволяет пользователю использовать любой из 4 пакетов, которые они хотят подключить к базе данных. Он отлично работает, но я недоволен тем, как я импортирую вещи.

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

import pyodbc
import pymssql
import turbodbc
from ibmdbpy.base import IdaDataBase

В настоящее время у меня есть следующая ситуация. Я пытаюсь импортировать все из них, но те, которые не импортируют, без проблем, моя программа просто предполагает, что они не будут вызваны, и если это они ошибки:

# some envs may not have all these packages installed so we try each:

try:
    import pyodbc
except:
    pass

try:
    import pymssql
except:
    pass

try:
    import turbodbc
except:
    pass

try:
    from ibmdbpy.base import IdaDataBase
except:
    pass

Это не кажется пифоническим. Поэтому я знаю, что есть такие пакеты, как holoviews или tensorflow, которые позволяют вам указать бэкэнд. Они, конечно, на порядок более сложные, чем у меня, но они должны обрабатывать один и тот же образец.

Как я могу сделать этот код правильно? это технически багги, потому что, если они намереваются использовать pyodbc но не имеют его, моя программа не будет их предупреждать, это будет ошибка во время выполнения. Так что действительно это выходит за рамки эстетики или философии; это технически подверженный ошибкам код.

Как бы вы справились с этой ситуацией?

Fyi, вот пример того, как вызывается код:

connect('Playground', package='pymssql')
Теги:
import

3 ответа

1
Лучший ответ
try: import pyodbc
except ImportError: pyodbc = None

затем позже, если pyodbc - None и user_wants_to_use_pyodbc: print_warning raise SomeConfigurationErrorOrSuch

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

  • 0
    Я до сих пор не верю, что среди всех ответов было найдено идеальное решение, но, сэм, ваше решение было тем, с которым я согласился, и оно, казалось, служило моим целям лучше всего. позже в моем коде, как вы и предлагали, я сделал это: return locals()[f'use_{package}_db2']() if package in ['pyodbc','IdaDataBase'] and package is not None else raise ValueError(f'{package} module not imported. Please pip install.')
2

Я бы использовал import_module из importlib:

from importlib import import_module

modules_to_import = ['pyodbc', 'pymssql', 'turbodbc', 'ibmdbpy.base.IdaDataBase']
for m in modules_to_import:
    try:
        globals()[m.split('.')[-1]] = import_module(m)
    except ModuleNotFoundError:
        print('Module {} not found'.format(m))
1

Вы можете поместить импорт в места, отличные от начала файла. "Повторное импортирование" что-то на самом деле ничего не делает, поэтому часто не требуется дорогостоящий import x:

def switch(x):
    if x == 'a':
        import json
        json.load(file)
    elif x == 'b':
        import pandas as pd
        pd.read_csv(file)

Вы также можете использовать importlib для динамического импорта модулей. Это особенно полезно, если у вас есть несколько реализаций того же API, которые вы хотите выбрать между

class Connection:
    def __init__(self, driver_module, driver_name):
        # or driver_module, driver_name = full_path.rsplit('.', 1)
        self.driver = get_attr(importlib.load_module(driver_module), driver_name)()
    def method(self):
        return self.driver.do()

Ещё вопросы

Сообщество Overcoder
Наверх
Меню