寻找符合Python风格的求和方式

本文摘自《流畅的Python》(《FluentPython》),作者Luciano Ramalho,译者安道 吴珂


=============

序言

就像“什么是美”没有确切的答案一样,“什么是Python风格”也没有标准答案。如果回答“地道的Python”,不能让人100%满意,因为对你来说是“地道的”,在我看来却可能不是。但我可以肯定的是,“地道”并不是指使用最鲜为人知的语言特性。

先决条件

  • 归约函数(reduce、sum、any、all)将序列或有限的迭代对象变成一个聚合结果
  • reduce函数在目前的更新中已经被收进functools包中了
  • 函数签名reduce(function, iterable, initializer)
    其中第三个参数initializer是可选的。它避免了空序列抛出异常,如果令了这个参数,将从这个参数开始。(一般为恒等值,如+|^等令为1,+ &令为0)。
    function是一个接收两个参数的函数。
  • 调用的一般过程为:如序列[1,2,3,4,5]
    fun(1,2) (=a)
    fun(a, 3) (=b)
    fun(b, 4) etc...

问题提出

Python-list上有一篇题为“Pythonic Way to Sum n-th List Element?”(链接似已失效?)的话题与本篇讨论的reduce函数有关。
该话题发起人Guy Middleton说他不喜欢使用lambda表达式,问如下方案可否改进:

>>>my_list = [[1, 2, 3], [40, 50, 60], [9, 8, 7]]
>>>import functools
>>>functools.reduce(lambda a,, b: a+b, [sub[1] for sub in my_list])
60

这段代码包含了:lambda、reduce和列表推导。这对于讨厌lambda和看不上列表推导的人两边不讨好——这两种人都很多。如果使用lambda,或许就不应该使用列表推导——过滤除外,但这不是过滤((for x in list if x > 0) 其中if就是过滤表达式)。


=============

本书的作者给出的方案是:

>>> functools.reduce(lambda a, b: a + b[1], my_list, 0)
60

但作者表示不会在真实代码如此写,(因为他也不喜欢lambda表达式)。此处仅仅是为了举例说明不使用列表推导怎么做。


=============

第一个答案

,来自Fernando Perez,IPython的创建者,强调了NumPy支持n维数组和n维切片:

>>> import numpy as np
>>>my_array = np.array(my_list)
>>>np.sum(my_array[:, 1]
60

(即一维全体,二维的下标1元素)


=============

第二个答案

,Guy Middleton推崇Paul Rubin和Skip Montanaro给出的下述方案:

>>> import operator
>>> functools.reduce(operator.add, my_list, 0)
60

其中,operator库中包含诸如add, xor等常用数字运算函数,包含两个参数


=============

以及

,EvanSimpson问道:"这样做有什么错?"

>>> total = 0
>>> for sub in my_list:
...            total += sub[1]
>>>total
60

foreach循环。
许多人都觉得这也很符合Python风格。Alex Martelli甚至说,Guido或许就会这么做。
作者喜欢这段代码,更喜欢David Eppstein对此给出的评论:

如果你想计算列表各个元素的和,写出的代码应该看起来像是在“计算元素之和”,而不是“迭代元素,维护一个变量t,再执行一系列求和操作”。如果不能站在一定高度上表明意图,让语言去关注低层次操作,那么要高级语言干嘛?

之后Alex Martelli又建议:

求和操作经常需要,我不介意Python提供一个这样的内置函数。但是,在我看来,“reduce(operator..add,...”不是好方法(作为一名APL老程序员和FP语言的爱好者,我应该喜欢,但我并不喜欢)。


随后

Alex建议提供并实现了sum()函数并在之后的Python2.3中内置了。因此,Alex喜欢的句法变成了标准:(列表推导,仅能生成list)

>>> sum([sub[1] for sub in my_list])
60

下一年年末(2004年11月),Python2.4发布,这一版引入了生成器表达式。因此,作者建议,当前这个问题最符合Python风格的答案是:(生成任何类型的序列)

>>> sum(sub[1] for sub in my_list)
60

这样写不仅比reduce函数可读性更强,而且还避免了空序列导致的陷阱:sum([])的结果是0,就这么简单
在这次讨论中,Alex Martelli指出,Python2内置的reduce函数成事不足败事有余,因为他推荐的地道编程方式难以理解。他的观点最优说服力:Python3把reduce函数移到functools模块中了。

end

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容