캐싱을 위해 dict에 존재하는 GET 인수에서 캐시 키를 생성해야합니다.
현재 sha1(repr(sorted(my_dict.items())))
( 내부적으로 hashlibsha1()
를 사용하는 편리한 방법입니다)를 사용하고 있지만 더 좋은 방법이 있는지 궁금합니다.
{'a': 1, 'b':2}
는 의미 적으로 {'b':2, 'a':1}
) 와 동일합니다 . 너무 복잡하지만 YMMV에는 사용하지 않았지만 피드백은 환영합니다.
캐싱을 위해 dict에 존재하는 GET 인수에서 캐시 키를 생성해야합니다.
현재 sha1(repr(sorted(my_dict.items())))
( 내부적으로 hashlibsha1()
를 사용하는 편리한 방법입니다)를 사용하고 있지만 더 좋은 방법이 있는지 궁금합니다.
{'a': 1, 'b':2}
는 의미 적으로 {'b':2, 'a':1}
) 와 동일합니다 . 너무 복잡하지만 YMMV에는 사용하지 않았지만 피드백은 환영합니다.
답변:
사전이 중첩되어 있지 않으면 dict의 항목으로 고정 세트를 만들고 다음을 사용할 수 있습니다 hash()
.
hash(frozenset(my_dict.items()))
JSON 문자열 또는 사전 표현을 생성하는 것보다 계산 집약도가 훨씬 낮습니다.
업데이트 : 아래 설명을 참조하십시오.이 방법으로 안정적인 결과를 얻지 못할 수 있습니다.
hash()
함수가 안정적인 출력을 생성하지 않는 것이 흥미로울 수 있습니다 . 이것은 동일한 입력이 주어지면 동일한 파이썬 인터프리터의 다른 인스턴스로 다른 결과를 반환한다는 것을 의미합니다. 나에게 통역사가 시작될 때마다 일종의 시드 값이 생성되는 것처럼 보입니다.
사용하는 sorted(d.items())
것만으로는 안정적인 repr을 얻을 수 없습니다. 의 일부 값은 d
사전 일 수도 있으며 해당 키는 여전히 임의의 순서로 나옵니다. 모든 키가 문자열 인 한 다음을 사용하는 것이 좋습니다.
json.dumps(d, sort_keys=True)
즉, 다른 컴퓨터 또는 Python 버전에서 해시가 안정적이어야하는 경우 이것이 방탄인지 확실하지 않습니다. 기본값으로 변경되는 것을 방지하기 위해 separators
및 ensure_ascii
인수 를 추가 할 수 있습니다 . 의견을 부탁드립니다.
ensure_ascii
인수는이 완전히 가상의 문제를 방지합니다.
make_hash
. gist.github.com/charlax/b8731de51d2ea86c6eb9
default=str
하여 그것을 극복 할 수 있습니다 dumps
. 잘 작동하는 것 같습니다.
편집 : 모든 키가 문자열 인 경우이 답변을 계속 읽기 전에 Jack O'Connor의 훨씬 간단하고 빠른 솔루션 (중첩 사전 해시에도 작동)을 참조하십시오.
답변이 수락되었지만 질문의 제목은 "python 사전 해시"이며 해당 제목과 관련하여 답변이 불완전합니다. (질문과 관련하여 답변이 완료되었습니다.)
중첩 된 사전
딕셔너리를 해시하는 방법에 대해 스택 오버플로를 검색하면 제목이 지정된이 질문에 걸려 넘어지고 중첩 된 사전을 여러 번 해시하려고하면 불만족 스러울 수 있습니다. 이 경우 위의 답변이 작동하지 않으므로 해시를 검색하기 위해 일종의 재귀 메커니즘을 구현해야합니다.
다음은 그러한 메커니즘 중 하나입니다.
import copy
def make_hash(o):
"""
Makes a hash from a dictionary, list, tuple or set to any level, that contains
only other hashable types (including any lists, tuples, sets, and
dictionaries).
"""
if isinstance(o, (set, tuple, list)):
return tuple([make_hash(e) for e in o])
elif not isinstance(o, dict):
return hash(o)
new_o = copy.deepcopy(o)
for k, v in new_o.items():
new_o[k] = make_hash(v)
return hash(tuple(frozenset(sorted(new_o.items()))))
보너스 : 해싱 객체 및 클래스
이 hash()
함수는 클래스 나 인스턴스를 해시 할 때 효과적입니다. 그러나 객체와 관련하여 해시에서 발견 한 문제는 다음과 같습니다.
class Foo(object): pass
foo = Foo()
print (hash(foo)) # 1209812346789
foo.a = 1
print (hash(foo)) # 1209812346789
foo를 변경 한 후에도 해시는 동일합니다. 이는 foo의 ID가 변경되지 않았기 때문에 해시가 동일하기 때문입니다. 현재 정의에 따라 foo를 다르게 해시하려면 실제로 변경되는 내용을 해시하는 것이 해결책입니다. 이 경우 __dict__
속성은 다음과 같습니다.
class Foo(object): pass
foo = Foo()
print (make_hash(foo.__dict__)) # 1209812346789
foo.a = 1
print (make_hash(foo.__dict__)) # -78956430974785
아아, 클래스 자체로 같은 일을하려고 할 때 :
print (make_hash(Foo.__dict__)) # TypeError: unhashable type: 'dict_proxy'
클래스 __dict__
속성은 일반적인 사전이 아닙니다.
print (type(Foo.__dict__)) # type <'dict_proxy'>
다음은 클래스를 적절히 처리하는 이전과 비슷한 메커니즘입니다.
import copy
DictProxyType = type(object.__dict__)
def make_hash(o):
"""
Makes a hash from a dictionary, list, tuple or set to any level, that
contains only other hashable types (including any lists, tuples, sets, and
dictionaries). In the case where other kinds of objects (like classes) need
to be hashed, pass in a collection of object attributes that are pertinent.
For example, a class can be hashed in this fashion:
make_hash([cls.__dict__, cls.__name__])
A function can be hashed like so:
make_hash([fn.__dict__, fn.__code__])
"""
if type(o) == DictProxyType:
o2 = {}
for k, v in o.items():
if not k.startswith("__"):
o2[k] = v
o = o2
if isinstance(o, (set, tuple, list)):
return tuple([make_hash(e) for e in o])
elif not isinstance(o, dict):
return hash(o)
new_o = copy.deepcopy(o)
for k, v in new_o.items():
new_o[k] = make_hash(v)
return hash(tuple(frozenset(sorted(new_o.items()))))
이것을 사용하여 원하는 많은 요소의 해시 튜플을 반환 할 수 있습니다.
# -7666086133114527897
print (make_hash(func.__code__))
# (-7666086133114527897, 3527539)
print (make_hash([func.__code__, func.__dict__]))
# (-7666086133114527897, 3527539, -509551383349783210)
print (make_hash([func.__code__, func.__dict__, func.__name__]))
참고 : 위의 모든 코드는 Python 3.x를 가정합니다. make_hash()
2.7.2에서 작동 한다고 가정하지만 이전 버전에서는 테스트하지 않았습니다 . 지금까지 예제 작품을 만드는 등, 난 않는 것을 알고있다
func.__code__
로 교체해야합니다
func.func_code
hash
주변 목록과 튜플에 전화를 추가했습니다 . 그렇지 않으면 내 사전의 값으로 발생하는 정수 목록을 가져 와서 해시 목록을 반환합니다.
보다 명확한 해결책이 있습니다.
def freeze(o):
if isinstance(o,dict):
return frozenset({ k:freeze(v) for k,v in o.items()}.items())
if isinstance(o,list):
return tuple([freeze(v) for v in o])
return o
def make_hash(o):
"""
makes a hash out of anything that contains only list,dict and hashable types including string and numeric types
"""
return hash(freeze(o))
if isinstance(o,list):
하면 if isinstance(obj, (set, tuple, list)):
이 기능이 모든 객체에서 작동 할 수 있습니다.
아래 코드는 Python을 다시 시작할 때 일관된 해시를 제공하지 않기 때문에 Python hash () 함수를 사용하지 않습니다 ( Python 3.3의 해시 함수가 세션간에 다른 결과를 반환 함 ). make_hashable()
객체를 중첩 된 튜플 make_hash_sha256()
로 변환하고 repr()
base64로 인코딩 된 SHA256 해시 로 변환합니다 .
import hashlib
import base64
def make_hash_sha256(o):
hasher = hashlib.sha256()
hasher.update(repr(make_hashable(o)).encode())
return base64.b64encode(hasher.digest()).decode()
def make_hashable(o):
if isinstance(o, (tuple, list)):
return tuple((make_hashable(e) for e in o))
if isinstance(o, dict):
return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))
if isinstance(o, (set, frozenset)):
return tuple(sorted(make_hashable(e) for e in o))
return o
o = dict(x=1,b=2,c=[3,4,5],d={6,7})
print(make_hashable(o))
# (('b', 2), ('c', (3, 4, 5)), ('d', (6, 7)), ('x', 1))
print(make_hash_sha256(o))
# fyt/gK6D24H9Ugexw+g3lbqnKZ0JAcgtNW+rXIDeU2Y=
make_hash_sha256(((0,1),(2,3)))==make_hash_sha256({0:1,2:3})==make_hash_sha256({2:3,0:1})!=make_hash_sha256(((2,3),(0,1)))
. 이것은 내가 찾고있는 해결책이 아니지만 좋은 중간체입니다. 나는 type(o).__name__
차별화를 강요하기 위해 각 튜플의 시작 부분 에 추가 하려고 생각하고 있습니다.
tuple(sorted((make_hashable(e) for e in o)))
2013 년 답변에서 업데이트 ...
위의 답변 중 어느 것도 나에게 신뢰할만한 것 같지 않습니다. 그 이유는 items ()를 사용하기 때문입니다. 내가 아는 한, 이것은 기계 의존적 순서로 나옵니다.
대신 이건 어때?
import hashlib
def dict_hash(the_dict, *ignore):
if ignore: # Sometimes you don't care about some items
interesting = the_dict.copy()
for item in ignore:
if item in interesting:
interesting.pop(item)
the_dict = interesting
result = hashlib.sha1(
'%s' % sorted(the_dict.items())
).hexdigest()
return result
dict.items
예상대로 정렬 된 목록을 반환하지 않는 것이 왜 중요하다고 생각 하십니까? frozenset
알아서
hash
는 고정 된 내용이 인쇄되는 방식 또는 이와 유사한 것에 신경 쓰지 않는다는 것을 알아야합니다. 여러 머신과 파이썬 버전에서 테스트하면 알 수 있습니다.
키 순서를 유지하려면 대신 hash(str(dictionary))
또는 더 hash(json.dumps(dictionary))
빠르고 더러운 솔루션을 선호합니다.
from pprint import pformat
h = hash(pformat(dictionary))
DateTime
JSON 직렬화 가능하지 않은 유사 유형에 대해서도 작동 합니다.
타사 frozendict
모듈 을 사용하여 받아쓰기를 중지하고 해시 가능하게 만들 수 있습니다.
from frozendict import frozendict
my_dict = frozendict(my_dict)
중첩 된 객체를 처리하려면 다음을 수행하십시오.
import collections.abc
def make_hashable(x):
if isinstance(x, collections.abc.Hashable):
return x
elif isinstance(x, collections.abc.Sequence):
return tuple(make_hashable(xi) for xi in x)
elif isinstance(x, collections.abc.Set):
return frozenset(make_hashable(xi) for xi in x)
elif isinstance(x, collections.abc.Mapping):
return frozendict({k: make_hashable(v) for k, v in x.items()})
else:
raise TypeError("Don't know how to make {} objects hashable".format(type(x).__name__))
더 많은 유형을 지원하려면 functools.singledispatch
(Python 3.7)을 사용하십시오.
@functools.singledispatch
def make_hashable(x):
raise TypeError("Don't know how to make {} objects hashable".format(type(x).__name__))
@make_hashable.register
def _(x: collections.abc.Hashable):
return x
@make_hashable.register
def _(x: collections.abc.Sequence):
return tuple(make_hashable(xi) for xi in x)
@make_hashable.register
def _(x: collections.abc.Set):
return frozenset(make_hashable(xi) for xi in x)
@make_hashable.register
def _(x: collections.abc.Mapping):
return frozendict({k: make_hashable(v) for k, v in x.items()})
# add your own types here
dict
의 DataFrame
객체.
elif
와 함께 작동하도록 다음 조항을 추가했습니다 DataFrame
: elif isinstance(x, pd.DataFrame): return make_hashable(hash_pandas_object(x).tolist())
답변을 편집하고 당신이 그것을 수락하는지 확인합니다 ...
hash
무작위 화는 파이썬 3.7에서 기본적으로 활성화 된 고의적 인 보안 기능입니다.
이를 위해 지도 라이브러리를 사용할 수 있습니다 . 구체적으로, maps.FrozenMap
import maps
fm = maps.FrozenMap(my_dict)
hash(fm)
설치하려면 다음을 maps
수행하십시오.
pip install maps
중첩 된 dict
경우도 처리합니다 .
import maps
fm = maps.FrozenMap.recurse(my_dict)
hash(fm)
면책 조항 : 나는 maps
도서관 의 저자입니다 .
.recurse
. maps.readthedocs.io/en/latest/api.html#maps.FrozenMap.recurse를 참조하십시오 . 목록의 순서는 의미 상 의미가 있습니다. 순서 독립성을 원하면을 호출하기 전에 목록을 세트로 변환 할 수 있습니다 .recurse
. 당신은 또한 사용할 수 있습니다 list_fn
하는 매개 변수 .recurse
와 다른 해쉬 데이터 구조를 사용하는 tuple
(.eg frozenset
)
나는 이렇게한다 :
hash(str(my_dict))
hash(str({'a': 1, 'b': 2})) != hash(str({'b': 2, 'a': 1}))
(일부 사전에서는 작동하지만 모두 작동하지는 않습니다.)