Python 编写生成器函数

Python 编写生成器函数,可以将许多函数简洁地写成生成器表达式的形式,比如之前讲过的映射和过滤。另外,也可以用Python内置的高阶函数(例如map()或者filter())或者生成器函数实现这些函数。编写代码时,如果生成器函数中包含多条语句,要留意代码实现是否偏离了函数式编程的指导原则:对无状态的函数求值。

使用Python进行函数式编程,就像在刀锋上行走:一边是纯函数式编程,一边是命令式编程。我们需要仔细地辨别出那些无法通过纯粹的函数式编程实现的部分,使用命令式Python完成它,并把这些部分与其他函数式部分的代码隔离开。

代码逻辑必须使用Python语句实现时,就只能用生成器函数。下面罗列了一些不能使用生成器表达式的场景。

  • 使用with语句处理外部资源时。

  • 循环条件比较灵活,不能用for语句,而需要用while语句时。

  • 循环中,由于满足特定条件,需要使用break语句或者return语句提前结束循环时。

  • 使用try-except结构处理异常时。

  • 包含内部函数定义时。

  • 复杂的if-elif分支语句。当需要处理的分支多于一种,无法用if-else表达时,分支语句会变得相对复杂。

  • 以及那些不常用的Python语句,包括for-elsewhile-elsetry-elsetry-else-finally等,也都无法在生成器表达式中使用。

通常使用break语句提前结束集合处理过程。当遍历时遇到的元素满足指定的要求,就可以结束整个处理过程了,这类似于检测集合中是否存在拥有某种属性元素的any()函数。还有一种情况是在处理完指定数量的元素(不是所有元素)后退出。

可以将寻找集合中的特定值简洁地表达为min(some-big-expression)或者max(something big)。在这种情况下,必须检查集合中的所有元素,确保选出的是最大值或者最小值。

少数情况下,需要实现一个first(function, collection)函数,只要找到第一个值就够了。为了避免不必要的计算,遍历结束得越早越好。

下面是该函数的一种实现:

def first(predicate: Callable, collection: Iterable) -> Any:
    for x in collection:
        if predicate(x): return x

遍历集合,对集合中的每个元素应用指定的谓词函数。如果谓词函数判断结果为True,则返回对应的元素;如果集合遍历完毕,则返回默认的None值。

可以从PyPI上下载该函数的一个版本,first模块基本上是对上面的思想的实现与扩展,更多细节可参考https://pypi.python.org/pypi/first。

判断一个数是否为质数时就可以用这里的first函数,示例如下:

import math
def isprimeh(x: int) -> bool:
    if x == 2: return True
    if x % 2 == 0: return False
    factor = first(
        lambda n: x % n == 0,
        range(3, int(math.sqrt(x) + .5) + 1, 2))
    return factor is None

该函数处理了一些特殊情况,包括2是质数,而其他偶数都不是质数,然后用上面定义的first()函数寻找指定集合中的第一个因子。

first()函数返回第一个因子。在这个场景中,这个数具体是什么并不重要,存在与否才重要。当因子不存在时,isprimeh()函数返回True

可以使用同样的方法处理数据异常。处理无效数据的map()函数如下:

def map_not_none(func: Callable, source: Iterable) -> Iterator:
    for x in source:
        try:
            yield func(x)
        except Exception as e:
            pass # For help debugging, use print(e)

该函数遍历可迭代对象中的每个元素,并将函数应用于元素。如果没有异常,则返回处理结果;如果发现了异常,则舍弃该异常元素。

处理包含无效值或者缺失值的数据时,这种方法很方便,无须创建复杂的过滤器筛选异常值,只要在处理过程中舍弃无效的输入值即可。

可以对包含无效值的输入数据执行映射,如下所示:

data = map_not_none(int, some_source)

some_source集合由字符串组成,map_not_none()函数对其中元素依次应用int()函数,可以方便地过滤掉那些不代表数字的字符串。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程