Mặc định lồng nhau của defaultdict


129

Có cách nào để tạo defaultdict cũng là mặc định cho defaultdict không? (tức là defaultdict đệ quy cấp vô hạn?)

Tôi muốn có thể làm:

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

Vì vậy, tôi có thể làm x = defaultdict(defaultdict), nhưng đó chỉ là cấp độ thứ hai:

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

Có những công thức có thể làm điều này. Nhưng nó có thể được thực hiện đơn giản chỉ bằng cách sử dụng các đối số defaultdict bình thường?

Lưu ý điều này là hỏi làm thế nào để thực hiện một defaultdict đệ quy cấp vô hạn, vì vậy nó khác với Python: defaultdict của defaultdict? , đó là cách thực hiện defaultdict hai cấp.

Có lẽ tôi sẽ chỉ sử dụng mô hình , nhưng khi tôi nhận ra tôi không biết làm thế nào để làm điều này, nó đã khiến tôi thích thú.


Bản sao có thể có của Python: defaultdict của defaultdict?
malioboro

2
Không thực sự ... thêm thông tin vào câu hỏi để cho biết tại sao. Mặc dù đó là một câu hỏi hữu ích.
Corley Brigman

Câu trả lời:


168

Đối với một số cấp tùy ý:

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": {}}}}}

Tất nhiên bạn cũng có thể làm điều này với lambda, nhưng tôi thấy lambdas ít đọc hơn. Trong mọi trường hợp, nó sẽ trông như thế này:

rec_dd = lambda: defaultdict(rec_dd)

1
Quả thực là một ví dụ hoàn hảo, cảm ơn. Bạn có thể vui lòng mở rộng nó cho trường hợp, rằng dữ liệu được tải từ json vào defaultdict của defaultdict?
David Belohrad

4
Một lưu ý. Nếu bạn đang cố gắng sử dụng mã này trong khi dưa chua lambdasẽ không hoạt động.
Viacheslav Kondratiuk

166

Các câu trả lời khác ở đây cho bạn biết cách tạo một defaultdictcái chứa "vô số" defaultdict, nhưng chúng không giải quyết được những gì tôi nghĩ có thể là nhu cầu ban đầu của bạn, đó chỉ đơn giản là có một mặc định hai chiều sâu.

Bạn có thể đã tìm kiếm:

defaultdict(lambda: defaultdict(dict))

Những lý do tại sao bạn có thể thích cấu trúc này là:

  • Nó rõ ràng hơn giải pháp đệ quy, và do đó có thể dễ hiểu hơn đối với người đọc.
  • Điều này cho phép "chiếc lá" của defaultdictcái gì đó không phải là từ điển, ví dụ ,: defaultdict(lambda: defaultdict(list))hoặcdefaultdict(lambda: defaultdict(set))

3
defaultdict (lambda: defaultdict (list)) Hình thức đúng?
Yuvaraj Loganathan

Ooops, vâng, lambdahình thức là chính xác - bởi vì defaultdict(something)trả về một đối tượng giống như từ điển, nhưng defaultdictmong đợi một cuộc gọi! Cảm ơn bạn!
Chris W.

4
Điều này được đánh dấu là một bản sao có thể có của một câu hỏi khác ... nhưng đó không phải là câu hỏi ban đầu của tôi. Tôi biết cách tạo defaultdict hai cấp; những gì tôi đã không biết là làm thế nào để làm cho nó đệ quy. Trên thực tế, câu trả lời này tương tự như stackoverflow.com/questions/5029934/
Khăn

Một nhược điểm của phương pháp lambda là các vật thể mà nó tạo ra không thể bị dict(result)
vấy bẩn

54

Có một mẹo tiện lợi để làm điều đó:

tree = lambda: defaultdict(tree)

Sau đó, bạn có thể tạo của bạn xvới x = tree().


22

Tương tự như giải pháp của BrenBarn, nhưng không chứa tên của biến treehai lần, do đó, nó hoạt động ngay cả sau khi thay đổi từ điển biến:

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

Sau đó, bạn có thể tạo mỗi cái mới xvới x = tree().


Đối với defphiên bản, chúng ta có thể sử dụng phạm vi đóng chức năng để bảo vệ cấu trúc dữ liệu khỏi lỗ hổng nơi các phiên bản hiện tại ngừng hoạt động nếu treetên được bật lại. Nó trông như thế này:

from collections import defaultdict

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

4
tôi sẽ phải suy nghĩ về điều này (nó phức tạp hơn một chút). nhưng tôi nghĩ rằng quan điểm của bạn là nếu làm x = cây (), nhưng sau đó ai đó sẽ đến sau và cây = Không, cái này vẫn hoạt động, và điều đó sẽ không?
Corley Brigman

11

Tôi cũng sẽ đề xuất thêm triển khai theo kiểu OOP, hỗ trợ lồng vô hạn cũng như được định dạng đúng repr.

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

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

Sử dụng:

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
Khéo léo ! Tôi đã thêm thông qua *args**kwargscho phép nó hoạt động giống như defaultdict, cụ thể là tạo ra một lệnh với các đối số từ khóa. Điều này rất hữu ích để đi NestedDefaultDictvàojson.load
Ciprian Tomoiagă

0

đây là một hàm đệ quy để chuyển đổi một dict mặc định đệ quy thành một dict bình thường

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

Tôi dựa trên câu trả lời của Andrew ở đây. Nếu bạn đang tìm cách tải dữ liệu từ json hoặc một dict hiện có vào defaultdict của nester, hãy xem ví dụ này:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.