怎么看待代码中,为了效率合并循环?

2015-04-24 22:12:50 +08:00
 zhmin
items = [...]
for item in items:
do_somthing_1(item)

for item in items:
do_something_2(item)

但是do_somthing_1和do_something_2是两个没有逻辑关联的过程,

合并循环,
for item in items:
do_somthing_1(item)
do_something_2(item)

大家一般倾向于哪种习惯?
4793 次点击
所在节点    Python
22 条回复
df4VW
2015-04-24 22:14:55 +08:00
如果确保相互不影响,你不选后者的理由是?
ruandao
2015-04-24 22:19:35 +08:00
看有没有必要合并呗, 一般不是写的时候, 直接放在一起吗, 多打几个字,多写一个for 不麻烦吗?

如果是新的需求, 则把循环放在一个新的方法里面
MrEggNoodle
2015-04-24 22:22:48 +08:00
其实没所谓吧,各有各的代码习惯~除非公司做了限制。
cfan8
2015-04-24 22:23:32 +08:00
楼主需要的是函数式编程,就不会有这种困扰了= =
zhmin
2015-04-24 22:27:39 +08:00
@df4VW 感觉第二种效率也没想象中的高吧,还是取决于do_somthing_1和do_somthing_2的复杂度。
最近接手同事的代码,第二种不好维护,往往一个for循环,里面的逻辑太多了,也不好拆分和维护
zts1993
2015-04-24 22:49:13 +08:00
for item in items:
process(item)




def process(item):
a()
b()


这样呢?
ryd994
2015-04-24 22:52:17 +08:00
效率没区别啊………
另外说不定编译器能优化掉呢?
zipher
2015-04-24 22:53:33 +08:00
基本都被编译器优化了
哪种易读写哪种
Hyperion
2015-04-24 23:22:07 +08:00
会合并,就是因为不相关才放一起。

循环合并会优化,楼上两位能否告知下相关资料地址?真不知道python会合并…

>>> def test():
... items = ['cat', 'meow', 'dog', 'woof']
... for item in items:
... print item
... for item in items:
... print item

>>> dis.dis(test.func_code)
2 0 LOAD_CONST 1 ('cat')
3 LOAD_CONST 2 ('meow')
6 LOAD_CONST 3 ('dog')
9 LOAD_CONST 4 ('woof')
12 BUILD_LIST 4
15 STORE_FAST 0 (items)

3 18 SETUP_LOOP 19 (to 40)
21 LOAD_FAST 0 (items)
24 GET_ITER
>> 25 FOR_ITER 11 (to 39)
28 STORE_FAST 1 (item)

4 31 LOAD_FAST 1 (item)
34 PRINT_ITEM
35 PRINT_NEWLINE
36 JUMP_ABSOLUTE 25
>> 39 POP_BLOCK

5 >> 40 SETUP_LOOP 19 (to 62)
43 LOAD_FAST 0 (items)
46 GET_ITER
>> 47 FOR_ITER 11 (to 61)
50 STORE_FAST 1 (item)

6 53 LOAD_FAST 1 (item)
56 PRINT_ITEM
57 PRINT_NEWLINE
58 JUMP_ABSOLUTE 47
>> 61 POP_BLOCK
>> 62 LOAD_CONST 0 (None)
65 RETURN_VALUE
>>>
ryd994
2015-04-24 23:38:58 +08:00
@Hyperion 抱歉没注意是python
python的话就没这回事了
Hyperion
2015-04-24 23:59:17 +08:00
@ryd994 奥,了解,谢谢。
secondwtq
2015-04-25 00:05:08 +08:00
一般习惯性放到一个循环里,大概最主要原因是讨厌“冗余”的 for。不过实际上我遇到大多数情况里面感觉两个循环任务还是有一些区别的。

如果是 Python 的话,我对其性能一直没有什么太大的好感,也并不希望能通过这种 trick 获得任何的提升。
zhmin
2015-04-25 00:28:12 +08:00
@Hyperion 如果两者相关的话, 那怎么分离?
mahone3297
2015-04-25 00:36:09 +08:00
确实是个问题。
为了可读性,我觉得分开比较好。。。
zhmin
2015-04-25 00:55:53 +08:00
@Hyperion 我做了一个测试,

1 def func_1(x):
2 x += 1
3
4 def func_2(x):
5 x += 1
6
7 def test_1():
8 for i in range(100000):
9 func_1(i)
10 func_2(i)
11
12 def test_2():
13 data = range(100000)
14 for i in data:
15 func_1(i)
16 for i in data:
17 func_2(i)
18
19 if __name__ == "__main__":
20 import timeit
21 print(timeit.timeit("test_1()",number=1000, setup="from __main__ import test_1"))
22 print(timeit.timeit("test_2()",number=1000, setup="from __main__ import test_2"))

仅仅就是简单自增,输出结果为
31.7822570801
31.4160778522

看来即使编译器没优化, 但效率几乎没差别。
如果是更加复杂的处理,估计效率差距会更少
Hyperion
2015-04-25 00:57:35 +08:00
@zhmin 具体情况具体看(这句话好万能…),一般不会考虑这么细,而且写的时候考虑这个也很麻烦。

最后优化时候再考虑吧。

比如两个操作引用了什么共同对象,而且操作上没交集,我还是会放一起。或者是循环的次数很庞大,能并就并了。

实际工程经验较少,仅供参考。
sivacohan
2015-04-25 01:12:03 +08:00
两个写法表达的含义是不同的。
第一个是对列表整体的操作。可以理解为复杂的推导式。

第二个是对列表元素的操作,是对列表中每一个元素元素的基本操作。

至于效率,我的问题是,你的热点真的在这吗?没测试,别空谈优化。
chevalier
2015-04-25 01:15:43 +08:00
“代码是写给人看的,顺便也机器能跑”
Hyperion
2015-04-25 02:21:44 +08:00
@zhmin 理论上来说,JUMP_ABSOLUTE 次数越少当然是越好,不过跳转开销其实也并不多。分次执行,去掉gc 的影响,我这边测试下来。range(1000000) 测试1000次,合并也就好了那么一丢丢。

合并:445.608458035
分开:448.548615123

但其实也没有什么太大的意义。

话说#17 楼的理解反了吧?…
Phoinikas
2015-04-25 10:41:15 +08:00
楼主你思路可能错了,你同事这么写的原因大概只是他觉得这样写可读性更高,而你的看法正好相反,这是没有标准的。

从效率上来说,循环本身是不耗时的(可以忽略),耗时的是循环里面的操作,两种写法中do_something_1和do_something_2执行的总数都是一样,所以测试出来两种写法的耗时并无大的区别。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/186131

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX