Như một bài tập và chủ yếu là để giải trí cho riêng tôi, tôi đang triển khai trình phân tích cú pháp packrat backtracking. Nguồn cảm hứng cho điều này là tôi muốn có ý tưởng tốt hơn về cách các macro hygenic sẽ hoạt động trong một ngôn ngữ giống algol (như được áp dụng cho các phương ngữ ngọng tự do cú pháp mà bạn thường thấy). Do đó, các lần chuyển khác nhau qua đầu vào có thể thấy các ngữ pháp khác nhau, do đó, kết quả phân tích cú pháp được lưu trong bộ nhớ cache là không hợp lệ, trừ khi tôi cũng lưu trữ phiên bản hiện tại của ngữ pháp cùng với kết quả phân tích cú pháp được lưu trong bộ nhớ cache. ( CHỈNH SỬA : hệ quả của việc sử dụng bộ sưu tập khóa-giá trị này là chúng phải là bất biến, nhưng tôi không có ý định để lộ giao diện cho phép chúng được thay đổi, vì vậy bộ sưu tập có thể thay đổi hoặc bất biến đều được)
Vấn đề là các hệ số python không thể xuất hiện dưới dạng chìa khóa cho các hệ thống khác. Ngay cả việc sử dụng một tuple (như tôi vẫn đang làm) cũng không giúp được gì.
>>> cache = {}
>>> rule = {"foo":"bar"}
>>> cache[(rule, "baz")] = "quux"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>>
Tôi đoán nó phải được giảm xuống. Giờ đây, thư viện tiêu chuẩn của python cung cấp gần đúng những gì tôi cần, collections.namedtuple
có cú pháp rất khác, nhưng có thể được sử dụng làm khóa. tiếp tục từ phiên trên:
>>> from collections import namedtuple
>>> Rule = namedtuple("Rule",rule.keys())
>>> cache[(Rule(**rule), "baz")] = "quux"
>>> cache
{(Rule(foo='bar'), 'baz'): 'quux'}
Đồng ý. Nhưng tôi phải tạo một lớp cho mỗi tổ hợp phím có thể có trong quy tắc mà tôi muốn sử dụng, điều này không quá tệ, bởi vì mỗi quy tắc phân tích cú pháp biết chính xác tham số mà nó sử dụng, vì vậy lớp đó có thể được xác định cùng một lúc. dưới dạng hàm phân tích quy tắc.
Chỉnh sửa: Một vấn đề bổ sung với namedtuple
s là chúng đúng vị trí. Hai bộ giá trị trông giống như chúng phải khác nhau trên thực tế có thể giống nhau:
>>> 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: Làm cách nào để lấy dict
s có thể được sử dụng làm chìa khóa chodict
s ?
Sau một chút về câu trả lời, đây là giải pháp hoàn chỉnh hơn mà tôi đang sử dụng. Lưu ý rằng điều này có tác dụng thêm một chút để làm cho các kết quả không thay đổi được cho các mục đích thực tế. Tất nhiên vẫn khá dễ dàng để hack xung quanh nó bằng cách gọi điện dict.__setitem__(instance, key, value)
nhưng chúng tôi đều là người lớn ở đây.
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
phải bất biến, ít nhất là sau khi bạn bắt đầu băm nó, vậy tại sao không bộ nhớ cachekey
vàhash
giá trị như các thuộc tính củahashdict
đối tượng? Tôi đã sửa đổi__key()
và__hash__()
thử nghiệm để xác nhận rằng nó nhanh hơn nhiều. SO không cho phép mã được định dạng trong nhận xét, vì vậy tôi sẽ liên kết nó ở đây: sam.aiki.info/hashdict.py