装饰器,改变值的类型

时间:2020-09-20 13:34:10

标签: python python-decorators

我想创建装饰器,这将改变返回值的类型:

class TypeDecorators:
    def to_int(self, f):
        def inner(*args):
            try:
                return int(f(*args))
            except:
                return None
        return inner
    
    def to_str(self, f):
        ...

    def to_bool(self, f):
        ...
    
    def to_float(self, f):
        ...
    
@TypeDecorators.to_str
def do_nothing(self, string: str):
    return string

@TypeDecorators.to_int
def do_something(self, string: str):
    return string

print(do_nothing('25'))
print(do_something('25'))

但是它不起作用,有人知道吗?出现此错误:

TypeError: to_str() missing 1 required positional argument: 'f'

2 个答案:

答案 0 :(得分:4)

您不需要一堆几乎相同的装饰器。只需定义一个参数化装饰器,以所需的返回类型作为其参数即可。

def change_to(t: type):
    def decorator(f):
        def wrapper(x):
            # Do this first, so you don't catch any exception *it* might raise
            rv = f(x)
            try:
                return t(rv)
            except ValueError:
                return None
        return wrapper
    return decorator


@change_to(str):
def do_nothing(x: str):
    return x


@change_to_(int):
def do_something(x: str):
    return x

答案 1 :(得分:0)

有点晚了,所以我添加了额外的信息,为什么上面显示的带参数的装饰器需要嵌套包装器。

这是基本的装饰器

def deco(func):
    print(f"wrapping {func} at IMPORT TIME!")
    return func


@deco
def some_function():
    pass

即使不调用some_function脚本也会打印出以下行:

wrapping <function some_function at 0x000001667FB0BCA0> at IMPORT TIME!

由此我们了解到@deco实际上只是some_function = deco(some_function)的语法糖。

因此装饰器仅获得一个参数,即目标函数。

但是,对于自变量装饰器,您调用装饰器函数,与普通装饰器不同:

def deco():
    print(f"inside deco!")  # called on import-time
    def inner_deco(func):
        print(f"inside inner_deco!")  # also called on import-time
        def wrapper(*args):
            print(f"inside wrapper!")  # will be called in run-time
            func(*args)
        return wrapper

    return inner_deco

@deco()
def some_function():
    pass

从本质上讲只是一种很酷的方法:

inner_deco = deco()

def b():
   pass

b = inner_deco(b)

内置简单装饰器的一个很好的例子是@functools.singledispatch,另一个是@functools.wraps(original_func)


我正在编写相同的解决方案:

from functools import wraps


def typed_decorator(target_type):

    def decorator_inner(func):
        @wraps(func)  # this copy signature of given function to wrapper for easier debugging.
        def wrapper(*args):
            return target_type(func(*args))

        return wrapper

    return decorator_inner


@typed_decorator(int)
def do_something(val):
    return val


@typed_decorator(bool)
def do_other(val):
    return val


print(type(do_something("20")))
print(type(do_other("30")))
<class 'int'>
<class 'bool'>