Python 扩展简单循环

Python 扩展简单循环,对简单循环的扩展包括两种情况。首先介绍过滤式扩展,这种扩展能够按照某种标准舍弃数据源中的某些数据,这些数据可能是异常值,或者存在格式错误。第二种情况是简单转换原始数据,在原有对象的基础上创建新的对象。这里是要将字符串转换为浮点数。不过如何通过映射扩展简单循环要看具体情况。下面介绍重构pairs()函数的方法,如果想调整点的顺序并舍弃某些值,应该怎么做呢?可以使用过滤式扩展筛选数据。

在之前循环体的实现中,为了将复杂度降到最低,仅仅返回了数据对,没有其他任何应用逻辑相关的处理。实现的简洁避免了开发者在复杂的状态变换中迷失方向。

为循环添加过滤式扩展的一种实现方案如下:

from typing import Iterator, Any, Iterable
Pairs_Iter = Iterator[Tuple[float, float]]
LL_Iter = Iterable[
    Tuple[Tuple[float, float], Tuple[float, float]]]
def legs_filter(lat_lon_iter: Pairs_Iter) -> LL_Iter:
    begin = next(lat_lon_iter)
    for end in lat_lon_iter:
        if #some rule for rejecting:
            continue
        yield begin, end
        begin = end

这样在循环体中添加了筛选规则来滤掉某些值,在保持循环简洁明了的前提下,处理过程能够按照预期进行。另外,由于该函数可以处理任何可迭代对象,而不必考虑数据对的具体用途,为它编写测试用例也很简单。

这里没有实现#some rule for rejecting部分的代码,这部分用于通过beginend或者二者结合过滤掉某些无效数据。例如需要过滤掉begin == end,以免出现长度为0的线段。

接下来的重构工作是为循环添加映射。在应用功能、设计目标不断变化的过程中,经常需要加入映射。这里的原始数据是字符串类型的,需要将它们转换为浮点数以备后用。这个转换本身不复杂,展示了这种扩展的实现方法。

下面的示例把生成器函数包裹在生成器表达式中,实现了数据映射。

trip = list(
    legs(
        (float(lat), float(lon))
        for lat,lon in lat_lon_kml(row_iter_kml(source))
    )
)

legs()函数的输入参数是一个生成器表达式,该生成器表达式把使用lat_lon_kml()的输出转换为浮点数。或者反过来说:首先把lat_lon_kml()函数的输出转换为浮点数对,再转换为一系列路径段。

现在事情变得有点复杂了,几个函数层层嵌套,在数据生成器上分别应用了float()legs()以及tuple()。对于这类复杂的表达式,常用的重构方法是把生成器表达式从实例化的集合中独立出来,对上述表达式的一种简化实现如下:

flt = (
    (float(lat), float(lon))
    for lat,lon in lat_lon_kml(row_iter_kml(source))
)
print(tuple(legs(ll_iter)))

这里将生成器函数赋给了变量flt,该变量不是集合对象,也不是通过列表推导生成的对象,只是给生成器表达式起了个名字(flt),然后在其他表达式中使用这个名字。

tuple()的求值导致作为参数的惰性变量开始求值,flt变量对应的对象只在需要求值的时候才被创建。

除了上面的代码实现,还有其他方法可以实现重构。数据源经常发生变化。在上面的例子中,lat_lon_kml()函数与其他表达式紧密绑定,导致处理其他数据源时,难以复用这个函数。

当需要把上面的转换过程float()参数化,以便之后复用,可以基于生成器表达式专门定义一个函数。下面抽取一些处理过程放在单独的函数里,来对操作过程进行分组。这里由于字符串到浮点数的转换与数据源无关,可以把这个复杂的转换表达式写入下面的函数中。

from typing import Iterator, Tuple, Text, Iterable
Text_Iter = Iterable[Tuple[Text, Text]]
LL_Iter = Iterable[Tuple[float, float]]
def float_from_pair(lat_lon_iter: Text_Iter) -> LL_Iter:
    return (
        (float(lat), float(lon))
        for lat,lon in lat_lon_iter
    )

该函数对可迭代对象每组值的第1个数据和第2个数据应用float()函数,把原始数据转换成由浮点数组成的二元组,这里借助了Python的for语句解析此二元组。

类型标示要求输入是Text_Iter类型:Text类型二元组组成的可迭代对象;返回值是LL_Iter类型:浮点数二元组组成的可迭代对象。LL_Iter类型可以用于其他复杂函数的定义中。

该函数可用于如下场景:

legs(
    float_from_pair(
        lat_lon_kml(
            row_iter_kml(source))))

这样就把KML文件中的数据转换为浮点数了。由于每一个处理环节都是一个简单的前缀式函数,可以方便地可视化整个过程,每个函数的输入都是内层嵌套函数的输出。

解析过程的输入值通常是字符串,对于数据处理应用来说,经常需要将输入值转换为浮点数、整数、十进制数等类型,这时就要在清洗源数据的表达式中插入类似于float_from_pair()的函数。

原先字符串类型的输出如下所示:

(('37.54901619777347', '-76.33029518659048'),
 ('37.840832', '-76.27383399999999'),
 ...
 ('38.976334', '-76.47350299999999'))

所需的浮点型数据如下所示:

(((37.54901619777347, -76.33029518659048),
 (37.840832, -76.273834)),
 ...
 ((38.330166, -76.458504), (38.976334, -76.473503)))

接下来简化转换流程,前面把代码优化成了flt = ((float(lat), float(lon)) for lat,
lon in lat_lon_kml()),根据函数替换规则,可以把类似于((float(lat), float (lon)) for lat,lon in lat_lon_kml())的复杂表达式,替换为返回值相同的函数,这里是float_from_pair(lat_lon_kml())。这样的重构可以简化复杂的表达式,同时保证功能不变。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程