Python 向装饰器添加参数

Python 向装饰器添加参数,一个常见的需求是使用额外的参数去自定义一个装饰器。我们可以做一些更复杂的处理,而不只是简单地创建一个复合函数 f o g(x)。使用参数化装饰器可以创建 (f(c) o g)(x)。前面已经使用了参数 C 作为创建封装器 f(c) 的一部分。这个参数化的复合函数 f(c) o g 之后便可以和实际数据 x 一同使用了。

在Python中,可以编写如下代码:

@deco(arg)
def func(x):
    something

这里包含两个步骤。首先是将参数应用于抽象装饰器以创建具体的装饰器,然后将该具体的装饰器,即参数化的函数deco(arg),应用于基函数定义中来创建装饰器函数。

该装饰器函数的效果如下所示:

def func(x):
    return something(x)
concrete_deco = deco(arg)
func= concrete_deco(func)

实际上做了以下三件事:
(1) 定义一个函数func()
(2) 对参数arg应用抽象装饰器deco()来创建一个具体装饰器concrete_deco()
(3) 对基函数应用该具体装饰器concrete_deco()来创建函数的装饰器版本,实际上就是deco(arg)(func)
带参数的装饰器包含了对最终函数的间接构造。这似乎已经超越了单纯的高阶函数,进入了更为抽象的领域,即创建高阶函数的高阶函数。

可以扩展能感知不良数据的装饰器来创建一个更灵活的转换器。下面定义一个@bad_char_remove装饰器,它以待删除字符为参数。这个参数化的装饰器如下:

import decimal
def bad_char_remove(
        *char_list: str
    ) -> Callable[[F], F]:
    def cr_decorator(function: F) -> F:
        @wraps(function)
        def wrap_char_remove(text: str, *args, **kw):
            try:
                return function(text, *args, **kw)
            except (ValueError, decimal.InvalidOperation):
                cleaned = clean_list(text, char_list)
                return function(cleaned, *args, **kw)
        return cast(F, wrap_char_remove)
    return cr_decorator

一个参数化的装饰器具有两个内部函数定义。

  • 抽象装饰器:cr_decorator函数会将其绑定的自由变量char_list变成具体的装饰器。随后返回该装饰器将其应用于函数,这将返回一个封装在wrap_char_remove函数内的函数。这里作为类型提示的类型变量F声明了封装操作将保留被封装函数的类型。
  • 被装饰的封装器:wrap_char_remove函数会用封装版本替代原始函数。由于使用了@wraps装饰器,因此被封装的基函数的名字将替代新函数的__name__属性(以及其他属性)。

整个装饰器bad_char_remove()函数的任务是将参数绑定到抽象装饰器并返回具体装饰器。类型提示阐明了返回值是一个能将Callable函数转换为另一个Callable函数的Callable对象。之后根据语言规则,会将具体装饰器应用于之后的函数定义。

下面的clean_list()函数用于删除在给定列表中的所有字符。

from typing import Tuple
def clean_list(text: str, char_list: Tuple[str, ...]) -> str:
    if char_list:
        return clean_list(
            text.replace(char_list[0], ""), char_list[1:])
    return text

其中的规则十分简单,因此使用了递归方法,也可以把它优化成一个循环。

可以使用@bad_char_remove装饰器创建转换函数,如下所示·:

@bad_char_remove("$", ",")
def currency(text: str, **kw) -> Decimal:
    return Decimal(text, **kw)

上面使用了装饰器来封装currency()函数。函数currency()的本质特征是对decimal.Decimal构造器的引用。

这个currency()函数可以处理不同的数据格式了。

>>> currency("13")
Decimal('13')
>>> currency("3.14")
Decimal('3.14')
>>> currency("1,701.00")
Decimal('1701.00')

接下来可以使用一个相对简单的map(currency, row)方法处理输入数据,以便将源数据从字符串转换为可用的Decimal值了。错误处理语句try:/except:已经隔离到了一个用于构建复合转换函数的函数中。

可以使用类似的设计来创建可以容错空值的函数。这些函数将使用类似的try:/except:封装器,但只简单地返回None值。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程