У меня есть пакет, который позволяет пользователю использовать любой из 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')
try: import pyodbc
except ImportError: pyodbc = None
затем позже, если pyodbc - None и user_wants_to_use_pyodbc: print_warning raise SomeConfigurationErrorOrSuch
Этот подход хорошо подходит для небольшого числа вариантов. Если у вас достаточно вариантов, чтобы абстрагироваться от этого подхода, вы можете использовать модуль importlib
для импорта модулей под управлением вашей программы.
Я бы использовал 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))
Вы можете поместить импорт в места, отличные от начала файла. "Повторное импортирование" что-то на самом деле ничего не делает, поэтому часто не требуется дорогостоящий 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()
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.')