【问题标题】:Is there a way to do local import for arguments in python decorators and types in annotations?有没有办法对 python 装饰器中的参数和注释中的类型进行本地导入?
【发布时间】:2020-12-22 23:04:28
【问题描述】:

我正在编写一个包含许多函数的 python 包,我在其中设置了本地导入以避免缺少包错误(这样即使我没有安装某个包,我的其他函数仍然可以工作)。

但是,我已经编写了一个装饰器来包装我的一些函数,但是如果装饰器中的参数需要某个包,我就无法进行本地导入。当我需要在函数参数的注解中从另一个包中导入某种类型时,会出现类似的本地导入问题。

def do_fancy_stuff(arg):
    def wrapped_func(func):
        def inner_func():
            print(f"Using {arg} on the target function!")
            # Some other statements to define the inner function, for simplicity here I just run func()
            func()
        return inner_func
    return wrapped_func

@do_fancy_stuff("A")
def some_func1():
    import numpy
    pass

@do_fancy_stuff("B")
def some_func2():
    pass

# How can I avoid an import statement here and put it in the decorator?
# import numpy
@do_fancy_stuff(numpy.inf) # error here
def some_func3():
    pass

# Similarly I need to import numpy outside the function if I want to do the annotation...
# import numpy
def other_func1(input_array : numpy.ndarray): # Error
    pass

我有一个想法,对函数外的所有导入使用 try-except。但这会破坏我最初的想法,即函数的所有依赖项都应该在该函数中本地定义。

另一个想法是使用exec 或者写一个小函数来包装它,但它似乎使代码的可读性大大降低:

def _get_numpy_inf():
    import numpy
    return numpy.inf

有没有更好的方法直接在装饰器语句和注释中进行本地导入,比如?

@do_fancy_stuff(import numpy; numpy.inf) 
(import numpy; @do_fancy_stuff(numpy.inf) )
def other_func1(input_array : import numpy; numpy.ndarray):
# Just some imaginations, it doesn't work

【问题讨论】:

    标签: python python-decorators


    【解决方案1】:

    我会尝试回答你的问题,但在我这样做之前,请听我说完:

    程序员永远不必编写假设依赖项可能丢失的代码

    如果您的项目的任何用户使用需求文件安装项目的依赖项,则依赖项都应该可用,允许所有文件和函数按预期运行。

    在文件顶部导入模块是一种良好的编程习惯,它使管理文件的依赖关系变得简单明了,同时还允许在函数之外的地方使用这些模块。

    虚拟环境

    为了成功管理项目的依赖关系,通常使用虚拟环境和任何 python 项目的需求文件。这种做法在python以外的语言中也广泛使用。

    要在名为 venv 的文件夹中创建虚拟环境,请在 shell 中使用以下命令:python -m venv venv

    要激活该环境,请使用以下命令:source venv/bin/activate

    安装依赖项

    要安装项目的所有依赖项,您可以调用pip install -r requirements.txt。您也可以在不使用虚拟环境的情况下执行此步骤。如果您选择这样做,它将在您的全局 python 模块中安装依赖项。这可能不是任何时候都需要的,因此存在虚拟环境。

    需求文件非常简单。它只是列出了您对各个行的依赖项。如下所示:

    numpy
    matplotlib
    another_dependency
    

    等等……

    回答您的问题

    尽管我真的不建议使用您当前拥有的导入结构,但我会建议以下解决方案:

    使用另一个函数围绕依赖值创建范围。从这个新函数返回修饰函数。

    # How can I avoid an import statement here and put it in the decorator?
    def some_func3():
        import numpy
        @do_fancy_stuff(numpy.inf) # error here
        def f():
            pass
        return f
    
    # Similarly I need to import numpy outside the function if I want to do the annotation...
    def other_func1():
        import numpy
        def f(input_array : numpy.ndarray): # Error
            pass
        return f
    

    调用函数:

    some_func3()()
    other_func1()(numpy.ones(20))
    

    查看这样的解决方案应该清楚地表明,使用这些函数的语法会变得笨拙且难以阅读。按照上面的指南,答案应该会让你更容易前进:)

    【讨论】:

    • 很有帮助,但语气可能令人反感,尤其是对于刚接触软件开发的人来说。我可以建议用更温和的语气来平滑吗? (您的回答很有帮助。我希望它能够轻松地到达目标受众。)
    • 我同意,我会尝试一些更轻松的修改:D
    • @YaakovBressler 我的意思是,这个答案似乎在向后弯腰,以免令人反感。比我认为的好。具体来说,您有什么建议来改变语气?
    • 也许这就是我阅读它的方式 --> 带有粗体标题等。 @juanpa.arrivillaga --> 在第二次阅读时,我同意,这是尊重。
    • 我发现改变句子来暗示好的东西,而不是攻击坏的东西让人感觉不像是在说话,所以我做了一些改变。
    猜你喜欢
    • 2021-06-15
    • 2020-07-22
    • 2012-05-11
    • 2022-09-21
    • 1970-01-01
    • 2020-05-16
    • 2021-11-30
    • 1970-01-01
    • 2019-06-16
    相关资源
    最近更新 更多