网站做管理员功能代码,怎样做网络销售网站,目前网站开发的新技术,湖北建设执业注册管理中心网站lambda本文将介绍Python中函数式编程的特性。在对函数式编程的概念有了了解后#xff0c;本文会介绍iterators和generators等语言特性#xff0c;还有itertools和functools等相关的库。函数式编程本章节将会介绍函数式编程的一些基本概念#xff1b;如果只是对Python的语言特…lambda本文将介绍Python中函数式编程的特性。在对函数式编程的概念有了了解后本文会介绍iterators和generators等语言特性还有itertools和functools等相关的库。函数式编程本章节将会介绍函数式编程的一些基本概念如果只是对Python的语言特性感兴趣的话可以跳过。编程语言支持用几种不同的方式分解问题。大多数的编程语言是面向过程的程序是计算机处理输入的指令集合。C, Pascal, 甚至Unix shell都是这类。在命令式编程中用户告诉计算机需要做什么语言的实现来完成高效的计算。SQL可能是最广为人知的宣告式編程语言了SQL语句负责查询描述要获取的数据集合SQL引擎决定是扫描表或者使用索引首先执行哪些字查询等等问题。面向对象的程序操作对象的集合。对象具有内部状态并支持查询和修改内部状态的方法。Smalltalk和Java是面向对象编程语言。C和Python支持面向对象但是不强制使用面向对象特性。函数式编程语言将问题分解成一系列函数。理想情况下函数接受输入产生输出并且没有影响这一过程的内部状态。众所周知的函数式语言包括ML系列(标准MLOCaml以及其他变种)和Haskell。设计计算机语言时设计者会选择强调一种特定的编程方法。这通常会导致采用另外的方法编写程序会变得困难。有一些其他的语言是支持多种不同方法的多范式语言。LispC和Python是多范式语言使用这些语言可以编写面向过程面向对象或者函数式的程序或者库。在一个大型程序中可能会使用不同的方法来编写不同的部分。比如程序中的GUI部分采用面向对象方法而处理的逻辑是面向过程或者是函数式的。在一个函数式的程序中输入会流经一组函数。每个函数对自己的输入进行处理并产出输出。对于那些有修改内部状态副作用和在进行在返回值中不可见的修改的函数函数式编程是不鼓励的。没有副作用的函数被称之为纯函数。没有副作用意味着不使用随程序运行而更新的数据结构每个函数的输出只取决于它的输入。有些编程语言对于函数是否是纯函数有着严格的限制它们甚至没有类似a3或者c a b这样的赋值语句但是想要完全避免副作用是很困难的。比如打印到屏幕或者写入磁盘文件就是有副作用的。比如在Python中print和time.sleep(1)都没有返回有用的值它们被调用只是为了发送文本到屏幕或者暂停执行一秒的副作用。函数式风格的Python程序通常不会走向避免所有I/O操作或所有赋值操作的极端它们一般会提供函数式的接口然后内部使用非函数式的特性实现功能。比如一个函数的内部依然会对局部变量赋值但是不会修改全局变量或者有其他的副作用。函数式编程可以看作是面向对象编程的对立面。对象包含了一些内部状态和修改这些状态的方法面向对象的程序指定对象的正确状态。而函数式编程希望尽量避免状态的改变在函数之间处理数据流。在Python中你可以通过编程接收和返回对象来同时利用这两种编程方式对象和应用有关(e-mail事务等等)。函数式的设计似乎是一个奇怪的制约因素。为什么要避免对象和副作用呢因为函数式风格有以下理论和实践的优势。正式证明性模块化组合性易于调试和测试正式证明性使用函数式编程的一个理论上的好处是在数学上验证一个程序的正确性是比较容易的。很长一段时间以来研究人员一直在寻找通过数学证明程序正确的方法。这种证明的方法和通过输入测试数据验证输出的正确性以及通过读取程序的源代码来判断正确性的方法不同它想要严格证明程序对所有可能的输入都能产生正确的结果。用于证明程序正确的技术是记下不变量输入数据和一直为真的程序变量。对每一行代码和不变量XY可以知道运行前X和Y是否为真和执行后X和Y是否为真。进行这样的比较直到程序结束这是不变量应该符合程序输出所需的条件。赋值行为会修改之前为真的不变量而不会产生新的可以向后传递的不变量因此上面的技术碰到赋值行为的时候会难以继续下去这也导致了函数式风格会避免赋值。不幸的是证明程序正确无疑是不切实际的这和Python无关。即使是极为简单的程序也需要几页长的证明;中等程度复杂程序的正确性证明难度将是巨大的日常使用的程序(Python解释器XML解析器Web浏览器)几乎没有可以被证明是正确的。即使是生成了一个证明也会存在需要验证这个证明的问题如果这个证明有问题那么得到程序正确的结论将是错误的。模块化函数式编程一个更实际的好处是它会强制使用者将问题分解。程序因此会更加模块化。相比一个实现复杂变换的长函数实现完成一件事的小函数更加容易。短小的函数更易于阅读和检查错误。易于测试和调试函数式风格的程序测试和调试起来更加容易。函数通畅很小并且功能明确所以调试起来会很方便。当程序无法运行时可以在每个函数入口检查数据是否正确。可以通过查看中间输入和输出来快速定位出现bug的函数。每个函数都可以成为单元测试的目标因此测试也会容易些。函数不依赖于系统状态因此测试只需要合成正确的输入然后检查输出是否符合预期。组合性在编写函数式风格的程序时会编写许多具有不同输入和输出的函数。这些函数有些是专门针对特定的应用但是其他的函数将在各种不同的程序中有用。比如一个传入目录路径并返回目录中所有XML文件的函数或者一个传入文件名并返回其内容的函数是可以在多种不同的情况下使用的。随着时间推移你可以慢慢组建属于自己的库。通常你可以使用已有的函数改变配置然后实现一些专门针对当前任务的函数来组成新的程序。Python特性迭代器我将首先介绍Python一个语言特性这个特性是编写函数式风格程序的基础迭代器。迭代器是表示数据流的对象此对象每次返回数据的一个元素。一个Python迭代器必须实现next()方法该方法不接受参数并且总是返回数据流的下一个元素。如果流中没有元素next()必须抛出StopIteration异常。迭代器不必是有限的编写产生无限数据流的迭代器非常合理。内置的iter()函数接受任意对象并尝试返回一个返回对象元素的迭代器如果对象不支持迭代则抛出TypeError异常。Python内置的数据类型中有几种是支持迭代的最常见的有列表和字典。如果一个对象可以生成一个迭代器则称该对象是可迭代的。可以体验一下迭代接口: L [1, 2, 3] it iter(L) print it it.next()1 it.next()2 it.next()3 it.next()Traceback (most recent call last):File , line 1, in StopIteration在一些不同的上下文中Python期望对象是可迭代的其中最重要的就是for语句。在语句for x in y中Y必须是迭代器或者能通过iter()方法生成一个迭代器的对象。以下两条语句是等价的for i in iter(obj):print ifor i in obj:print i可以通过list()和tuple()构造方法操作迭代器得到列表或者是元组。 L [1, 2, 3] iterator iter(L) t tuple(iterator) t(1, 2, 3)序列解包也支持迭代器如果知道迭代器将返回N个元素则可以将其解包为N元组 L [1, 2, 3] iterator iter(L) a, b, c iterator a, b, c(1, 2, 3)内置的max()和min()方法接受迭代器作为参数返回最大或者最小的元素。in和not in操作同样支持迭代器如果X在迭代器返回的流中则X in iterator返回true。如果迭代器是无限的话明显会有一些问题max()和min()将永远不会返回如果X没有出现在流中in和not in也将不会返回。请注意在迭代器中只能向前取数据没有办法得到上一个元素重置迭代器或者复制它。迭代器对象可以可选地提供这些附加功能但是迭代器协议仅指定next()方法。因此函数可能会消耗迭代器的所有输出如果需要使用相同的流执行不同的操作则必须创建一个新的迭代器。支持迭代器的数据类型上面已经介绍了列表和元组是如何支持迭代器的。事实上任何Python序列类型(如字符串)都支持创建迭代器。对一个字典调用iter()方法将会返回一个迭代器该迭代器循环使用字典的键。 m {Jan: 1, Feb: 2, Mar: 3, Apr: 4, May: 5, Jun: 6, Jul: 7, Aug: 8, Sep: 9, Oct: 10, Nov: 11, Dec: 12} for key in m:... print key, m[key]...Feb 2Aug 8Jan 1Dec 12Oct 10Mar 3Sep 9May 5Jun 6Jul 7Apr 4Nov 11请注意上面输出的顺序是随机的因为排序是基于字典对象的哈希顺序。对字典对象使用iter()方法会返回键的迭代器但是字典有其他方法得到不同的迭代器。如果想迭代键值键值对分别可以调用iterkeys(),itervalues(),iteritems()来的得到对应的迭代器。dict()构造方法可以接受返回(key, value)元组流的迭代器生成新的字典。 L [(Italy, Rome), (France, Paris), (US, Washington DC)] dict(iter(L)){Italy: Rome, US: Washington DC, France: Paris}文件也可以通过readline()方法来迭代文件的内容也就是说可以通过下面的方式来读取文件的每一行for line in file:...可以从一个可迭代对象生成一个集合并迭代器中的元素set([Italy, US, France]) S set((2, 3, 5, 7, 11, 13)) for i in S:... print i...23571113生成器表达式和列表推导对迭代器通常有两个操作对每个元素进行操作选择满足某个条件的元素的子集。比如给定一个字符串的列表可能需要去除每个字符串末尾的空格或者提取包含给定子串的字符串。列表推导和生成器表达式是这种操作的简写符号这是从Haskell中借鉴来的。可以通过下面的代码从字符串流中去掉所有空格line_list [ line 1\n, line 2 \n, ...]# 生成器表达式返回迭代器stripped_iter (line.strip() for line in line_list)# 列表推导式返回列表stripped_list [line.strip() for line in line_list]可以通过添加if条件来筛选特定的元素。stripped_list [line.strip() for line in line_list if line ! ]使用列表推导式可以得到一个Python列表strip_list是包含所有结果的列表不是迭代器。生成器表达式返回一个迭代器根据需要计算值而不需要一次算出所有的值。这意味着当处理一个无限的数据流或者是一个数据量非常大的迭代器时列表推导式并不适用。上述的情况应该是用生成器表达式。生成器表达式使用()括起来列表推导式由[]括起来。生成器表达式语法如下( expression for expr in sequence1if condition1for expr2 in sequence2if condition2for expr3 in sequence3 ...if condition3for exprN in sequenceNif conditionN )对于列表推导式语法只有外面的括号不一致。生成输出的元素将是expression的连续值。if子语句都是可选的只有当条件为true时表达式才被计算并添加到结果中。生成器表达式必须写在括号内也可以写在表示函数调用的括号内。如果将要创建的迭代器马上会传给一个函数可以这样写obj_total sum(obj.count for obj in list_all_objects())子语句for...in包含要迭代的序列。哪些序列的长度不必相同因为迭代的顺序是从左到右不是并行的。对sequence1中的每个元素sequence2都会从头迭代。然后对sequence1和sequence2的每个结果对循环迭代sequence3。换句话说列表解析或生成器表达式与以下Python代码等价for expr1 in sequence1:if not (condition1):continue # 跳过这个元素for expr2 in sequence2:if not (condition2):continue # 跳过这个元素...for exprN in sequenceN:if not (conditionN):continue # 跳过这个元素# 输出表达式的值这意味着当有多个for...in子语句但没有if条件的情况下所得到的输出的长度将等于所有序列长度的乘积。如果有两个长度为3的列表结果的长度就是9。 seq1 abc seq2 (1, 2, 3) [(x,y) for x in seq1 for y in seq2][(a, 1), (a, 2), (a, 3), (b, 1), (b, 2), (b, 3), (c, 1), (c, 2), (c, 3)]为了不引起Python语法上的歧义如果expression用来生成一个元组则必须将其用()括起来。下面例子中第一个有语法错误第二个是正确的。# 语法错误[ x,y for x in seq1 for y in seq2]# 正确[ (x,y) for x in seq1 for y in seq2]生成器生成器是特殊的一类函数用于简化迭代器的编写。常规的函数计算一个值并返回但是生成器会返回返回值的迭代器。你肯定对Python或者C语言如果调用函数很熟悉。当一个函数被调用时它会创建一个私有的命名空间在这个空间内创建局部变量。当函数执行到return语句时局部变量被销毁并且将结果返回给调用者。该函数的下一次被调用时它会创建一个新的私有命名空间和局部变量。但是如果局部变量在退出时没有被销毁该怎么办呢如果想过一段时候在上次未执行完的地方继续执行该怎么办呢这就要提到生成器了它们被认为是可恢复执行的函数。下面是一个最简单的生成器函数的例子def generate_ints(N):for i in range(N):yield i任何含有yield关键字的函数都被认为是生成器函数这是由Python字节码编译器检测到的编译器特别编译了该函数。当生成器函数被调用时它不会返回单个值而是会返回支持迭代器协议的生成器对象。在执行yield语句时生成器输出i的值这和return语句类似。yield和return语句之间的最大区别在于在到达yield时生成器的执行状态将被暂停并保留局部变量。在下一次调用生成器的.next()方法时该函数将继续执行。下面是generate_ints()生成器的使用方法 gen generate_ints(3) gen gen.next()0 gen.next()1 gen.next()2 gen.next()Traceback (most recent call last):File , line 1, in StopIteration写成for i in generate_ints(3)或者a,b,cgenerate_ints(3)也是一样的。在生成器函数中return语句只能在没有值的情况下使用并且表示值的结束。在执行return语句后生成器不能再返回更多的值。如果在生成器函数中return带了返回值比如return 5会被认为是语法错误。可以通过手动抛出StopIteration异常或者让函数执行到最后来让生成器函数不再产生新的值。可以通过自定义的类将生成器的所有本地变量存储为实例来达到生成器的效果。比如通过将self.count设置为0将self.next()实现为自增self.count并返回来返回一列整数。然而对于一个有一定复杂度的生成器实现自定义的类会更麻烦。Python测试套件中的test_generator.py有更多有意思的例子。下面是一个递归实现树的顺序遍历的生成器。def inorder(t):if t:for x in inorder(t.left):yield xyield t.labelfor x in inorder(t.right):yield x另外两个例子提供了N皇后问题(在N*N棋盘上放N个皇后使其彼此不会互相威胁)和骑士之旅的解法。向生成器传值在Python2.4及之前的版本中生成器只能产生输出。一旦生成器的代码被调用生成一个迭代器当函数恢复执行时没有办法将任何新的信息传递到函数中。可以通过让生成器检查全局变量或者传递一些可变对象供调用方修改来将新信息传入但是这样很不优雅。在Python2.5中有一个简单的方法将值传递给生成器。yield成为了表达式返回一个可以分配给变量或者以其他方式运行的值。val (yield i)我推荐大家在执行和返回值相关的操作时始终将yield表达式括起来如上面的例子。这个括号并不总是必须的但是总是添加它比记住何时需要它们更容易。通过调用send(value)方法可以将值传到生成器中。这个方法继续执行生成器的代码yield表达式会返回传入的值。如果next()方法被调用yield返回None。下面是一个每次增加1的简单计数器允许修改内部计数器的值。def counter(maximum):i 0while i maximum:val (yield i)# 如果给定value改变计数器if val is not None:i valelse:i 1下面是改变计数器的方法 it counter(10) print it.next()0 print it.next()1 it.send(8)8 print it.next()9 print it.next()Traceback (most recent call last):File , line 1, in StopIteration因为yield会经常返回None代码中应该检查这种情况。不要在表达式中使用这个值除非确定send()是恢复函数的唯一方法。除了send()生成器还有两个新的方法throw(type, valueNone, tracebackNone)用于在生成器中抛出异常当生成器暂停执行时由yield语句抛出这个异常。close()通过在生成器中抛出GeneratorExit异常来终止迭代。在抛出这个异常后生成器代码必须抛出GeneratorExit或者StopIteration捕获这个异常是非法的会触发RuntimeError。在Python的垃圾回收器对生成器进行回收时也会调用close()。处理GeneratorExit异常推荐使用try: ... finally:而不是catch GeneratorExit。上面的改动让生成器由单向生产者变为生产者和消费者。生成器也成为了协程--一种更通用的子程序。子程序在一个点进入在另外点退出(函数的入口和return语句)但是协程可以在多个不同的点进入退出恢复执行(yield语句)。内置函数现在来看看迭代器常用的内置函数。两个Python内置的函数map()和filter()在某种程度上已经被淘汰了它们的功能和列表推导式重合不过返回的是实际的列表而不是迭代器。map(f, iterA, iterB, ...)返回一个列表内容是f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ...。 def upper(s):... return s.upper() map(upper, [sentence, fragment])[SENTENCE, FRAGMENT] [upper(s) for s in [sentence, fragment]][SENTENCE, FRAGMENT]正如上面所展示的使用列表推导式可以实现一样的效果。itertools.imap()完成一样的功能不过它可以处理无尽的迭代器等会在讨论itertools模块的再谈。filter(predicate, iter)返回一个包含满足特定条件的所有序列元素的列表这个功能和列表推导重复。predicate是返回一些条件的真值的函数为了和filter()一起使用predicate只能传入一个参数。 def is_even(x):... return (x % 2) 0 filter(is_even, range(10))[0, 2, 4, 6, 8]上面的功能也可以使用列表推导式完成 [x for x in range(10) if is_even(x)][0, 2, 4, 6, 8]reduce(func, iter, [initial_value])在itertools模块中没有对应的方法因为它累积地对可迭代对象的所有元素执行操作因此不能用于无限迭代。func函数必须接受两个参数并返回一个值。reduce()接受迭代器返回的前两个元素A和B返回func(A, B)。之后请求第三个元素C计算func(func(A, B), C)然后请求迭代器返回的第四个元素持续这样的步骤一直到迭代完所有元素。如果可迭代对象不返回任何元素会抛出TypeError异常。如果提供了初始值第一轮运算会是func(initial_value, A)。 import operator reduce(operator.concat, [A, BB, C])ABBC reduce(operator.concat, [])Traceback (most recent call last):File , line 1, in TypeError: reduce() of empty sequence with no initial value reduce(operator.mul, [1,2,3], 1)6 reduce(operator.mul, [], 1)1如果在reduce()中使用operator.add()会得到迭代对象所有值的和。这个场景使用非常广泛有内置的sum()方法来计算。 reduce(operator.add, [1,2,3,4], 0)10 sum([1,2,3,4])10 sum([])0在很多reduce()的使用场景中使用for循环会更好。product reduce(operator.mul, [1,2,3], 1)# 等同于product 1for i in [1,2,3]:product * ienumerate(iter)对可迭代对象中的元素进行计数返回计数值和没有元素的二元组。 for item in enumerate([subject, verb, object]):... print item...(0, subject)(1, verb)(2, object)循环遍历列表并记录满足某些条件的索引时常常使用enumerate()f open(data.txt, r)for i, line in enumerate(f):if line.strip() :print Blank line at line #%i % isorted(iterable, [cmpNone], [keyNone], [reverseFalse])将可迭代对象中的所有元素收集到列表中对列表进行排序并返回排序结果。cmp,key和reverse参数传递给构造的列表的.sort()方法。 import random rand_list random.sample(range(10000), 8) rand_list[3027, 8533, 16, 6602, 4183, 9577, 4842, 5713] sorted(rand_list)[16, 3027, 4183, 4842, 5713, 6602, 8533, 9577] sorted(rand_list, reverseTrue)[9577, 8533, 6602, 5713, 4842, 4183, 3027, 16]内置的any(iter)和all(iter)函数查看一个可迭代对象的真值。如果有任意元素为真值any()返回True如果所有元素都为真值all()返回True。 any([0,1,0])True any([0,0,0])False any([1,1,1])True all([0,1,0])False all([0,0,0])False all([1,1,1])True小函数和lambda表达式编写函数式程序时通常需要很少的功能作为谓词或以某种方式组合元素。如果有一个Python内置函数或是模块是合适的那么就不需要重新定义一个新的功能stripped_lines [line.strip() for line in lines]existing_lines filter(os.path.exists, file_list)如果不存在现有的函数那么你需要实现一个。编写小函数的一种方法是使用lambda语句。lambda需要一些参数和处理这些参数的表达式并创建一个返回表达式值的小函数lowercase lambda x: x.lower()print_assign lambda name, value: name str(value)adder lambda x, y: xy一种替代的方法是用def定义一个函数def lowercase(x):return x.lower()def print_assign(name, value):return name str(value)def adder(x, y):return x y哪种方式更好呢这是一个编程风格的问题我通常的做法是避免使用lambda。原因是lambda在可以定义的功能上是非常有限的。结果必须作为单个表达式计算这意味着不能使用if... elif... else和try... except语句。如果试图在lambda语句中做太多的事情那么最终会出现一个难以理解的过于复杂的表达式。能快速说出下面代码的作用吗total reduce(lambda a, b: (0, a[1]b[1]), items)[1]需要一些时间来弄清楚表达方式才能弄清楚代码想要干什么。使用一个简短的嵌套的def语句会好一些def combine(a, b):return 0, a[1] b[1]total reduce(combine, items)[1]如果只用一个for循环就更好了。total 0for a, b in items:total b或者使用内置的sum()和生成器表达式total sum(b for a, b in items)很多时候使用for循环比使用reduce()代码更加清晰。Fredrik Lundh曾经提出过以下lambda重构的规则写一个lambda函数。写一个注释说明该lambda函数的作用。研究注释一段时间并想出一个名字来捕捉评论的本职。使用该名称将lambda函数转换为def语句。移除注释。我很喜欢这些规则但是你可以有自己的选择。itertools模块itertools模块包含许多常用的迭代器以及用于组合几个迭代器的函数。本节将通过几个小例子来介绍模块的内容。该模块的功能分为几类基于现有迭代器创建新迭代器的函数。将迭代器作为函数的参数。用于选择迭代器部分输出的函数。对迭代器输出进行分组的函数。创建新的迭代器函数itertools.count(n)返回无限的整数流每次增加1。可以选择起始的数字默认是0:itertools.count() 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...itertools.count(10) 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...函数itertools.cycle(iter)保存可迭代对象内容的副本返回一个新的迭代器该迭代器从头到尾返回其元素。新的迭代器将无限重复这些元素。itertools.cycle([1,2,3,4,5]) 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...itertools.repeat(elem, [n])对传入的元素重复n次如果n没有提供则无限返回该元素。itertools.repeat(abc) abc, abc, abc, abc, abc, abc, abc, abc, abc, ...itertools.repeat(abc, 5) abc, abc, abc, abc, abcitertools.chain(iterA, iterB, ...)输入任意数量的可迭代对象返回第一个迭代器的所有元素然后返回第二个迭代器的所有元素依次类推知道返回最有一个迭代器的所有元素。itertools.chain([a, b, c], (1, 2, 3)) a, b, c, 1, 2, 3itertools.izip(iterA, iterB, ...)每次从可迭代对象中读取一个元素然后在元组中返回它们itertools.izip([a, b, c], (1, 2, 3)) (a, 1), (b, 2), (c, 3)这个和内置的zip()函数类似但是不构成内存列表并在返回之前耗尽所有的输入迭代器只有当它们被请求时才构造并返回元组。(专业术语叫做惰性求值)。该迭代器旨在于所有长度相同的可迭代对象使用。如果可迭代对象长度不同生成的流将与最短的可迭代对象长度相同。itertools.izip([a, b], (1, 2, 3)) (a, 1), (b, 2)但是应该避免这样做因为从更长的可迭代对象中读取的元素可能会被丢弃。这意味着不能进一步使用该可迭代对象因为有跳过丢弃元素的风险。itertools.islice(iter, [start], stop, [step])返回迭代器的切片流。它会在遇到第一个stop的元素时返回。如果提供了起始索引将会获得stop-start之间的元素如果提供了step值将会相应地跳过元素。和Python字符串和列表的切片不同这里的start,stop,step不能取负值。itertools.islice(range(10), 8) 0, 1, 2, 3, 4, 5, 6, 7itertools.islice(range(10), 2, 8) 2, 3, 4, 5, 6, 7itertools.islice(range(10), 2, 8, 2) 2, 4, 6itertools.tee(iter, [n])复制一个迭代器它返回n个独立的迭代器它们将返回源迭代器的内容。n的默认值是2。复制迭代器需要保存源迭代器的一些内容因此如果源迭代器很大并且一个新的迭代器比其他迭代器更多被消费那么这会消耗很可观的内存。itertools.tee(itertools.count()) iterA, iterBiterA -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...iterB -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...对可迭代对象的元素进行操作有两个函数用于对可迭代的内容进行其他函数的调用。itertools.imap(f, iterA, iterB)返回包含f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ...的流itertools.imap(operator.add, [5, 6, 5], [1, 2, 3]) 6, 8, 8operator模块包含一组与Python运算符相对应的函数。比如operator.add(a, b)(两个元素相加)operator.ne(a, b)(等同于a!b)和operator.attrgetter(id)(返回一个可以获取id属性的可调用方法)。itertools.starmap(func, iter)假定可迭代对象返回一个元组流并使用这些元组作为参数调用f():itertools.starmap(os.path.join,[(/usr, bin, java), (/bin, python),(/usr, bin, perl), (/usr, bin, ruby)])/usr/bin/java, /bin/python, /usr/bin/perl, /usr/bin/ruby选择元素另一组函数根据谓词选择迭代器元素的子集。itertools.ifilter(predicate, iter)返回所有predicate为真的元素def is_even(x):return (x % 2) 0itertools.ifilter(is_even, itertools.count()) 0, 2, 4, 6, 8, 10, 12, 14, ..itertools.ifilterfalse(predicate, iter)正好相反返回所有为假的元素itertools.ifilterfalse(is_even, itertools.count()) 1, 3, 5, 7, 9, 11, 13, 15, ...itertools.takewhile(predicate, iter)只要predicate为真就一直返回值。一旦predicate为假就停止迭代。def less_than_10(x):return (x 10)itertools.takewhile(less_than_10, itertoosl.count()) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9itertools.takewhile(is_even, itertools.count()) 0itertools.dropwhile(predicate, iter)丢弃所有predicate为真的值返回其他值。itertools.dropwhile(less_than_10, itertools.count()) 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...itertools.dropwhile(is_even, itertools.count()) 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...元素分组现在谈的最后一个函数itertools.groupby(iter, key_funcNone)是最复杂的。key_func(elem)是一个可以计算可迭代对象返回的每个元素键值的函数。如果没有传入这个函数键值就是元素本身。函数groupby()从具有相同键值的底层迭代中收集所有连续元素并返回一个包含键值的2元组流河具有该键的元素的迭代器。city_list [(Decatur, AL), (Huntsville, AL), (Selma, AL),(Anchorage, AK), (Nome, AK),(Flagstaff, AZ), (Phoenix, AZ), (Tucson, AZ),...]def get_state((city, state)):return stateitertools.groupby(city_list, get_state) (AL, iterator-1),(AK, iterator-2),(AZ, iterator-3), ...iterator-1 (Decatur, AL), (Huntsville, AL), (Selma, AL)iterator-2 (Anchorage, AK), (Nome, AK)iterator-3 (Flagstaff, AZ), (Phoenix, AZ), (Tucson, AZ)函数groupby()假定底层可迭代对象的内容已经根据键值进行排序。请注意返回的迭代器依然使用底层的可迭代对象因此必须在消费iterator-1中的内容后再去请求iterator-2及其相应的键值。functools模块在Python2.5中引入的functools模块包含一些高阶函数。高阶函数将一个或多个函数作为输入并返回一个新的函数。其中最有用的就是functools.partial()函数。对函数式风格的程序来说有时要构建具有填充一些参数的现有函数的变体。比如函数f(a, b, c)你可能希望创建一个新的函数g(b, c)相当于f(1, b, c)这被称之为“部分功能应用程序”。partial的构造函数使用参数(function, arg1, arg2, ... kwarg1value1, kwarg2value2)。生成的对象是可调用的所以可以用它来构造可以填充参数的函数。下面是一个小例子import functoolsdef log(message, subsystem):# 向特定的system写入消息print %s: %s % (subsystem, message)...server_log functools.partial(log, subsystemserver)server_log(Unable to open socket)operator模块之前提到过operator模块。它包含一组于Python操作符相对应的函数。这些函数在函数式风格的代码中通常很有用因为它可以替代一些只包括单个操作的函数。其中的一些函数有数学运算add(),sub(),mul(),div(),floordiv(),abs(),...逻辑运算not_(),truth()。位运算and_(),or_(),invert()。对比eq(),ne(),lt(),le(),gt()和ge()。对象标识is_(),is_not()。请参阅operator模块的文档来获取完整列表。