【发布时间】:2017-08-14 12:28:44
【问题描述】:
我需要从一组嵌套的配置值中创建一个标识符标记。
令牌可以是 URL 的一部分,因此——为了使处理更容易——它应该只包含十六进制数字(或类似的东西)。
配置值是嵌套的元组,其中包含 int、bool、str 等可散列类型的元素。
我的想法是使用内置的hash() 函数,因为即使配置结构发生变化,它也会继续工作。
这是我的第一次尝试:
def token(config):
h = hash(config)
return '{:X}'.format(h)
这将产生可变长度的标记,但这并不重要。
不过,困扰我的是令牌可能包含一个前导减号,因为hash() 的返回值是一个有符号整数。
作为避免符号的一种方法,我想到了以下解决方法,即在哈希值中添加一个常量。
这个常数应该是hash() 可以取值范围的一半(取决于平台,例如,对于 32 位/64 位系统不同):
HALF_HASH_RANGE = 2**(sys.hash_info.width-1)
这是一个合理且可移植的解决方案吗? 还是我会用这个射自己的脚?
我也看到了使用struct.pack()的建议(它返回一个bytes对象,可以在其上调用.hex()方法),但它也需要事先知道哈希值的范围(供选择的正确格式字符)。
附录:
加密强度或偶然碰撞不是问题。
在这种情况下hashlib 库的缺点是它需要编写一个转换器来遍历输入结构并将所有内容转换为bytes 表示,这很麻烦。
【问题讨论】:
-
我倾向于做
mask = (1<<sys.hash_info.width) - 1h = hash(config) & mask。 -
哦,聪明。可能不是所有最Pythonic-est的方式......
-
这是一个使用小整数的原理演示:
[i & 0xf for i in range(-8, 8)]。 FWIW,这是一个相当标准的 Python 习惯用法,用于将有符号整数转换为无符号整数。 -
好的,谢谢。好吧,你可能是对的——如果你不应该使用 Python 为什么会有位运算符。
-
这些哈希值是否打算在您的程序的单次运行之后使用?如果是这样,您不能使用内置的
hash()- 不能保证在所有 Python 版本中都以相同的方式计算,并且在某些时候,字符串哈希开始在每个程序运行时被有意地随机化。