列表中最长单词的长度
获取最长单词长度的更“Python风格”的方法是什么呢:
len(max(words, key=len))
还是:
max(len(w) for w in words)
或者..其他方法?words
是一个字符串列表。
我发现我经常需要做这个操作,经过对几个不同样本大小的测试,第一种方法似乎一直更快,尽管乍一看似乎不太高效(因为len
被调用了两次,看起来有点多余,但这似乎并不影响 - 这种形式下在C代码中会发生更多事情吗?)
6 个回答
如果你把生成器表达式改成用 map
函数来写(在 Python 2.x 中用 imap
),
max(map(len, words))
… 其实这样比用键的版本要快一点,并不是更慢。
在 python.org 的 64 位 3.3.0 版本上:
In [186]: words = ['now', 'is', 'the', 'winter', 'of', 'our', 'partyhat'] * 100
In [188]: %timeit max(len(w) for w in words)
%10000 loops, best of 3: 90.1 us per loop
In [189]: %timeit len(max(words, key=len))
10000 loops, best of 3: 57.3 us per loop
In [190]: %timeit max(map(len, words))
10000 loops, best of 3: 53.4 us per loop
在 Apple 的 64 位 2.7.2 版本上:
In [298]: words = ['now', 'is', 'the', 'winter', 'of', 'our', 'partyhat'] * 100
In [299]: %timeit max(len(w) for w in words)
10000 loops, best of 3: 99 us per loop
In [300]: %timeit len(max(words, key=len))
10000 loops, best of 3: 64.1 us per loop
In [301]: %timeit max(map(len, words))
10000 loops, best of 3: 67 us per loop
In [303]: %timeit max(itertools.imap(len, words))
10000 loops, best of 3: 63.4 us per loop
我觉得这样写比用 key
的版本更符合 Python 的风格,原因和生成器表达式一样。
至于它是否和生成器表达式一样符合 Python 风格,这个可以争论。有些人喜欢 map
、filter
、reduce
等;有些人则不喜欢;我个人觉得,当你想用一个已经存在并且名字好听的函数时(也就是说,不需要用 lambda
或 partial
来处理),用 map
会更好,不过这也因人而异(特别是如果你叫 Guido 的话)。
最后一点:
调用
len
两次的冗余似乎没什么关系——在这种情况下,C 代码会做更多的事情吗?
可以这样想:你已经调用了 len
N 次。如果改成调用 N+1
次,和你需要做的 N
次相比,几乎不会有什么区别,除非你有一些 极小 的 超大 字符串。
虽然:
max(len(w) for w in words)
看起来“读起来”更简单一些,但使用生成器会增加一些额外的开销。
而:
len(max(words, key=len))
可以利用内置函数进行优化,而且因为 len
通常对字符串来说是一个非常高效的操作,所以会更快……
我觉得这两种写法都可以,但如果不考虑速度的话,max(len(w) for w in words)
这段代码看起来更容易理解。
当我在看这两种写法时,花了我更长的时间去搞明白 len(max(words, key=len))
是在干嘛,直到我多想了一会儿才明白,之前的理解还是错的。代码应该一眼就能看懂,除非有特别的理由让它变得复杂。
从其他人的帖子和我自己的测试来看,那个不太容易理解的写法确实更快。但其实两者的速度都还不错,并不是特别慢。除非这段代码在关键路径上,否则没必要太担心速度问题。
总的来说,我觉得更容易理解的写法更符合 Python 的风格。
顺便提一下,这是少数几个 Python 2 在同样任务上明显比 Python 3 快的情况之一。