Як вправу, і в основному для власної розваги, я впроваджую синтаксичний аналізатор для зворотного відстеження. Натхненням для цього є те, що я хотів би мати кращу ідею про те, як будуть працювати гігенічні макроси в алголіноподібній мові (на відміну від діалектів без синтаксису, які ви зазвичай знайдете). Через це різні проходи через вхід можуть бачити різні граматики, тому результати кешованого аналізу недійсні, якщо я також не зберігаю поточну версію граматики разом із кешованими результатами синтаксичного аналізу. ( РЕДАКТУВАТИ : наслідком такого використання колекцій ключ-значення є те, що вони повинні бути незмінними, але я не маю наміру виставляти інтерфейс, щоб дозволити їх змінювати, тому або змінні, або незмінні колекції чудово)
Проблема в тому, що диктовки 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
має зовсім інший синтаксис, але може використовуватися як ключ. продовжуючи зверху сесію:
>>> 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
s, які можна використовувати як ключі до інших 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__()
, і протестував, щоб підтвердити, що це набагато швидше. ТАК не дозволяє форматований код у коментарях, тому я зв’яжу