作为一种练习,并且主要是出于我的娱乐,我正在实现回溯packrat解析器。这样做的灵感是,我想更好地了解Hygenic宏如何在类似algol的语言中工作(与通常在其中找到的无语法lisp方言相对应)。因此,通过输入的不同传递可能会看到不同的语法,因此缓存的解析结果是无效的,除非我还将语法的当前版本与缓存的解析结果一起存储。( 编辑 :使用键值集合的结果是它们应该是不可变的,但我无意公开接口以允许对其进行更改,因此可变或不可变的集合都可以)
问题是python字典不能作为其他字典的键出现。即使使用元组(无论如何也要这样做)也无济于事。
>>> cache = {} >>> rule = {"foo":"bar"} >>> cache[(rule, "baz")] = "quux" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'dict' >>>
我想它必须一直是元组。现在,python标准库提供了大约所需的内容,collections.namedtuple语法非常不同,但是 可以 用作键。从以上会议继续:
collections.namedtuple
>>> from collections import namedtuple >>> Rule = namedtuple("Rule",rule.keys()) >>> cache[(Rule(**rule), "baz")] = "quux" >>> cache {(Rule(foo='bar'), 'baz'): 'quux'}
好。但是我必须为我想使用的规则中的每个可能的键组合创建一个类,这并不是很糟糕,因为每个解析规则都确切知道它使用的参数,因此可以同时定义该类。作为解析规则的函数。
编辑:namedtuples的另一个问题是它们严格位于位置。看起来应该不同的两个元组实际上可以相同:
namedtuple
>>> 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可以用作其他dicts的键的s?
dict
在回答了一些问题之后,这是我正在使用的更完整的解决方案。请注意,这做了一些额外的工作,以使所得到的指示对于实际用途几乎不可变。当然,通过致电解决它仍然很容易,dict.__setitem__(instance, key, value)但是我们都是成年人。
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()
这是制作可哈希字典的简单方法。请记住,出于明显的原因,不要在将它们嵌入另一个字典后对它们进行变异。
class hashabledict(dict): def __hash__(self): return hash(tuple(sorted(self.items())))