연습으로, 주로 내 자신의 즐거움을 위해 역 추적 packrat 파서를 구현하고 있습니다. 이에 대한 영감은 알골과 같은 언어에서 위생적인 매크로가 어떻게 작동하는지에 대해 더 잘 알고 싶습니다 (일반적으로 사용하는 구문이없는 lisp 방언에 따라). 이 때문에 입력을 통과하는 다른 과정에서 다른 문법을 볼 수 있으므로 캐시 된 구문 분석 결과와 함께 현재 버전의 문법을 저장하지 않는 한 캐시 된 구문 분석 결과는 유효하지 않습니다. ( 편집 :이 키-값 컬렉션 사용의 결과는 변경 불가능해야하지만 변경 될 수 있도록 인터페이스를 노출 할 의도가 없으므로 변경 가능하거나 변경 불가능한 컬렉션 모두 괜찮습니다)
문제는 파이썬 사전이 다른 사전에 키로 나타날 수 없다는 것입니다. 어쨌든 튜플을 사용하는 것조차 도움이되지 않습니다.
>>> cache = {}
>>> rule = {"foo":"bar"}
>>> cache[(rule, "baz")] = "quux"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>>
나는 그것이 끝까지 튜플이어야한다고 생각합니다. 이제 파이썬 표준 라이브러리는 내가 필요로하는 대략적인 것을 제공 collections.namedtuple
하고 매우 다른 구문을 가지고 있지만 키로 사용할 수 있습니다. 위 세션에서 계속 :
>>> from collections import namedtuple
>>> Rule = namedtuple("Rule",rule.keys())
>>> cache[(Rule(**rule), "baz")] = "quux"
>>> cache
{(Rule(foo='bar'), 'baz'): 'quux'}
확인. 하지만 사용하려는 규칙에서 가능한 각 키 조합에 대해 클래스를 만들어야합니다. 이는 각 구문 분석 규칙이 사용하는 매개 변수를 정확히 알고 있기 때문에 클래스를 동시에 정의 할 수 있기 때문입니다. 규칙을 구문 분석하는 함수로.
편집 : namedtuple
s 의 추가 문제 는 엄격하게 위치가 있다는 것입니다. 서로 달라야하는 것처럼 보이는 두 개의 튜플은 실제로 동일 할 수 있습니다.
>>> you = namedtuple("foo",["bar","baz"])
>>> me = namedtuple("foo",["bar","quux"])
>>> you(bar=1,baz=2) == me(bar=1,quux=2)
True
>>> bob = namedtuple("foo",["baz","bar"])
>>> you(bar=1,baz=2) == bob(bar=1,baz=2)
False
tl'dr : dict
다른 dict
s의 키로 사용할 수 있는를 어떻게 얻 습니까?
답변을 조금 해킹 한 후 사용중인보다 완벽한 솔루션이 있습니다. 이것은 결과 딕셔너리를 실제 목적을 위해 모호하게 불변으로 만들기 위해 약간의 추가 작업을 수행합니다. 물론 전화로 해킹하는 것은 여전히 쉽지만 dict.__setitem__(instance, key, value)
우리는 모두 성인입니다.
class hashdict(dict):
"""
hashable dict implementation, suitable for use as a key into
other dicts.
>>> h1 = hashdict({"apples": 1, "bananas":2})
>>> h2 = hashdict({"bananas": 3, "mangoes": 5})
>>> h1+h2
hashdict(apples=1, bananas=3, mangoes=5)
>>> d1 = {}
>>> d1[h1] = "salad"
>>> d1[h1]
'salad'
>>> d1[h2]
Traceback (most recent call last):
...
KeyError: hashdict(bananas=3, mangoes=5)
based on answers from
http://stackoverflow.com/questions/1151658/python-hashable-dicts
"""
def __key(self):
return tuple(sorted(self.items()))
def __repr__(self):
return "{0}({1})".format(self.__class__.__name__,
", ".join("{0}={1}".format(
str(i[0]),repr(i[1])) for i in self.__key()))
def __hash__(self):
return hash(self.__key())
def __setitem__(self, key, value):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def __delitem__(self, key):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def clear(self):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def pop(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def popitem(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def setdefault(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
def update(self, *args, **kwargs):
raise TypeError("{0} does not support item assignment"
.format(self.__class__.__name__))
# update is not ok because it mutates the object
# __add__ is ok because it creates a new object
# while the new object is under construction, it's ok to mutate it
def __add__(self, right):
result = hashdict(self)
dict.update(result, right)
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
hashdict
는 최소한 해싱을 시작한 후에는 변경 불가능해야합니다. 그러면key
및hash
값을hashdict
객체의 속성으로 캐시하지 않는 이유 는 무엇입니까?__key()
및을 수정 하고__hash__()
테스트하여 훨씬 더 빠른지 확인했습니다. 그래서 주석에 형식화 된 코드를 허용하지 않으므로 여기에 링크하겠습니다 : sam.aiki.info/hashdict.py