使用struct.unpack将字节转换为整数,而不是Python中的sum

时间:2012-10-16 23:56:50

标签: python file-io python-2.7 integer

通过浏览本网站上的各种问题,我发现了3种可读方法,可以从文件中读取4字节(32位无符号小端)整数。即:

1) myInt, = struct.unpack('<I', bytes)
2) myInt = struct.unpack('<I', bytes)[0]
3) myInt = sum(bytes[i] << (i*8) for i in range(4))

哪一种最好?我知道使用unpack需要导入struct模块,但是任何特定方法的其他优缺点是什么。

1 个答案:

答案 0 :(得分:2)

假设 best 你意味着更有效率,我会说前两个中的任何一个。

从这个微观基准可以看出,第三个更糟糕的是:

>>> bytes=b'\x10\x11\x12\x13'
>>> import struct
>>> import timeit
>>> timeit.timeit('a,=struct.unpack("<I", bytes)', 'from __main__ import struct, bytes')
0.16049504280090332
>>> timeit.timeit('a=struct.unpack("<I", bytes)[0]', 'from __main__ import struct, bytes')
0.1881420612335205
>>> timeit.timeit('sum(bytes[i] << (i*8) for i in range(4))', 'from __main__ import bytes')
1.2574431896209717

另外第三个在python2中不起作用,而第一个和第二个起作用(所以它们也更便携)。

第三个也不是可读的,虽然对struct有一点了解,但前两个版本很容易理解。

即使第一个稍快一点,我也会选择第二个,因为如果快速阅读就不容易看到这个逗号,而[0]清楚地说明你是第一个要素。 另请注意,速度的差异实际上很小,并且可能会在较新/较旧版本的python中发生变化,因此仅仅为了速度而使用第一个版本并不是一个优化。

更新

解释为什么sum如此慢(以及更多......):

考虑到python中的整数是对象,就像任何其他对象一样。 因此,当您执行5 + 2时,您将创建两个整数对象,并执行__add__方法。 因此,添加不需要一台机器指令。

这就是为什么bitshift解决方案要慢得多,它必须创建中间对象并执行一些方法调用(&#34;成本&#34;,因为参数必须由解释器打包和解包)。

你不应该认为C中有效的东西在python中是有效的。

在CPython中优化代码的黄金法则(注意:CPython不是python。我正在谈论官方实现,而不是像PyPy,Jython等那样的替代方案),是尽可能多的计算&#34; C级&#34;。按&#34; C级&#34;我指的是用C语言编写的函数内部。

在这种情况下,&#34; C功能&#34;是struck.unpack,这比使用sum的解决方案更好(注意:在sum内部有一个&#34; python级别&#34;循环比一个&#34;慢。 C级&#34;循环)。

另一个例子是map:

#python2
>>> import timeit
>>> L = ['1', '2', '3'] * 5
>>> timeit.timeit('map(int, L)', 'from __main__ import L')
5.549130916595459
>>> timeit.timeit('[int(x) for x in L]', 'from __main__ import L')
6.402460098266602

(列表越长,map解决方案相对于列表理解的速度越快)

我认为你看到this我的回答可能是有益的,我会通过O(n logn)算法展示纯蟒蛇O(n)算法如何通过任何合理的输入大小获得节拍&#34; C级&#34;循环[也请注意[senderle&#39; s answer]。

为什么这在python2中不起作用:

giacomo@jack-laptop:~$ python2
Python 2.7.3 (default, Aug  1 2012, 05:14:39) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> bytes='\x10\x11\x12\x13'
>>> import timeit
>>> timeit.timeit('sum(bytes[i] << (i*8) for i in range(4))', 'from __main__ import bytes')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/timeit.py", line 230, in timeit
    return Timer(stmt, setup, timer).timeit(number)
  File "/usr/lib/python2.7/timeit.py", line 195, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
  File "<timeit-src>", line 6, in <genexpr>
TypeError: unsupported operand type(s) for <<: 'str' and 'int'

在python2文件中,返回字符串和字符串元素是字符串,因此您无法执行移位操作。如果它对您有用,那么您使用的是python3。

相关问题