我有这个Python代码来执行此操作:
from struct import pack as _pack
def packl(lnum, pad = 1):
if lnum < 0:
raise RangeError("Cannot use packl to convert a negative integer "
"to a string.")
count = 0
l = []
while lnum > 0:
l.append(lnum & 0xffffffffffffffffL)
count += 1
lnum >>= 64
if count <= 0:
return '\0' * pad
elif pad >= 8:
lens = 8 * count % pad
pad = ((lens != 0) and (pad - lens)) or 0
l.append('>' + 'x' * pad + 'Q' * count)
l.reverse()
return _pack(*l)
else:
l.append('>' + 'Q' * count)
l.reverse()
s = _pack(*l).lstrip('\0')
lens = len(s)
if (lens % pad) != 0:
return '\0' * (pad - lens % pad) + s
else:
return s
这需要大约174个usec来将2**9700 - 1
转换为我机器上的字符串。如果我愿意使用Python 2.7和Python 3.x特定的bit_length
方法,我可以通过预先分配l
数组将其缩短到159个usecs,使其成为非常合适的大小。开始使用l[something] =
语法而不是l.append
。
我能做些什么才能让它更快?这将用于转换密码学中使用的大质数以及一些(但不是很多)较小的数字。
修改
目前这是Python中最快的选项&lt; 3.2,它接受的方向大约是接受答案的一半:
def packl(lnum, padmultiple=1):
"""Packs the lnum (which must be convertable to a long) into a
byte string 0 padded to a multiple of padmultiple bytes in size. 0
means no padding whatsoever, so that packing 0 result in an empty
string. The resulting byte string is the big-endian two's
complement representation of the passed in long."""
if lnum == 0:
return b'\0' * padmultiple
elif lnum < 0:
raise ValueError("Can only convert non-negative numbers.")
s = hex(lnum)[2:]
s = s.rstrip('L')
if len(s) & 1:
s = '0' + s
s = binascii.unhexlify(s)
if (padmultiple != 1) and (padmultiple != 0):
filled_so_far = len(s) % padmultiple
if filled_so_far != 0:
s = b'\0' * (padmultiple - filled_so_far) + s
return s
def unpackl(bytestr):
"""Treats a byte string as a sequence of base 256 digits
representing an unsigned integer in big-endian format and converts
that representation into a Python integer."""
return int(binascii.hexlify(bytestr), 16) if len(bytestr) > 0 else 0
在Python 3.2中,int
类具有to_bytes
和from_bytes
函数,可以比上面给出的方法更快地完成更多。
答案 0 :(得分:10)
这是一个通过ctypes
调用Python / C API的解决方案。目前,它使用NumPy,但如果NumPy不是一个选项,它可以纯粹用ctypes
完成。
import numpy
import ctypes
PyLong_AsByteArray = ctypes.pythonapi._PyLong_AsByteArray
PyLong_AsByteArray.argtypes = [ctypes.py_object,
numpy.ctypeslib.ndpointer(numpy.uint8),
ctypes.c_size_t,
ctypes.c_int,
ctypes.c_int]
def packl_ctypes_numpy(lnum):
a = numpy.zeros(lnum.bit_length()//8 + 1, dtype=numpy.uint8)
PyLong_AsByteArray(lnum, a, a.size, 0, 1)
return a
在我的机器上,这比你的方法快15倍。
编辑:以下是使用ctypes
的相同代码并返回字符串而不是NumPy数组:
import ctypes
PyLong_AsByteArray = ctypes.pythonapi._PyLong_AsByteArray
PyLong_AsByteArray.argtypes = [ctypes.py_object,
ctypes.c_char_p,
ctypes.c_size_t,
ctypes.c_int,
ctypes.c_int]
def packl_ctypes(lnum):
a = ctypes.create_string_buffer(lnum.bit_length()//8 + 1)
PyLong_AsByteArray(lnum, a, len(a), 0, 1)
return a.raw
这是另外两倍的速度,在我的机器上总计达到30的加速因子。
答案 1 :(得分:5)
为了完整性以及此问题的未来读者:
从Python 3.2开始,有一些函数int.from_bytes()
和int.to_bytes()
可以在字节顺序选择中执行bytes
和int
个对象之间的转换。
答案 2 :(得分:3)
我想你真的应该只使用numpy,我确信它有内置或其他内置。使用array
模块进行攻击可能会更快。但无论如何我都会捅它。
IMX,创建生成器并使用列表推导和/或内置求和比追加到列表的循环更快,因为追加可以在内部完成。哦,在一根大绳子上''sstrip'必须付出昂贵代价。
另外,一些风格点:特殊情况不够特别;并且您似乎没有收到关于新x if y else z
构造的备忘录。 :)虽然我们反正不需要它。 ;)
from struct import pack as _pack
Q_size = 64
Q_bitmask = (1L << Q_size) - 1L
def quads_gen(a_long):
while a_long:
yield a_long & Q_bitmask
a_long >>= Q_size
def pack_long_big_endian(a_long, pad = 1):
if lnum < 0:
raise RangeError("Cannot use packl to convert a negative integer "
"to a string.")
qs = list(reversed(quads_gen(a_long)))
# Pack the first one separately so we can lstrip nicely.
first = _pack('>Q', qs[0]).lstrip('\x00')
rest = _pack('>%sQ' % len(qs) - 1, *qs[1:])
count = len(first) + len(rest)
# A little math trick that depends on Python's behaviour of modulus
# for negative numbers - but it's well-defined and documented
return '\x00' * (-count % pad) + first + rest
答案 3 :(得分:3)
只是想发布一个跟随Sven的回答(效果很好)。 反向操作 - 从任意长字节对象到Python Integer对象需要以下内容(因为我找不到PyLong_FromByteArray()C API函数):
import binascii
def unpack_bytes(stringbytes):
#binascii.hexlify will be obsolete in python3 soon
#They will add a .tohex() method to bytes class
#Issue 3532 bugs.python.org
return int(binascii.hexlify(stringbytes), 16)