2013-08-29 7 views
11

Nó sẽ là tiện lợi nếu một defaultdict có thể được khởi tạo dọc theo dòng saudefaultdict một bước khởi

d = defaultdict(list, (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), 
    ('b', 3))) 

để sản xuất

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

Thay vào đó, tôi nhận được

defaultdict(<type 'list'>, {'a': 2, 'c': 3, 'b': 3, 'd': 4}) 

Để có được những gì tôi cần, cuối cùng tôi phải làm điều này:

d = defaultdict(list) 
for x, y in (('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)): 
    d[x].append(y) 

Đây là bước IMO nhiều hơn cần thiết, tôi có thiếu gì đó ở đây không?

+0

Làm cách nào để sử dụng được thêm vào danh sách? Còn những loại khác thì sao? –

+0

@JonClements, điểm tốt. Tuy nhiên người ta sẽ nghĩ rằng 'danh sách' là một trường hợp sử dụng phổ biến, đủ để một phương pháp tiện lợi (có lẽ là một phương pháp lớp) được biện minh? – iruvar

+3

Phương pháp tiện lợi này không phải là chính xác những gì bạn vừa viết ra ở cuối bài viết của bạn? Tại sao không quấn ba dòng đó vào một hàm và gọi nó là một ngày? –

Trả lời

9

hành vi bạn mô tả sẽ không nhất quán với các hành vi khác của defaultdict.Có vẻ như những gì bạn muốn là FooDict sao cho

>>> f = FooDict() 
>>> f['a'] = 1 
>>> f['a'] = 2 
>>> f['a'] 
[1, 2] 

Chúng tôi có thể làm điều đó, nhưng không phải với defaultdict; cho phép gọi nó là AppendDict

import collections 

class AppendDict(collections.MutableMapping): 
    def __init__(self, container=list, append=None, pairs=()): 
     self.container = collections.defaultdict(container) 
     self.append = append or list.append 
     for key, value in pairs: 
      self[key] = value 

    def __setitem__(self, key, value): 
     self.append(self.container[key], value) 

    def __getitem__(self, key): return self.container[key] 
    def __delitem__(self, key): del self.container[key] 
    def __iter__(self): return iter(self.container) 
    def __len__(self): return len(self.container) 
3

Sắp xếp và itertools.groupby đi một chặng đường dài:

>>> L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)] 
>>> L.sort(key=lambda t:t[0]) 
>>> d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(L, key=lambda t: t[0])]) 
>>> d 
defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

Để làm cho hơn này của một lớp lót:

L = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2), ('b', 3)] 
d = defaultdict(list, [(tup[0], [t[1] for t in tup[1]]) for tup in itertools.groupby(sorted(L, key=operator.itemgetter(0)), key=lambda t: t[0])]) 

Hope this helps

+4

Nếu OP không thích vòng lặp 'for' hoàn hảo, tôi nghi ngờ rằng' itertools.groupby', 'sort', một danh sách comp và' lambda' hoặc 'itemgetter' sẽ kháng cáo. – DSM

+0

Hấp dẫn. Nhưng lưu ý rằng 'sort' và' groupby' kết thúc làm tất cả các công việc chân ở đây. để bạn có thể dễ dàng đưa đầu ra từ 'groupby' đến' dict' thông thường thay vì 'defaultdict'! – iruvar

+0

@ 1_CR: Bạn nói đúng. Nhưng, tôi đã đưa cho bạn một bản mặc định bởi vì bạn đã yêu cầu. – inspectorG4dget

13

gì bạn dường như đang thiếu là rằng defaultdict là một phân lớp đơn giản (không đặc biệt là "huyền diệu") của dict. Tất cả đối số đầu tiên là cung cấp chức năng nhà máy cho thiếu các phím. Khi bạn khởi tạo defaultdict, bạn đang khởi tạo một dict.

Nếu bạn muốn tạo ra

defaultdict(<type 'list'>, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]}) 

bạn nên khởi tạo nó theo cách của bạn sẽ khởi tạo bất kỳ khác dict có giá trị là danh sách:

d = defaultdict(list, (('a', [1, 2]), ('b', [2, 3]), ('c', [3]), ('d', [4]))) 

Nếu dữ liệu ban đầu của bạn có được trong dạng tuple có phần tử thứ 2 luôn là số nguyên, sau đó chỉ cần đi với vòng lặp for. Bạn gọi nó thêm một bước; Tôi gọi nó là cách rõ ràng và rõ ràng để làm điều đó.

+0

+1. BTW, khởi tạo nó như 'defaultdict (danh sách, {'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]})' cũng công trinh. – ChaimG

3

Tôi nghĩ rằng hầu hết trong số này là rất nhiều khói và gương để tránh một for vòng lặp đơn giản:

di={} 
for k,v in [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)]: 
    di.setdefault(k,[]).append(v) 
# di={'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Nếu mục tiêu của bạn là một trong những dòng và bạn muốn cú pháp lạm dụng mà tôi có thể không hoàn toàn tán thành hoặc hỗ trợ bạn có thể sử dụng một sự hiểu biết tác dụng phụ:

>>> li=[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 2),('b', 3)] 
>>> di={};{di.setdefault(k[0],[]).append(k[1]) for k in li} 
set([None]) 
>>> di 
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Nếu bạn thực sự muốn đi quá nhiệt tình vào không đọc được:

>>> {k1:[e for _,e in v1] for k1,v1 in {k:filter(lambda x: x[0]==k,li) for k,v in li}.items()} 
{'a': [1, 2], 'c': [3], 'b': [2, 3], 'd': [4]} 

Bạn không muốn làm điều đó. Sử dụng vòng lặp Luke!

1
>>> kvs = [(1,2), (2,3), (1,3)] 
>>> reduce(
... lambda d,(k,v): d[k].append(v) or d, 
... kvs, 
... defaultdict(list)) 
defaultdict(<type 'list'>, {1: [2, 3], 2: [3]})