2010-11-14 78 views
9

Có ai biết nếu có một lớp tiêu chuẩn cho một từ điển vô hạn có thể lồng trong Python không?Từ điển lồng nhau vô tận trong Python

Tôi thấy bản thân mình lặp lại mô hình này:

d = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) 
d['abc']['def']['xyz'] += 1 

Nếu tôi muốn thêm "lớp khác" (ví dụ d['abc']['def']['xyz']['wrt']), tôi phải xác định khác làm tổ của defaultdicts.

Để khái quát hóa mẫu này, tôi đã viết một lớp đơn giản ghi đè __getitem__ để tự động tạo từ điển lồng nhau tiếp theo.

ví dụ:

d = InfiniteDict(('count',0),('total',0)) 
d['abc']['def']['xyz'].count += 0.24 
d['abc']['def']['xyz'].total += 1 
d['abc']['def']['xyz']['wrt'].count += 0.143 
d['abc']['def']['xyz']['wrt'].total += 1 

Tuy nhiên, có ai biết trước khi triển khai ý tưởng này không? Tôi đã thử dùng Google, nhưng tôi không chắc nó sẽ được gọi là gì.

Trả lời

10

Bạn có thể bắt nguồn từ defaultdict để có được hành vi mà bạn muốn:

class InfiniteDict(defaultdict): 
    def __init__(self): 
     defaultdict.__init__(self, self.__class__) 

class Counters(InfiniteDict): 
    def __init__(self): 
     InfiniteDict.__init__(self)            
     self.count = 0 
     self.total = 0 

    def show(self): 
     print "%i out of %i" % (self.count, self.total) 

Cách sử dụng của lớp này sẽ trông như thế này:

>>> d = Counters() 
>>> d[1][2][3].total = 5 
>>> d[1][2][3].show() 
0 out of 5 
>>> d[5].show() 
0 out of 0 
+1

Cảm ơn. Điều này đến gần nhất với những gì tôi đang tìm kiếm, và giúp tôi tìm ra giải pháp * chính xác *. – Cerin

2

Nó được gọi đơn giản là "cây". There is a module dường như làm những gì bạn muốn.

+3

Tôi đoán rằng 'ai đó' sẽ là tác giả liệt kê, Roc Zhou. – PaulMcG

13

Điều này tự nhiên cho vay theo định nghĩa đệ quy.

>>> import collections 
>>> def nested_dd(): 
...  return collections.defaultdict(nested_dd) 
... 
>>> foo = nested_dd() 
>>> foo 
defaultdict(<function nested_dd at 0x023F0E30>, {}) 
>>> foo[1][2]=3 
>>> foo[1] 
defaultdict(<function nested_dd at 0x023F0E30>, {2: 3}) 
>>> foo[1][2] 
3 
+1

Rất gọn gàng, nhưng có lỗi tương tự như câu trả lời đã đăng của tôi, không hỗ trợ cho các giá trị 'đếm' và 'tổng' không ở các nút lá. – PaulMcG

+1

@Paul - thực sự; bạn không thể có bánh của bạn và ăn nó quá! Có sự hỗ trợ đặc biệt cho 'count' và' total' break đối xứng, vì vậy nó sẽ phải được hardwired vào lớp ở đâu đó. – katrielalex

+1

@aaronasterling: Không có vấn đề với bất kỳ giới hạn đệ quy nào kể từ khi hàm không gọi chính nó một cách đệ quy. – sth

1

này gần:

class recursivedefaultdict(defaultdict): 
    def __init__(self, attrFactory=int): 
     self.default_factory = lambda : type(self)(attrFactory) 
     self._attrFactory = attrFactory 
    def __getattr__(self, attr): 
     newval = self._attrFactory() 
     setattr(self, attr, newval) 
     return newval 

d = recursivedefaultdict(float) 
d['abc']['def']['xyz'].count += 0.24 
d['abc']['def']['xyz'].total += 1 

data = [ 
    ('A','B','Z',1), 
    ('A','C','Y',2), 
    ('A','C','X',3), 
    ('B','A','W',4), 
    ('B','B','V',5), 
    ('B','B','U',6), 
    ('B','D','T',7), 
    ] 

table = recursivedefaultdict(int) 
for k1,k2,k3,v in data: 
    table[k1][k2][k3] = v 

Nó không hoàn toàn những gì bạn muốn, vì mức lồng nhau sâu sắc nhất không có mặc định của bạn 0 giá trị cho 'đếm' hoặc 'tổng'.

Đã chỉnh sửa: Ah, công trình này bây giờ - chỉ cần thêm phương thức __getattr__ và điều này thực hiện những gì bạn muốn.

Chỉnh sửa 2: Bây giờ bạn có thể xác định phương pháp nhà máy khác cho các thuộc tính, ngoài ints. Nhưng tất cả chúng phải cùng loại, không thể đếm được float và tổng số là int.

+0

Đề xuất nhỏ: bạn có thể sử dụng 'functools.partial (loại (self), attrFactory)'. – katrielalex

2

Các giải pháp lý tưởng, lấy cảm hứng từ câu trả lời sth của:

from collections import defaultdict 

class InfiniteDict(defaultdict): 
    def __init__(self, **kargs): 
     defaultdict.__init__(self, lambda: self.__class__(**kargs)) 
     self.__dict__.update(kargs) 

d = InfiniteDict(count=0, total=0) 
d['abc']['def'].count += 0.25 
d['abc']['def'].total += 1 
print d['abc']['def'].count 
print d['abc']['def'].total 
d['abc']['def']['xyz'].count += 0.789 
d['abc']['def']['xyz'].total += 1 
print d['abc']['def']['xyz'].count 
print d['abc']['def']['xyz'].total 
1

Tôi nghĩ rằng một lớp lót này là giải pháp gần như hoàn hảo:

>>> from collections import defaultdict 
>>> infinite_defaultdict = lambda: defaultdict(infinite_defaultdict) 
>>> d = infinite_defaultdict() 
>>> d['x']['y']['z'] = 10 

bởi Raymond hettinger trên Twitter (https://twitter.com/raymondh/status/343823801278140417)

Các vấn đề liên quan