defaultdict ที่ซ้อนกันของ defaultdict


130

มีวิธีทำให้ defaultdict เป็นค่าเริ่มต้นสำหรับ defaultdict ด้วยหรือไม่? (เช่นค่าเริ่มต้นการเรียกซ้ำแบบไม่มีที่สิ้นสุด?)

ฉันต้องการที่จะทำได้:

x = defaultdict(...stuff...)
x[0][1][0]
{}

ฉันทำได้x = defaultdict(defaultdict)แต่นั่นเป็นเพียงระดับที่สอง:

x[0]
{}
x[0][0]
KeyError: 0

มีสูตรที่สามารถทำได้ แต่สามารถทำได้โดยใช้อาร์กิวเมนต์ defaultdict ปกติหรือไม่?

โปรดทราบว่านี่เป็นการถามถึงวิธีการสร้างค่าเริ่มต้นแบบเรียกซ้ำแบบไม่มีที่สิ้นสุดดังนั้นจึงแตกต่างจากPython: defaultdict of defaultdict? ซึ่งเป็นวิธีการตั้งค่า defaultdict สองระดับ

ฉันอาจจะใช้รูปแบบพวงแต่เมื่อฉันรู้ว่าฉันไม่รู้วิธีทำมันทำให้ฉันสนใจ


อาจซ้ำกันของPython: defaultdict ของ defaultdict?
malioboro

2
ไม่จริง ... เพิ่มข้อมูลในคำถามเพื่อระบุสาเหตุ แม้ว่านั่นจะเป็นคำถามที่มีประโยชน์
Corley Brigman

คำตอบ:


169

สำหรับจำนวนระดับโดยพลการ:

def rec_dd():
    return defaultdict(rec_dd)

>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}

แน่นอนคุณสามารถทำได้ด้วยแลมด้า แต่ฉันพบว่าแลมบ์ดาอ่านได้น้อยลง ไม่ว่าในกรณีใดก็ตามจะมีลักษณะดังนี้:

rec_dd = lambda: defaultdict(rec_dd)

1
เป็นตัวอย่างที่สมบูรณ์แบบจริงๆขอบคุณ คุณช่วยขยายความได้ไหมว่าข้อมูลถูกโหลดจาก json เป็น defaultdict ของ defaultdict
David Belohrad

4
หนึ่งบันทึก หากคุณพยายามใช้รหัสนี้ในขณะที่การดองlambdaไม่ได้ผล
Viacheslav Kondratiuk

167

คำตอบอื่น ๆ ที่นี่จะบอกวิธีสร้างdefaultdictที่มี "จำนวนนับไม่ถ้วน"defaultdictแต่พวกเขาไม่สามารถระบุสิ่งที่ฉันคิดว่าอาจเป็นความต้องการเริ่มต้นของคุณซึ่งก็คือการมีค่าเริ่มต้นสองระดับ

คุณอาจกำลังมองหา:

defaultdict(lambda: defaultdict(dict))

เหตุผลที่คุณอาจชอบโครงสร้างนี้คือ:

  • มีความชัดเจนมากกว่าวิธีการแก้ปัญหาแบบวนซ้ำดังนั้นจึงน่าจะเข้าใจได้มากกว่าสำหรับผู้อ่าน
  • สิ่งนี้ทำให้ "ใบไม้" defaultdictเป็นอย่างอื่นที่ไม่ใช่พจนานุกรมเช่น: defaultdict(lambda: defaultdict(list))หรือdefaultdict(lambda: defaultdict(set))

3
defaultdict (lambda: defaultdict (list)) รูปแบบที่ถูกต้อง?
Yuvaraj Loganathan

อ๊ะใช่lambdaแบบฟอร์มถูกต้อง - เนื่องจากdefaultdict(something)ส่งคืนวัตถุคล้ายพจนานุกรม แต่defaultdictคาดว่าจะสามารถเรียกใช้ได้! ขอบคุณ!
Chris W.

