用于任意对象的python哈希函数的替代方法
在python2.7中,我成功地使用用于任意对象的python哈希函数的替代方法,python,python-3.x,python-2.x,Python,Python 3.x,Python 2.x,在python2.7中,我成功地使用hash()将对象放入持久存储在磁盘上的存储桶中。模型代码如下所示: class PersistentDict(object): def __setitem__(self, key, value): bucket_index = (hash(key)&0xffffffff) % self.bucket_count self._store_to_bucket(bucket_index, key, value) def __get
hash()
将对象放入持久存储在磁盘上的存储桶中。模型代码如下所示:
class PersistentDict(object):
def __setitem__(self, key, value):
bucket_index = (hash(key)&0xffffffff) % self.bucket_count
self._store_to_bucket(bucket_index, key, value)
def __getitem__(self, key):
bucket_index = (hash(key)&0xffffffff) % self.bucket_count
return self._fetch_from_bucket(bucket_index)[key]
在python3中,hash()
使用了随机或固定的salt,这使得它不可用/不太理想[1]。显然,不可能使用一个。因此,我需要一个替代方案:
- 必须在解释器调用中保持稳定
- 可能需要在执行时提供参数,例如在调用中设置salt
- 必须支持任意对象(dict所支持的任何对象)
hash()
几乎适用于所有对象
[1] 使用
hash()
- 如果盐是随机的,则在解释器调用中不可靠
- 如果盐是固定的,则防止应用程序使用随机盐析功能
- 如果使用不同的盐创建了两个
PersistentDict
s,则不可用
我成功地使用了散列和zlib.adler32
的组合。最直接的实现是:
def hashkey(obj, salt=0):
"""
Create a key suitable for use in hashmaps
:param obj: object for which to create a key
:type: str, bytes, :py:class:`datetime.datetime`, object
:param salt: an optional salt to add to the key value
:type salt: int
:return: numeric key to `obj`
:rtype: int
"""
if obj is None:
return 0
if isinstance(obj, str):
return zlib.adler32(obj.encode(), salt) & 0xffffffff
elif isinstance(obj, bytes):
return zlib.adler32(obj, salt) & 0xffffffff
elif isinstance(obj, datetime_type):
return zlib.adler32(str(obj).encode(), salt) & 0xffffffff
return hash(obj) & 0xffffffff
在Python3.4.3中,这比调用普通的散列
要慢得多,大约需要0.07usec。对于常规对象,hashkey
采用~1.0usec。0.8 usec表示字节
,0.7表示str
间接费用大致如下:
- 0.1函数调用使用C(
hash(obj)
vsdef pyhash(obj):返回hash(obj)
)
- 0.2 usec到0.5 usec,用于通过
isinstance
- 对于
zlib.adler32
或zlib.crc32
vshash
:~0.160 usec vs~0.75 usec(adler和crc为+/-4 usec)
- 0.15用于
str
对象的obj.encode()
(“foobar”
)
- 1.5用于
datetime.datetime
对象的str(obj).encode()
最大的优化来自if
语句的排序。如果大多数人都希望使用普通对象,那么以下是我能想到的最快的方法:
def hashkey_c(obj, salt=0):
if obj.__class__ in hashkey_c.types:
if obj is None:
return 0
if obj.__class__ is str:
return zlib.adler32(obj.encode(), salt) & 0xffffffff
elif obj.__class__ is bytes:
return zlib.adler32(obj, salt) & 0xffffffff
elif obj.__class__ is datetime_type:
return zlib.adler32(str(obj).encode(), salt) & 0xffffffff
return hash(obj) & 0xffffffff
hashkey_c.types = {str, bytes, datetime_type, type(None)}
总时间:str
和bytes
约为0.7usec,datetime
非常糟糕,对象、整数等约为0.35usec。如果单独对dict
键(aka类型)进行显式检查,则使用dict
将类型映射为hash compariable(即hashkey.dict类型中不是obj.\uu类
,而是hashkey.explicit.\u dict类型中的obj.\uu类
)
一些补充说明:
hash
对于使用默认\uuuuu hash\uuuuu
实现的任何对象,在解释器启动期间都不稳定,包括None
- 对于包含salted类型的不可变容器(定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
我成功地使用了散列和zlib.adler32
的组合。最简单的实现是:
def hashkey(obj, salt=0):
"""
Create a key suitable for use in hashmaps
:param obj: object for which to create a key
:type: str, bytes, :py:class:`datetime.datetime`, object
:param salt: an optional salt to add to the key value
:type salt: int
:return: numeric key to `obj`
:rtype: int
"""
if obj is None:
return 0
if isinstance(obj, str):
return zlib.adler32(obj.encode(), salt) & 0xffffffff
elif isinstance(obj, bytes):
return zlib.adler32(obj, salt) & 0xffffffff
elif isinstance(obj, datetime_type):
return zlib.adler32(str(obj).encode(), salt) & 0xffffffff
return hash(obj) & 0xffffffff
在Python 3.4.3中,这比调用普通的散列
要慢得多,它大约需要0.07 usec。对于常规对象,散列键
需要~1.0 usec。0.8 usec用于字节
和0.7用于str
间接费用大致如下:
- 0.1函数调用使用C(
hash(obj)
vsdef pyhash(obj):返回hash(obj)
)
- 0.2 usec到0.5 usec,用于通过
isinstance
- 对于
zlib.adler32
或zlib.crc32
vshash
:~0.160 usec vs~0.75 usec(adler和crc为+/-4 usec)
- 0.15用于
str
对象的obj.encode()
(“foobar”
)
- 1.5用于
datetime.datetime
对象的str(obj).encode()
最大的优化来自于对if
语句的排序。如果一个人主要期望普通对象,那么下面是我能想到的最快的方法:
def hashkey_c(obj, salt=0):
if obj.__class__ in hashkey_c.types:
if obj is None:
return 0
if obj.__class__ is str:
return zlib.adler32(obj.encode(), salt) & 0xffffffff
elif obj.__class__ is bytes:
return zlib.adler32(obj, salt) & 0xffffffff
elif obj.__class__ is datetime_type:
return zlib.adler32(str(obj).encode(), salt) & 0xffffffff
return hash(obj) & 0xffffffff
hashkey_c.types = {str, bytes, datetime_type, type(None)}
总时间:str
和bytes
约为0.7usec,datetime
非常糟糕,对象、整数等约为0.35usec。如果单独对dict
键(aka类型)进行显式检查,则使用dict
将类型映射为hash compariable(即hashkey.dict类型中不是obj.\uu类
,而是hashkey.explicit.\u dict类型中的obj.\uu类
)
一些补充说明:
hash
对于使用默认\uuuuu hash\uuuuu
实现的任何对象,在解释器启动期间都不稳定,包括None
- 对于包含salted类型的不可变容器(定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
随机散列只适用于str
、bytes
和datetime
对象。对于这些类型,您只需要一个替代对象即可。@MartijnPieters谢谢!我想str和bytes可以被zlib/hashlib覆盖。我会看看是否能找到datetime的快速方法。如果时间允许,我仍然会彻底测试并寻找替代方法然而,涉及到区域;我怀疑时区的微妙之处可能会导致不同时区对象的ISO8601表示形式相同,这可能很重要。对于None
,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>实现是从type
继承的从一个解释器到另一个解释器进程,该值可以随机出现。这意味着设置pythonhasheed
不会影响