【问题标题】:How properly work with chain of class decorators in python?在 python 中如何正确使用类装饰器链?
【发布时间】:2019-11-17 07:54:37
【问题描述】:

我目前正在学习装饰器并理解,装饰器可以通过两种方式创建:作为函数和作为类。作为一个班级,我从this answer 指导 从pep318 阅读此示例,我决定重写示例 4,以检查函数的属性和返回值类型。所以下面是一些代码:

from inspect import signature


def accepts(*types):
    def check_types(f):
        f_args = list(signature(f).parameters)
        assert len(types) == len(f_args)
        def inner_f(*args, **kwargs):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), f"arg {a} doesn't match {t}"
            return f(*args, **kwargs)

        inner_f.__name__ = f.__name__
        return inner_f
    return check_types


def returns(rtype):
    def check_returns(f):
        def new_f(*args, **kwargs):
            result = f(*args, **kwargs)
            assert isinstance(result, rtype), f"return value {result} doesn't match {rtype}"
        new_f.__name__ = f.__name__
        return new_f
    return check_returns


class CheckTypes(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types
        f_args = list(signature(self._func).parameters)
        assert len(types) == len(f_args)

    def __call__(self, *args, **kwargs):
        for number, (a, t) in enumerate(zip(args, self._types)):
            assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"        


class ExternalWrapperCheckTypes(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func):
        return CheckTypes(func, *self._types)

class CheckReturns(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types

    def __call__(self, *args, **kwargs):
        result = self._func(*args, **kwargs)
        assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"


class ExternalWrapperCheckReturns(object):

    def __init__(self, *types):
        self._types = types

    def __call__(self, func):
        return CheckReturns(func, *self._types)


@accepts(int, (int, float))
@returns((int,))
def decorated_by_functions(arg1, arg2):
    return "Incorrect output"


@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int,))
def decorated_by_classes(arg1, arg2):
    return "Incorrect output"


def main():
    res1 = decorated_by_functions (42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
    res2 = decorated_by_classes(42, 42.42) # Ignore assertion

那么,在什么问题上? decorated_by_functions 将按预期导致断言错误,但 decorated_by_classes 忽略断言。在我看来 - 在这两种方法中重载函数 __call__ 的问题,我可能会返回类的实例或其他东西,但是当我返回它时 - 行为没有改变。

【问题讨论】:

  • 你写了例如@returns((int)),显然你想要 ((int,)) 用于一个元组,或者([int]) 用于一个列表。
  • @J_H 有道理,已修复,谢谢
  • 你的 CheckTypes 包装器实际上并没有调用任何东西。 (您的返回类型检查器也不返回任何内容,但这不是导致此问题的原因。)

标签: python python-3.x class decorator python-decorators


【解决方案1】:

更新

实际上,我的直觉是正确的——我需要返回正确的对象,感谢pythontips 这是我们的函数,使用参数调用:self._func(*args, **kwargs)。感谢大家的关注和时间!

最终解决方案有这样的观点:

from inspect import signature


class CheckTypes(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types
        f_args = list(signature(self._func).parameters)
        assert len(types) == len(f_args)

    def __call__(self, *args, **kwargs):
        for number, (a, t) in enumerate(zip(args, self._types)):
            assert isinstance(a, t), f"{number} arg {a} with type {type(a)} doesn't match {t}"

        return self._func(*args, **kwargs)

class ExternalWrapperCheckTypes(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func, *args, **kwargs):
        return CheckTypes(func, *self._types)

class CheckReturns(object):
    def __init__(self, func, *types):
        self._func = func
        self._types = types

    def __call__(self, *args, **kwargs):
        result = self._func(*args, **kwargs)
        assert isinstance(result, self._types), f"return value {result} doesn't match {self._types}"

        return self._func(*args, **kwargs)

class ExternalWrapperCheckReturns(object):
    def __init__(self, *types):
        self._types = types

    def __call__(self, func, *args, **kwargs):
        return CheckReturns(func, *self._types)


@ExternalWrapperCheckTypes(int, (int, float))
@ExternalWrapperCheckReturns((int, ))
def decorated_by_classes(arg1, arg2):
    return "Incorrect output"


def main():
    ans = decorated_by_classes(42, 42.42) # AssertionError: return value s doesn't match (<class 'int'>,)
    print(ans)

【讨论】:

    猜你喜欢
    • 2021-09-30
    • 2014-10-17
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 2011-11-20
    • 1970-01-01
    • 2011-12-15
    相关资源
    最近更新 更多