Python 中可选导入的惯用方式

8
我有一个包,允许用户使用其中任何一个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

这不符合Python的风格。我知道有一些包,比如holoviews或tensorflow,允许您指定后端。当然,它们比我的复杂度高出数个数量级,但它们必须处理相同的模式。

我应该如何修改这段代码呢?从技术上讲,如果他们打算使用pyodbc但没有安装它,我的程序将不会警告他们,而是在运行时出现错误。因此,这实际上超出了美学或哲学范畴;这是技术上容易出错的代码。

您会如何处理这种情况?

另外,以下是调用代码的示例:

connect('Playground', package='pymssql')
4个回答

13
try: 
    import pyodbc
except ImportError: 
    pyodbc = None

然后稍后:

if pyodbc is None and user_wants_to_use_pyodbc:
    print_warning()
    raise SomeConfigurationErrorOrSuch()

这种方法对于少量选项效果很好。如果您有足够多的选项需要抽象出来,那么可以使用importlib模块在程序控制下导入模块。


我仍然不相信任何答案中都提供了理想解决方案,但感谢Sam,我选择了你的解决方案,它似乎最适合我的需求。正如你建议的那样,在我的代码后面,我做了这个: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.') - MetaStack
另一个好的答案:https://dev59.com/L14c5IYBdhLWcg3w7-F4#27361558 - Sukombu

4
我会使用importlib中的import_module函数:
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))

2

我使用过与上面答案类似的东西,但是有时候你可能需要模拟一个对象来欺骗lint。

try:
    from neuralprophet import NeuralProphet
    using_neuralprophet = True
except ImportError:
    class NeuralMock:
       whatever=False
    using_neuralprophet = False
    NeuralProphet = NeuralMock()

Source: timemachines


0

你可以将导入语句放在文件的其他位置。"重新导入"某个模块实际上并不会做任何事情,因此频繁地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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接