在 Python 中导入很少使用的包的最佳实践
Best practices for importing rarely used package in Python
我的 Python 包的一些功能依赖于外部库。这是一个非 Python 包,可能很难安装,所以我希望用户仍然可以使用我的包,但在使用依赖于此非 [=35= 的任何功能时它会失败] 包裹。
这个的标准做法是什么?我只能在使用它的方法中导入非Python包,但我真的很讨厌这样做
我当前的设置:
myInterface.py
myPackage/
--classA.py
--classB.py
接口脚本 myInterface.py
导入 classA
和 classB
并且 classB
导入非 Python 包。如果导入失败,我会打印一条警告。如果调用了 myMethod
并且未安装软件包,下游将出现一些错误,但我不会在任何地方捕获它,也不会警告用户。
每次调用接口脚本时都会导入 classB
,所以我不会在那里出现任何失败,这就是我包含 pass
的原因。就像我上面说的,我可以在方法内部导入并让它在那里失败,但我真的很喜欢将所有导入保存在一个地方。
来自classB.py
try:
import someWeirdPackage
except ImportError:
print("Cannot import someWeirdPackage")
pass
class ClassB():
...
def myMethod():
swp = someWeirdPackage()
...
我不确定在这种情况下是否有任何最佳实践,但如果不支持,我会重新定义该函数:
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()
如果您只导入一个外部库,我会按照以下思路进行操作:
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 个外部函数的特殊情况,请使用您的方法或我的第一个方法,以防您无法添加自定义 sception 处理。
您可以为这两种情况创建两个单独的 类。第一个将在包存在时使用。第二个将在包不存在时使用。
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
您也可以使用下面给出的方法来解决您面临的问题。
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()
我的 Python 包的一些功能依赖于外部库。这是一个非 Python 包,可能很难安装,所以我希望用户仍然可以使用我的包,但在使用依赖于此非 [=35= 的任何功能时它会失败] 包裹。
这个的标准做法是什么?我只能在使用它的方法中导入非Python包,但我真的很讨厌这样做
我当前的设置:
myInterface.py
myPackage/
--classA.py
--classB.py
接口脚本 myInterface.py
导入 classA
和 classB
并且 classB
导入非 Python 包。如果导入失败,我会打印一条警告。如果调用了 myMethod
并且未安装软件包,下游将出现一些错误,但我不会在任何地方捕获它,也不会警告用户。
classB
,所以我不会在那里出现任何失败,这就是我包含 pass
的原因。就像我上面说的,我可以在方法内部导入并让它在那里失败,但我真的很喜欢将所有导入保存在一个地方。
来自classB.py
try:
import someWeirdPackage
except ImportError:
print("Cannot import someWeirdPackage")
pass
class ClassB():
...
def myMethod():
swp = someWeirdPackage()
...
我不确定在这种情况下是否有任何最佳实践,但如果不支持,我会重新定义该函数:
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()
如果您只导入一个外部库,我会按照以下思路进行操作:
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 个外部函数的特殊情况,请使用您的方法或我的第一个方法,以防您无法添加自定义 sception 处理。
您可以为这两种情况创建两个单独的 类。第一个将在包存在时使用。第二个将在包不存在时使用。
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
您也可以使用下面给出的方法来解决您面临的问题。
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()