在Python中导入很少使用的包的最佳实践

时间:2017-06-29 16:15:42

标签: python python-import

我的Python软件包依赖于一个外部库来完成它的一些功能。这是一个非Python包,可能很难安装,所以我希望用户仍能使用我的软件包,但在使用任何依赖于这个非Python软件包的函数时会失败。

这是什么标准做法?我只能在使用它的方法中导入非Python包,但我真的很讨厌这样做

我目前的设置:

myInterface.py
myPackage/
--classA.py
--classB.py

接口脚本myInterface.py导入classAclassBclassB导入非Python包。如果导入失败,我会打印警告。如果调用myMethod并且未安装软件包,则下游会出现一些错误,但我不会在任何地方发现它,也不会警告用户。

每次调用接口脚本时都会导入

classB,因此我无法解决任何问题,这就是我加入pass的原因。就像我上面说的那样,我可以在方法中导入并让它在那里失败,但我真的很喜欢将所有的导入保存在一个地方。

来自classB.py

try:
    import someWeirdPackage
except ImportError:
    print("Cannot import someWeirdPackage")
    pass

class ClassB():
    ...
    def myMethod():
        swp = someWeirdPackage()
        ...

4 个答案:

答案 0 :(得分:3)

如果您只导入一个外部库,我会按照以下方式进行操作:

try:
    import weirdModule
    available = True
except ImportError:
    available = False

def func_requiring_weirdmodule():
    if not available:
        raise ImportError('weirdModule not available')
    ...

只有在您想要提供更多描述性错误时才需要进行条件和错误检查。如果没有,你可以省略它,让python在尝试调用非导入模块时抛出相应的错误,就像在当前设置中那样。

如果多个函数确实使用weirdModule,则可以将检查包装到函数中:

def require_weird_module():
    if not available:
        raise ImportError('weirdModule not available')

def f1():
    require_weird_module()
    ...

def f2():
    require_weird_module()
    ...

另一方面,如果您有多个库由不同的函数导入,您可以动态加载它们。虽然它看起来不漂亮,但python会缓存它们并且没有任何问题。我会使用importlib

import importlib

def func_requiring_weirdmodule():
    weirdModule = importlib.import_module('weirdModule')

同样,如果您的多个函数导入复杂的外部模块,您可以将它们包装到:

def import_external(name):
    return importlib.import_module(name)

def f1():
    weird1 = import_external('weirdModule1')


def f2():
    weird2 = import_external('weirdModule2')

最后,您可以创建一个处理程序来防止两次导入相同的模块,这些内容类似于:

class Importer(object):

    __loaded__ = {}

    @staticmethod
    def import_external(name):
        if name in Importer.__loaded__:
            return Importer.__loaded__[name]
        mod = importlib.import_module(name)
        Importer.__loaded__[name] = mod
        return mod

def f1():
    weird = Importer.import_external('weird1')


def f2():
    weird = Importer.import_external('weird1')

虽然我很确定importlib会对场景进行高速缓存,但您并不需要手动缓存。

简而言之,虽然看起来很难看,但在python中动态导入模块没有任何问题。事实上,很多图书馆都依赖于此。另一方面,如果它仅仅是针对3种方法访问1个外部函数的特殊情况,请使用您的方法或我的第一个方法,以防您无法添加自定义处理。

答案 1 :(得分:2)

我不确定在这种情况下是否有任何最佳做法,但如果不支持,我会重新定义该功能:

def warn_import():
    print("Cannot import someWeirdPackage")

try:
    import someWeirdPackage
    external_func = someWeirdPackage
except ImportError:
    external_func = warn_import


class ClassB():
    def myMethod(self):
        swp = external_func()


b = ClassB()
b.myMethod()

答案 2 :(得分:0)

您可以为这两种情况创建两个单独的类。当包存在时,将使用第一个。第二个将在包不存在时使用。

class ClassB1():
    def myMethod(self):
        print("someWeirdPackage exist")
        # do something

class ClassB2(ClassB1):
    def myMethod(self):
        print("someWeirdPackage does not exist")
        # do something or raise Exception

try:    
    import someWeirdPackage
    class ClassB(ClassB1):
        pass
except ImportError:
    class ClassB(ClassB2):
        pass

答案 3 :(得分:0)

您还可以使用以下方法来克服您所面临的问题。

class UnAvailableName(object):

    def __init__(self, name):
        self.target = name

    def __getattr_(self, attr):
        raise ImportError("{} is not available.".format(attr))


try:
    import someWeirdPackage
except ImportError:
    print("Cannot import someWeirdPackage")
    someWeirdPackage = someWeirdPackage("someWeirdPackage")

class ClassB():
    def myMethod():
        swp = someWeirdPackage.hello()

a = ClassB()
a.myMethod()