【问题标题】:Error using decorator: decorator() takes 1 positional argument but 2 were given使用装饰器时出错:装饰器()采用 1 个位置参数,但给出了 2 个
【发布时间】:2019-04-10 18:45:34
【问题描述】:

我正在阅读和关注 Pro Python 书,我使用书中相同的代码创建了这两个装饰器 annotation_decoratortypesafe,但是当我尝试运行此代码时,我收到:

TypeError: decorator() 接受 1 个位置参数,但给出了 2 个

代码和书上的一样,我不知道为什么会这样,你们能发现什么问题吗?我在这里托管了 PoC,以防你想测试:https://repl.it/repls/GlassNotedPayware

import functools
import inspect
from itertools import chain


def annotation_decorator(process):
    """
    Creates a decorator that processes annotations for each argument passed
    into its target function, raising an exception if there's a problem.
    """
    @functools.wraps(process)
    def decorator(func):
        spec = inspect.getfullargspec(func)
        annotations = spec.annotations

        defaults = spec.defaults or ()
        defaults_zip = zip(spec.args[-len(defaults):], defaults)
        kwonlydefaults = spec.kwonlydefaults or {}

        for name, value in chain(defaults_zip, kwonlydefaults.items()):
            if name in annotations:
                process(value, annotations[name])

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # Populate a dictionary of explicit arguments passed positionally
            explicit_args = dict(zip(spec.args, args))
            new_args = []
            new_kwargs = {}
            keyword_args = kwargs.copy()

            # Deal with explicit arguments passed positionally
            for name, arg in explicit_args:
                if name in annotations:
                    new_args.append(process(arg, annotations[name]))

            # Add all explicit arguments passed by keyword
            for name in chain(spec.args, spec.kwonlyargs):
                if name in kwargs:
                    new_kwargs[name] = process(keyword_args.pop(name),
                                               annotations[name])

            # Deal with variable positional arguments
            if spec.varargs and spec.varargs in annotations:
                annotation = annotations[spec.varargs]
                for arg in args[len(spec.args):]:
                    new_args.append(process(arg, annotation))

            # Deal with variable keyword arguments
            if spec.varkw and spec.varkw in annotations:
                annotation = annotations[spec.varkw]
                for name, arg in keyword_args.items():
                    new_kwargs[name] = process(arg, annotation)

            r = func(*new_args, **new_kwargs)

            if 'return' in annotations:
                r = process(r, annotations['return'])

            return r

        return wrapper

    return decorator


@annotation_decorator
def typesafe(value, annotation):
    """
    Verify that the function is called with the right argument types and
    that it returns a value of the right type, according to its annotations
    """
    if not isinstance(value, annotation):
        raise TypeError("Expected %s, got %s." % (annotation.__name__,
                                                  type(value).__name__))

    return value


@annotation_decorator
def coerce_arguments(value, annotation):
    return annotation(value)


@typesafe(str, str)
def combine(a, b):
    return a + b


combine('spam', 'alot')

【问题讨论】:

  • annotation_decorator 看起来应该接受一个参数,并且它返回接受typesafe 等的装饰器。作为论据。您基本上是用装饰器替换函数,而不是装饰函数。
  • 谢谢!遵循书籍示例非常奇怪,必须弄清楚为什么事情不起作用!哈哈,我根据您的评论回复。

标签: python decorator


【解决方案1】:

我根据@chepner 的评论修改了两处:

我没有在combine 函数上使用typesafe 装饰器,而是简单地用annotation_decorator 装饰,将typesafe 作为arg 传递,并将函数注释添加到函数的变量中。

@annotation_decorator(process=typesafe)
def combine(a: str, b: str):
    return a + b

【讨论】:

    猜你喜欢
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 2023-01-11
    • 2019-01-06
    相关资源
    最近更新 更多