Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从Decorator导入Python模块_Python_Python 3.x_Python Import_Python Decorators - Fatal编程技术网

从Decorator导入Python模块

从Decorator导入Python模块,python,python-3.x,python-import,python-decorators,Python,Python 3.x,Python Import,Python Decorators,我正在用Python3开发一个应用程序,我所做的是非传统的 cx_Oracle是一个很难设置的模块,对于我的应用程序来说,它是一个可选的依赖项。我想做的是将模块的导入包装在装饰器中,只放在使用它的函数之上。这将避免在我的模块顶部导入,并允许它不被设置 class Loader(): def load_cx_oracle(func): def inner(*args, **kwargs): # Additional code before impo

我正在用Python3开发一个应用程序,我所做的是非传统的

cx_Oracle是一个很难设置的模块,对于我的应用程序来说,它是一个可选的依赖项。我想做的是将模块的导入包装在装饰器中,只放在使用它的函数之上。这将避免在我的模块顶部导入,并允许它不被设置

class Loader():
    def load_cx_oracle(func):
        def inner(*args, **kwargs):

            # Additional code before import.

            import cx_Oracle

            return func(*args, **kwargs)
        return inner

    @load_cx_oracle
    def function_using_cx_oracle(self):
        conn = cx_Oracle.connect()

但是,当我尝试上述操作时,我得到了
name错误:未定义名称“cx\U Oracle”

如果需要可选导入,请在模块开头使用try except:

try:
    import cx_Oracle
except ImportError:
    cx_Oracle = None

import
仅在使用模块名称的命名空间中绑定模块名称。如果在函数中导入cx\u Oracle,则名称
cx\u Oracle
仅在该函数中可用

但是,您可以使用
global
将分配设置为全局。将装饰器更改为使用:

global cx_Oracle
import cx_Oracle

这种方法是否真的是正确的还有争议。根据代码的使用方式,如果用户希望
cx\u Oracle
可用,只调用一个函数,按照Daniel的回答使用try-wrapped导入,或者通过一些外部设置(例如,配置文件)定义加载,可能会更干净.

接受的答案有几个问题。其中最重要的是,每次调用函数时,它都会运行导入逻辑。第二,装饰器必须在它所使用的相同模块中定义,否则装饰器和装饰器将具有不同的全局变量。您可以通过函数的
\uuu globals\uuu
属性直接访问函数的全局。在执行导入逻辑之前,代码示例首先检查函数的全局函数中是否存在模块。该示例还使用
functools.wrapps
decorator在使用
help(func)
等工具时保留文档字符串、函数名和参数名


这解决了我的问题。出于两个原因,我希望与一位装饰师一起完成这些功能。我的示例中遗漏了一些配置信息。此应用程序有时只运行其中一个函数。基本上,只运行列出的一个函数将被传递参数。正如@Dunes提到的,只有在装饰器与装饰函数定义在同一个模块中时,才使用
global
。这也会防止从多个函数调用中多次导入。例如,如果您确实为
def subtract(self,x,y)
包含了decorator,那么它仍然只能加载一次,还是对每个函数单独加载?我知道目前的答案确实要求每个函数,但我特别想知道,是否可以修改您的导入逻辑,以防止同一模块中的函数之间发生这种情况。是的,这样可以防止多次导入,因为同一模块中声明的函数共享相同的全局变量。每次运行导入逻辑只不过是检查“运算符”即可已经在globals中--导入检查的第一件事是模块是否已经导入,在这种情况下,它是一个简单的名称分配。然而,你的第二点才是真正的问题——所以+1才是真正的问题。@EthanFurman OP说他所做的不仅仅是导入。如果只是导入,那么我同意检查是没有意义的。你是对的,它只在同一个模块中工作。在OP的示例中,decorator本身不仅在同一个模块中定义,而且在与修饰函数相同的类中定义,我认为这意味着跨模块使用不是问题(尽管这可能是不现实的)。无论如何,我认为使用
func.\uuuu globals\uuuuu
比使用
global
更加可疑,如果必须这样做,最好完全使用另一种方法。
from functools import wraps

def load_operator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if "operator" not in func.__globals__:
            # setup logic -- only executed once
            import operator
            func.__globals__["operator"] = operator
        return func(*args, **kwargs)
    return wrapper

class A:
    @load_operator
    def add(self, x, y):
        return operator.add(x, y)

    def subtract(self, x, y):
        return operator.subtract(x, y)

a = A()
try:
    a.subtract(1, 2)
except NameError:
    print("operator not yet loaded")
print(a.add(1, 2))
print(A.add)