4
สิ่งนี้ถูกทำเครื่องหมายว่าอาจซ้ำกับคำถามอื่น ... แต่ไม่ใช่คำถามเดิมของฉัน ฉันรู้วิธีสร้าง defaultdict สองระดับ สิ่งที่ฉันไม่รู้คือทำอย่างไรให้มันเกิดซ้ำ คำตอบนี้ในความเป็นจริงคล้ายกับstackoverflow.com/questions/5029934/…
Corley Brigman

ข้อเสียอย่างหนึ่งของวิธีแลมบ์ดาคือวัตถุที่สร้างขึ้นไม่สามารถดองได้ ... แต่คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยการหล่อเป็นแบบปกติdict(result)ก่อนการดอง
CpILL

54

มีเคล็ดลับที่ดีในการทำเช่นนั้น:

tree = lambda: defaultdict(tree)

จากนั้นคุณสามารถสร้างของคุณด้วยxx = tree()


22

คล้ายกับโซลูชันของ BrenBarn แต่ไม่มีชื่อของตัวแปรtreeสองครั้งดังนั้นจึงทำงานได้แม้จะมีการเปลี่ยนแปลงพจนานุกรมตัวแปร:

tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))

จากนั้นคุณสามารถสร้างใหม่xด้วยx = tree().


สำหรับdefเวอร์ชันนี้เราสามารถใช้ขอบเขตการปิดฟังก์ชันเพื่อป้องกันโครงสร้างข้อมูลจากข้อบกพร่องที่อินสแตนซ์ที่มีอยู่หยุดทำงานหากtreeชื่อถูกตอบกลับ ดูเหมือนว่า:

from collections import defaultdict

def tree():
    def the_tree():
        return defaultdict(the_tree)
    return the_tree()

4
ฉันจะต้องคิดเกี่ยวกับสิ่งนี้ (มันซับซ้อนกว่านี้เล็กน้อย) แต่ฉันคิดว่าประเด็นของคุณคือถ้าทำ x = tree () แต่มีคนมาทีหลังและทำ tree = ไม่มีอันนี้จะยังใช้ได้และนั่นจะไม่?
Corley Brigman

11

ฉันยังอยากจะเสนอ OOP reprสไตล์การดำเนินงานมากขึ้นซึ่งสนับสนุนการทำรังที่ไม่มีที่สิ้นสุดเช่นเดียวกับรูปแบบถูกต้อง

class NestedDefaultDict(defaultdict):
    def __init__(self, *args, **kwargs):
        super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)

    def __repr__(self):
        return repr(dict(self))

การใช้งาน:

my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']

print(my_dict)  # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}

1
เรียบร้อย! ฉันได้เพิ่ม passthrough ของ*argsและ**kwargsซึ่งช่วยให้มันทำงานเช่นเดียวกับdefaultdictการสร้างคำสั่งด้วยอาร์กิวเมนต์คำหลัก สิ่งนี้มีประโยชน์สำหรับการผ่านNestedDefaultDictเข้าjson.load
Ciprian Tomoiagă

0

นี่คือฟังก์ชันแบบเรียกซ้ำเพื่อแปลงคำสั่งเริ่มต้นแบบเรียกซ้ำเป็นคำสั่งปกติ

def defdict_to_dict(defdict, finaldict):
    # pass in an empty dict for finaldict
    for k, v in defdict.items():
        if isinstance(v, defaultdict):
            # new level created and that is the new value
            finaldict[k] = defdict_to_dict(v, {})
        else:
            finaldict[k] = v
    return finaldict

defdict_to_dict(my_rec_default_dict, {})

0

ฉันอ้างอิงจากคำตอบของ Andrew ที่นี่ หากคุณต้องการโหลดข้อมูลจาก json หรือ dict ที่มีอยู่ลงใน nester defaultdict ดูตัวอย่างนี้:

def nested_defaultdict(existing=None, **kwargs):
    if existing is None:
        existing = {}
    if not isinstance(existing, dict):
        return existing
    existing = {key: nested_defaultdict(val) for key, val in existing.items()}
    return defaultdict(nested_defaultdict, existing, **kwargs)

https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.