从Decorator导入Python模块
我正在用Python3开发一个应用程序,我所做的是非传统的 cx_Oracle是一个很难设置的模块,对于我的应用程序来说,它是一个可选的依赖项。我想做的是将模块的导入包装在装饰器中,只放在使用它的函数之上。这将避免在我的模块顶部导入,并允许它不被设置从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
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)