เพื่อเป็นการออกกำลังกายและส่วนใหญ่เพื่อความบันเทิงของตัวเองฉันใช้โปรแกรมแยกวิเคราะห์ packrat แบบย้อนกลับ แรงบันดาลใจสำหรับสิ่งนี้คือฉันต้องการมีความคิดที่ดีขึ้นเกี่ยวกับวิธีที่มาโครที่ไม่ถูกสุขลักษณะจะทำงานในภาษาที่มีลักษณะคล้ายอัลกอล (ตามที่ใช้ประกอบกับภาษาลิสต์ที่ปราศจากวากยสัมพันธ์ที่คุณมักจะพบ) ด้วยเหตุนี้การป้อนข้อมูลที่แตกต่างกันอาจเห็นไวยากรณ์ที่แตกต่างกันดังนั้นผลลัพธ์การแยกวิเคราะห์ที่แคชจึงไม่ถูกต้องเว้นแต่ฉันจะเก็บไวยากรณ์เวอร์ชันปัจจุบันไว้พร้อมกับผลลัพธ์การแยกวิเคราะห์ที่แคชไว้ด้วย ( แก้ไข : ผลที่ตามมาของการใช้คอลเลกชันคีย์ - ค่านี้คือควรจะไม่เปลี่ยนรูป แต่ฉันไม่ได้ตั้งใจที่จะเปิดเผยอินเทอร์เฟซเพื่ออนุญาตให้เปลี่ยนแปลงดังนั้นคอลเลกชันที่เปลี่ยนแปลงได้หรือไม่เปลี่ยนรูปก็ใช้ได้)
ปัญหาคือ python dicts ไม่สามารถปรากฏเป็นคีย์ของ Dict อื่น ๆ ได้ แม้แต่การใช้ทูเพิล (อย่างที่ฉันกำลังทำอยู่) ก็ไม่ช่วยอะไร
>>> 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
จะต้องเปลี่ยนรูปอย่างน้อยหลังจากที่คุณเริ่ม hashing มันดังนั้นทำไมไม่แคชkey
และhash
ค่าแอตทริบิวต์ของhashdict
วัตถุ? ฉันแก้ไข__key()
และ__hash__()
ทดสอบเพื่อยืนยันว่าเร็วกว่ามาก SO ไม่อนุญาตรหัสที่จัดรูปแบบในความคิดเห็นดังนั้นฉันจะเชื่อมโยงที่นี่: sam.aiki.info/hashdict.py