Don*_*ell 46 python python-3.x
谁能帮我?我正试图想出一种计算方法
>>> sum_widths = sum(col.width for col in cols if not col.hide)
Run Code Online (Sandbox Code Playgroud)
并且还要计算此总和中的项目数,而不必进行两次传递cols.
这看起来令人难以置信,但在扫描了std-lib(内置函数,itertools,functools等)之后,我甚至找不到一个可以计算迭代中成员数量的函数.我发现了这个功能itertools.count,这听起来像我想要的,但它实际上只是一个看似命名的range功能.
经过一番思考后我想出了以下内容(这很简单,除了它的钝性之外,缺少库函数可能是可以原因的):
>>> visable_col_count = sum(col is col for col in cols if not col.hide)
Run Code Online (Sandbox Code Playgroud)
但是,使用这两个函数需要两次遍历iterable,这只是错误的方式.
作为替代方案,以下功能可以满足我的需求:
>>> def count_and_sum(iter):
>>> count = sum = 0
>>> for item in iter:
>>> count += 1
>>> sum += item
>>> return count, sum
Run Code Online (Sandbox Code Playgroud)
这个问题是它需要100倍(根据timeit)生成器表达形式的总和.
如果有人能想出一个我想要的简单单行程,请告诉我(使用Python 3.3).
编辑1
伙计们,这里有很多好点子.感谢所有回复的人.我需要一段时间才能消化所有这些答案,但我会尽力选择一个来检查.
编辑2
我重复了两个简单的建议(count_and_sum功能和2个独立的sum功能)的时间,并发现我的原始时间已经过时,可能是由于在后台运行的自动安排的备份过程.
我也在这里给出了大部分优秀的建议,这些建议都是相同的模型.分析这些问题的答案一直是我相当的教育:新的用途deque,enumerate并reduce与第一次count和accumulate.谢谢大家!
以下是使用我正在开发的用于显示的软件的结果(来自我的慢上网本):
?????????????????????????????????????????????????????????
? Count and Sum Timing ?
?????????????????????????????????????????????????????????
? Method ?Time (usec)?Time (% of base)?
?????????????????????????????????????????????????????????
?count_and_sum (base) ? 7.2? 100%?
?Two sums ? 7.5? 104%?
?deque enumerate accumulate? 7.3? 101%?
?max enumerate accumulate ? 7.3? 101%?
?reduce ? 7.4? 103%?
?count sum ? 7.3? 101%?
?????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
(我没有把复杂和折叠方法的时间视为过于模糊,但无论如何,谢谢.)
由于所有这些方法的时序差异很小,我决定使用该count_and_sum函数(带有显式for循环)作为最具可读性,显性和简单(Python Zen),它也恰好是最快的!
我希望我能接受这些令人惊讶的答案中的一个是正确的,但它们都同样好,虽然或多或少模糊,所以我只是投票给所有人并接受我自己的答案为正确(count_and_sum功能),因为这就是我正在使用的.
什么是"应该有一个 - 最好只有一个 - 明显的方式来做它."?
iru*_*var 103
使用复数
z = [1, 2, 4, 5, 6]
y = sum(x + 1j for x in z)
sum_z, count_z = y.real, int(y.imag)
print sum_z, count_z
18.0 5
Run Code Online (Sandbox Code Playgroud)
DSM*_*DSM 49
我不知道速度,但这有点漂亮:
>>> from itertools import accumulate
>>> it = range(10)
>>> max(enumerate(accumulate(it), 1))
(10, 45)
Run Code Online (Sandbox Code Playgroud)
M4r*_*ini 29
适应DSM的答案.使用deque(... maxlen=1)以节省内存使用.
import itertools
from collections import deque
deque(enumerate(itertools.accumulate(x), 1), maxlen=1)
Run Code Online (Sandbox Code Playgroud)
ipython中的计时代码:
import itertools , random
from collections import deque
def count_and_sum(iter):
count = sum = 0
for item in iter:
count += 1
sum += item
return count, sum
X = [random.randint(0, 10) for _ in range(10**7)]
%timeit count_and_sum(X)
%timeit deque(enumerate(itertools.accumulate(X), 1), maxlen=1)
%timeit (max(enumerate(itertools.accumulate(X), 1)))
Run Code Online (Sandbox Code Playgroud)
结果:现在比OP的方法更快
1 loops, best of 3: 1.08 s per loop
1 loops, best of 3: 659 ms per loop
1 loops, best of 3: 1.19 s per loop
Run Code Online (Sandbox Code Playgroud)
sen*_*hin 23
以下是一些可能感兴趣的时序数据:
import timeit
setup = '''
import random, functools, itertools, collections
x = [random.randint(0, 10) for _ in range(10**5)]
def count_and_sum(it):
c, s = 0, 0
for i in it:
c += 1
s += i
return c, s
def two_pass(it):
return sum(i for i in it), sum(True for i in it)
def functional(it):
return functools.reduce(lambda pair, x: (pair[0]+1, pair[1]+x), it, [0, 0])
def accumulator(it):
return max(enumerate(itertools.accumulate(it), 1))
def complex(it):
cpx = sum(x + 1j for x in it)
return cpx.real, int(cpx.imag)
def dequed(it):
return collections.deque(enumerate(itertools.accumulate(it), 1), maxlen=1)
'''
number = 100
for stmt in ['count_and_sum(x)',
'two_pass(x)',
'functional(x)',
'accumulator(x)',
'complex(x)',
'dequed(x)']:
print('{:.4}'.format(timeit.timeit(stmt=stmt, setup=setup, number=number)))
Run Code Online (Sandbox Code Playgroud)
结果:
3.404 # OP's one-pass method
3.833 # OP's two-pass method
8.405 # Timothy Shields's fold method
3.892 # DSM's accumulate-based method
4.946 # 1_CR's complex-number method
2.002 # M4rtini's deque-based modification of DSM's method
Run Code Online (Sandbox Code Playgroud)
鉴于这些结果,我不确定OP如何通过一次通过方法看到100倍的减速.即使数据看起来与随机整数列表完全不同,也不应该发生.
此外,M4rtini的解决方案看起来像是明显的赢家.
为了澄清,这些结果在CPython 3.2.3中.有关与PyPy3的比较,请参阅James_pic的回答,其中显示了JIT编译对某些方法的一些重大收益(M4rtini的评论中也提到了这一点).
Jam*_*pic 20
作为后续senshin回答,值得注意的是,性能差异主要是由于CPython实现中的怪癖,使得某些方法比其他方法慢(例如,forCPython中的循环相对较慢).我认为在PyPy(使用PyPy3 2.1 beta)中尝试完全相同的测试会很有趣,它具有不同的性能特征.在PyPy中,结果是:
0.6227 # OP's one-pass method
0.8714 # OP's two-pass method
1.033 # Timothy Shields's fold method
6.354 # DSM's accumulate-based method
1.287 # 1_CR's complex-number method
3.857 # M4rtini's deque-based modification of DSM's method
Run Code Online (Sandbox Code Playgroud)
在这种情况下,OP的一次通过方法最快.这是有道理的,因为它可以说是最简单的(至少从编译器的角度来看)并且PyPy可以通过内联方法调用消除许多开销,而CPython不能.
为了比较,我机器上的CPython 3.3.2给出了以下内容:
1.651 # OP's one-pass method
1.825 # OP's two-pass method
3.258 # Timothy Shields's fold method
1.684 # DSM's accumulate-based method
3.072 # 1_CR's complex-number method
1.191 # M4rtini's deque-based modification of DSM's method
Run Code Online (Sandbox Code Playgroud)
你可以用与此类似的技巧来计算总和
>>> from itertools import count
>>> cnt = count()
>>> sum((next(cnt), x)[1] for x in range(10) if x%2)
25
>>> next(cnt)
5
Run Code Online (Sandbox Code Playgroud)
但是,使用for循环可能会更具可读性
感谢所有精彩的答案,但我决定使用我原来的count_and_sum函数,调用如下:
>>> cc, cs = count_and_sum(c.width for c in cols if not c.hide)
Run Code Online (Sandbox Code Playgroud)
正如对我最初问题的编辑中所解释的那样,这被证明是最快且最具可读性的解决方案。
| 归档时间: |
|
| 查看次数: |
16222 次 |
| 最近记录: |