如何正确使用装饰器?(TypeError: wrapper()需要0个位置参数,但给了1个)

问题描述 投票:0回答:1

我想写一个装饰器,在使用函数之前检查特定的包是否可用。

在下面的例子中,我想写一个装饰器,在使用函数之前检查特定的包是否可用。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'

现在,decorator似乎是在运行时检查包是否存在,而不是在实际调用函数时检查。 我如何调整这段代码?

python module package decorator python-decorators
1个回答
1
投票

这将工作

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() 你的函数正在接受一个参数,而根据定义,它不需要任何参数。所以传递 _ 在此声明中 wrapper(_) 会做的工作。

_ 是虚的,不能用,但它仍然是东西。IDE也不会抱怨这个未使用的变量。

要想只在函数被调用时执行装饰器,你需要像上面一样使用装饰器工厂。请看 本参考文献 了解更多细节。


1
投票

如果你想让检查在底层函数被调用时发生,你需要一个额外的层次来包装它。

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 安装了,要求它来进行检查是没有意义的。

© www.soinside.com 2019 - 2024. All rights reserved.