Python 复合设计

Python 复合设计,复合函数的常用数学表示法如下所示:

Python 复合设计

这里的思想是可以定义一个结合了另外两个函数 f(y) 和 g(x) 的新函数 f o g(x)
Python中复合函数的多行定义可以通过以下代码实现:

@f_deco
def g(x):
    something

生成的函数本质上等同于 f o g(x)。装饰器f_deco()必须融合f()的内部定义与给定的g()来定义并返回复合函数。

实现细节显示了实际上Python提供的是一种略复杂的复合。封装器的结构有助于我们将Python装饰器的复合理解为以下形式:

Python 复合设计

应用于某个函数 g(x)的装饰器将包含一个封装函数 w 。该封装器包括两部分,其中一部分 w_{\alpha}(y) 用作被封装函数 g(x) 的参数,而另一部分w_{\beta}(z) 作为被封装函数返回的结果。

下面给出具体示例,something_wrapper()装饰器的定义如下所示:

@wraps(argument_function)
def something_wrapper(*args, **kw):
    # The "before" part, w_α, applied to *args or **kw
    result = argument_function(*args, **kw)
    # The "after" part, w_β, applied to the result
    return after_result

这里显示了在原始函数的前面和后面可以注入额外处理的位置。这里强调了复合函数的抽象概念与Python的具体实现之间的一个重要区别:装饰器既可以创建 f(g(x)) 或 g(f(x)),也可以创建形式更复杂的 f_{\beta}(g(f_{\alpha}(x)))。装饰器语法无法完全描述所创建的复合函数的类型。

装饰器的真正价值在于封装函数中可以使用任何Python语句。装饰器可以使用if语句或for语句将函数转换为条件结构或循环结构。后面的示例会利用try语句对不良数据执行标准恢复操作。在这个通用框架中可以实现很多便利。

许多函数式编程都遵循 f o g(x) = f(g(x)) 设计模式。通过两个小函数来定义一个复合函数并不总是有用的。在某些情况下,分离这两个函数更有意义;而在其他情况下,我们可能想创建一个能汇总处理的复合函数。

创建map()filter()reduce()等常用的高阶函数的复合函数很容易。由于这些函数都很简单,因此复合函数通常很容易描述,并能提高程序的可读性。

例如,应用程序可能包含map(f, map(g, x))函数,创建复合函数并使用map(f_g, x)表达式来描述集合的复合可能更清楚。使用f_g = lambda x: f(g(x))往往有助于将一个复杂应用解释为简单函数的复合。

值得注意的是,这两种技术在性能上相差不大。函数map()是惰性的的,即对于两个map()函数来说,从x中提取一项,会先经由g()函数处理,然后由f()函数处理。对于单个map()函数来说,从x中提取一项后会交由复合函数f_g()处理。

预处理不良数据

在一些EDA应用中,一个横切关注点是如何处理缺失的或无法解析的数值。货币值常混合有floatintDecimal格式的数值,我们希望以统一的方式来处理它们。

在其他情况下,我们会碰到不适用不可用的数据值,不应该让它们干扰主线程的计算。允许Not Applicable值在不引发异常的前提下传递给表达式,通常会比较方便。下面重点介绍三个针对不良数据的转换函数:bd_int()bd_float()bd_decimal()。我们会在内置转换函数之前定义添加复合函数的特性。

简易的不良数据装饰器如下所示:

import decimal
from typing import Callable, Optional, Any, TypeVar, cast

FuncType = Callable[..., Any]
F = TypeVar('F', bound=FuncType)

def bad_data(function: F) -> F:
    @wraps(function)
    def wrap_bad_data(text: str, *args: Any, **kw: Any) -> Any:
        try:
            return function(text, *args, **kw)
        except (ValueError, decimal.InvalidOperation):
            cleaned = text.replace(",", "")
            return function(cleaned, *args, **kw)
    return cast(F, wrap_bad_data)

该函数封装了一个转换函数(function),以便在第一次转换碰到不良数据时尝试第二次转换。第二次转换将在删除,字符后进行。此封装器将*args**kw参数传递给被封装的函数,使得被封装的函数可以获得额外的参数。

将类型变量Ffunction参数的原始定义绑定。定义装饰器返回相同类型(F)的函数。使用函数cast()mypy工具给出提示——封装器不会更改被封装函数的签名。

可以使用此封装器创建能感知不良数据的函数,如下所示:

bd_int = bad_data(int)
bd_float = bad_data(float)
bd_decimal = bad_data(Decimal)

这样就可以创建出一组函数,它们既可以转换良好数据,也可以针对特定类型的不良数据进行有限的数据清理。

对于某些可调用对象,很难写出它们的类型提示。尤其是int()函数具有可选关键字参数,所以其类型提示会比较复杂。有关为可调用对象创建复杂类型签名的指南,请参阅http://mypy.readthedocs.io/en/latest/kinds_of_types.html?highlight=keyword#extended-callable-types。
使用bd_int()函数的示例如下:

>>> bd_int("13")
13
>>> bd_int("1,371")
1371
>>> bd_int("1,371", base=16)
4977

上面运用了bd_int()函数对字符串进行了简易转换,它也能接收含特定标点符号的字符串。此外每个转换函数可接收额外的参数。

有时需要一个更为灵活的装饰器,或者添加一些特性,例如处理各种数据清洗方案的功能。简单删除并不总是理想的。有时还要删除$°符号。稍后将介绍更复杂的参数化装饰器。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程