Python 使用导入内联是否可能产生负面影响?

Python 使用导入内联是否可能产生负面影响?,python,Python,我正在开发一个Python程序,该程序需要能够运行,即使它缺少某些功能所需的库。(编辑:我编写了一个小代码来实现建议的最佳解决方案,并且使用了doctest。) 我将这些库的导入语句内联到使用它们的函数中,而不是放在Python文件的顶部,从而解决了这个问题。这意味着即使没有库,也可以很好地加载文件,当然,如果尝试调用其中一个函数,也会抛出ImportError 这项工作非常有效,我发现自己有时也在为标准库模块做这项工作——但现在我想知道这样做是否会产生一些隐藏成本 基线代码: import n

我正在开发一个Python程序,该程序需要能够运行,即使它缺少某些功能所需的库。(编辑:我编写了一个小代码来实现建议的最佳解决方案,并且使用了doctest。)

我将这些库的导入语句内联到使用它们的函数中,而不是放在Python文件的顶部,从而解决了这个问题。这意味着即使没有库,也可以很好地加载文件,当然,如果尝试调用其中一个函数,也会抛出ImportError

这项工作非常有效,我发现自己有时也在为标准库模块做这项工作——但现在我想知道这样做是否会产生一些隐藏成本

基线代码:

import numpy

def foo(): 
  return numpy.array([])

def bar(): 
  return numpy.array([1, 2, 3])
使用内联导入的代码:

def foo(): 
  import numpy
  return numpy.array([])

def bar(): 
  import numpy
  return numpy.array([1, 2, 3])
编辑:

我完全同意不内联标准库代码,这显然很糟糕

我现在认为保护进口是正确的解决方案

特别是,我对调用进行了一些计时测试,虽然时间差对于大多数应用程序来说可能并不显著,但它是可以感知的(我知道这很好!)

在琐事上

import numpy
def f():
  return numpy
在我的机器上重复100000次需要180毫秒,但是

def f():
  import numpy
  return numpy
大约需要870毫秒

非常粗略的一点是,这需要花费多达四个简单的函数调用——在大多数情况下是显而易见的,但并不重要。尽管如此,最好避免这样做,如果它不花费你什么


在实验中,我还意识到了内联导入的另一个缺点——当调用函数时,这些导入会在不可预测的时间发生。在我的应用程序中,它具有实时元素,这是不可接受的。

不,这样做应该没有缺点或隐藏的成本。模块被缓存并且只执行一次,即使您多次导入它们。然后导入只是(重新)设置模块的本地引用。

不太可能

导入只会发生一次,但可能发生在意外的时间(对于用户),即第一次调用执行导入的函数时


此外,这也是可读性的问题——如果按照惯例在顶部进行导入,那么代码的每个读者都会立即知道其依赖项是什么。当导入发生在模块的第284行时,这种清晰性可能会丢失…

没有明显的性能影响,但会使代码变得混乱。如果您决定添加新的导入或必须更改旧的导入,您必须在任何地方更改它,而不是只在一个地方更改

此外,您应该确保这是有文档记录的。如果库似乎正确导入,但在调用特定函数后很长时间内失败,则某些用户可能会感到恼火。此外,虽然整体性能没有受到影响,但可能会出现性能“重新调整”,这会在一些意想不到的地方导致性能下降。第一次调用导入
numpy
的函数时,它必须进行导入,这需要时间。用户也可能会发现这是不可取的,并希望所有缓慢的导入都提前完成

您可以很容易地通过顶部的所有导入获得类似的效果:

try:
    import numpy
except ImportError:
    warnings.warn("Numpy not available, some functions may not work!")

稍后尝试使用尝试访问
numpy
的函数将失败,并出现名称错误。通过使用警告(或只是打印/记录的消息),您还可以提前通知某些事情将不起作用,而不是稍后突然失败。

您这样做并没有遵循PEP 8。在标准库导入的情况下,您这样做没有充分的理由,这是非常糟糕的,足以让一些人避开您的代码(或者至少礼貌地唠叨您不应该这样做)

当然,政治公众人物第8版并不是无缘无故地这么说的。在这种情况下,有一个比个人偏好和一致性更好的原因:如果您将所有导入放在顶部,您可以很容易地找到模块的依赖关系。如果导入分布在整个文件中,这将变得更加麻烦。此外,现在几乎每个对库的调用都会引发
ImportError
,这是非常不幸的:通常的工作流是导入所有内容,如果可以导入,则假定它可以工作(这是设置virtualenv时有用的手动测试)。写得不太好的代码可能会开始执行I/O之类的操作,并在其间调用您的函数(不希望出现
ImportError
),然后对错误感到惊讶并无法正确清理

还有一点开销,即每次调用包含导入的函数时都会执行一些额外的指令。然而,在大多数情况下,此开销相当小,并且不会导入模块两次(或三次,或无数次)。当然,它也违反了干法


当遇到这个问题时,我和其他人选择将导入放在文件的顶部,周围是
try:。。。除了导入错误:
。然后,您可以指定一个虚拟值、发出警告、记录某些内容,或者执行任何在您的案例中有意义的操作。您甚至可以导入一个替换模块(例如,当支持没有特定模块的旧Python版本时)或您自己提供的存根模块。

现在我正在查看反汇编代码,在内联案例中肯定添加了额外的指令。我将尝试对它们进行计时…性能可能无关紧要,但函数的丑陋混乱是不好的-我将仅出于这个原因避免这种情况。您还可以将一个对象从
\uuuu getattribute\uuuu()
分配给
numpy
,以使其超级清晰。Lattyware:好主意,我就这么做!该系统将被艺术家使用,因此可理解的错误消息是一个优先事项。事实证明,您确实需要使用
\\\uu getattr\\\u\\\
-我更新了我的原始问题,添加了实现此功能的代码链接以及doct