装饰器,改变值的类型

Decorator, that changes type of value

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

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'

您不需要一堆几乎相同的装饰器。只需定义一个参数化装饰器,将所需的 return 类型作为参数。

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

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

这是基本装饰器:

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)的语法糖。

因此装饰器只有一个参数,目标函数。

但是,对于 argumented 装饰器,您 调用了 不同于普通装饰器的装饰器函数:

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)

built-in 简单装饰器的一个很好的例子是 @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'>