Python 使用不同的内部导入从模块导入函数两次

Python import functions from module twice with different internal imports

我有一个模块 lib 需要 numpy。因此,例如,假设我有一个看起来像

的假设函数
import numpy
def doSomething(x):
    return numpy.sqrt(x)

现在要在单独的模块中使用该函数,我会将名称导入为

from lib import doSomething
...

这是棘手的部分...现在我想公开 doSomething 的另一个版本,其中 numpy 是从另一个库(特别是 autograd)导入的.所以,例如,我希望能够有一个函数

from autograd import numpy
def doSomething(x):
    return numpy.sqrt(x)

这些函数之间的唯一区别是 numpy 从哪里导入。特别是,我想在同一代码中使用 doSomething 的两个版本,也就是说,我想以某种方式导入 doSomething 两次...一次使用默认 numpy,还有一次来自 autograd 的 numpy。像这样:

useAutograd = False
from lib(useAutograd) import doSomething
useAutograd = True
from lib(useAutograd) import doSomething as doSomethingAutograd 

我知道这里有几个选项,但 none 令人满意。

  1. 我会复制一份代码库,一份使用默认的 numpy,一份使用 autograd 中的 numpy。这很糟糕,因为它需要我维护两个代码库,它们是彼此的副本,只有不同的导入。

  2. 我可以放入条件导入:

    try:
        from autograd import numpy
    except ImportError:
        import numpy
    

    这很糟糕,因为用户无法控制导入的版本...如果他们有 autograd,那么他们必须使用该版本。

  3. 我可以定义一个环境变量来控制导入

    import os
    if os.environ.get('AUTOGRADNUMPY'):
        try:
            from autograd import numpy
        except ImportError:
            import numpy
    else:
        import numpy
    

    缺点是虽然用户可以控制导入,但他们只能选择一个版本(据我所知)。所以他们不能在同一代码中使用这两个版本。

这个用例是否有更好的替代方案?

感兴趣者的背景:

Autograd 有自己的一组函数,模仿 numpy 并允许使用自动微分(与 tensorflow 相同)轻松计算导数,而不需要昂贵的数值微分。

但是,他们的 numpy 实现不是最优化的版本 (AFAIK)。因此,允许用户在需要函数的 jacobian 时使用带有 autograd 导入的版本,并在不需要时使用默认的、高度优化的 numpy 包是有利的。

您无需执行任何操作即可实现此目的。

如果你这样做

from lib import doSomething
from lib_with_autograd import doSomething as doSomethingAutograd

这些函数中的每一个都使用在其特定模块中导入的 numpy。所以 doSomethingAutograd 使用 lib_with_autograd 中导入的那个,doSomething 使用 lib

中导入的那个

如果您希望避免重复您的代码库,请将您的界面改为 class。例如:

class using_numpy:
    import numpy

    @classmethod
    def do_something(cls, x):
        return cls.numpy.sqrt(x)

class using_autograd(using_numpy):
    from autograd import numpy

现在 using_numpy.do_something 将使用 numpy,而 using_autograd.do_something 将使用 autograd.numpy

或者,如果您对 classmethod 感到不舒服,您可以将接口实例化为 class,例如:

class interface:
    def __init__(self, mdl):
        self.mdl = mdl

    def do_something(self, x):
        return self.mdl.sqrt(x)

import numpy
import autograd

with_numpy = interface(numpy)
with_autograd = interface(autograd.numpy)

由于 python 中的所有内容都是一个对象,包括模块,您可以这样做:

def doSomething(x, numpy=None):
    if numpy is None:
        import numpy
    return numpy.sqrt(x)

然后你可以不设置numpy调用函数,那么它会使用默认的numpy。如果你想使用另一个 numpy,就这样调用它:

from autograd import numpy as autograd_numpy
doSomething(x, numpy=autograd_numpy)