[英]How to properly use a decorator? (TypeError: wrapper() takes 0 positional arguments but 1 was given)
我正在尝试编写一个装饰器,在使用 function 之前检查特定包是否可用。
在下面的示例中, numpy
不应引发错误,但non_existent_test_package
应通知用户他们需要安装软件包才能使用此功能。 这样做的目的是减少依赖性。
import numpy as np
import importlib
def check_available_packages(packages):
if isinstance(packages,str):
packages = [packages]
packages = np.asarray(sorted(set(packages)))
def wrapper(func):
installed = list()
for package in packages:
try:
globals()[package] = importlib.import_module(package)
installed.append(True)
except ImportError:
installed.append(False)
installed = np.asarray(installed)
assert np.all(installed), "Please install the following packages to use this functionality:\n{}".format(", ".join(map(lambda x: "'{}'".format(x), packages[~installed])))
return func
return wrapper
@check_available_packages(["numpy"])
def f():
print("This worked")
@check_available_packages(["numpy", "non_existent_test_package"])
def f():
print("This shouldn't work")
# ---------------------------------------------------------------------------
# AssertionError Traceback (most recent call last)
# <ipython-input-222-5e8224fb30bd> in <module>
# 23 print("This worked")
# 24
# ---> 25 @check_available_packages(["numpy", "non_existent_test_package"])
# 26 def f():
# 27 print("This shouldn't work")
# <ipython-input-222-5e8224fb30bd> in wrapper(func)
# 15 installed.append(False)
# 16 installed = np.asarray(installed)
# ---> 17 assert np.all(installed), "Please install the following packages to use this functionality:\n{}".format(", ".join(map(lambda x: "'{}'".format(x), packages[~installed])))
# 18 return func
# 19 return wrapper
# AssertionError: Please install the following packages to use this functionality:
# 'non_existent_test_package'
现在装饰器似乎在运行时检查包是否存在,而不是在实际调用 function 时。 如何调整此代码?
这将起作用
import numpy as np
import importlib
def check_available_packages(packages):
if isinstance(packages, str):
packages = [packages]
packages = np.asarray(sorted(set(packages)))
def decorator(func):
def wrapper():
installed = list()
for package in packages:
try:
globals()[package] = importlib.import_module(package)
installed.append(True)
except ImportError:
installed.append(False)
installed = np.asarray(installed)
assert np.all(installed), "Please install the following packages to use this functionality:\n{}".format(
", ".join(packages[~installed]))
func()
return wrapper
return decorator
@check_available_packages(["numpy"])
def foo():
print("This worked")
@check_available_packages(["numpy", "non_existent_test_package"])
def bar():
print("This shouldn't work")
foo()
bar()
问题是您拥有的wrapper()
function 正在接受参数,而根据定义它不需要任何参数。 所以在这个语句wrapper(_)
中传递_
就可以了。
_
是虚拟的,不能使用,但它仍然是一些东西。 IDE 也不会抱怨未使用的变量。
只有在调用 function 时才执行装饰器,您需要使用上述装饰器工厂。 有关更多详细信息,请参阅此参考。
如果您希望在调用底层 function 时进行检查,则需要对它进行额外的包装:
import functools
def check_available_packages(packages):
if isinstance(packages,str):
packages = [packages]
packages = sorted(set(packages))
def decorator(func): # we need an extra layer of wrapping
@functools.wraps(func) # the wrapper replaces func in the global namespace
def wrapper(*args, **kwargs): # so it needs to accept any arguments that func does
missing_packages = [] # no need for fancy numpy array indexing, a list will do
for package in packages:
try:
globals()[package] = importlib.import_module(package)
except ImportError:
missing_packages.append(package)
assert not missing_packages, "Please install the following packages to use this functionality:\n{}".format(", ".join(missing_packages))
return func(*args, **kwargs) # call the function after doing the library checking!
return wrapper
return decorator
我从代码中删除了对numpy
的依赖,这对我来说似乎完全没有必要,特别是如果您正在测试是否安装了numpy
,那么要求它进行检查是没有意义的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.