繁体   English   中英

如何正确使用装饰器? (TypeError: wrapper() 采用 0 位置 arguments 但给出了 1)

[英]How to properly use a decorator? (TypeError: wrapper() takes 0 positional arguments but 1 was given)

我正在尝试编写一个装饰器,在使用 function 之前检查特定包是否可用。

在下面的示例中, numpy不应引发错误,但non_existent_test_package应通知用户他们需要安装软件包才能使用此功能。 这样做的目的是减少依赖性。

基于@henry-harutyunyan 建议的更新


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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM