2012-09-01 13 views
142

Được cung cấp từ điển { k1: v1, k2: v2 ... } Tôi muốn nhận được { k1: f(v1), k2: f(v2) ... } với điều kiện tôi chuyển một hàm f.Lập bản đồ các giá trị trong từ điển python

Có chức năng tích hợp nào không? Hay tôi phải làm

dict([(k, f(v)) for (k, v) in my_dictionary.iteritems()]) 

Lý tưởng nhất là tôi sẽ chỉ cần viết

my_dictionary.map_values(f) 

hoặc

my_dictionary.mutate_values_with(f) 

Nghĩa là, nó không quan trọng với tôi nếu từ điển gốc bị đột biến hoặc một bản sao được tạo.

+2

Cách tốt hơn viết ví dụ của bạn sẽ là 'dict ((k, f (v)) cho k, v trong mydict.iteritems())', tức là không có dấu ngoặc vuông, điều đó sẽ ngăn cản việc tạo danh sách trung gian thông qua trình tạo. – bereal

Trả lời

205

Không có chức năng nào như vậy; cách dễ nhất để làm điều này là sử dụng một sự hiểu biết dict:

my_dictionary = {k: f(v) for k, v in my_dictionary.items()} 

Trong python 2.7, sử dụng phương pháp .iteritems() thay vì .items() để tiết kiệm bộ nhớ. Cú pháp đọc hiểu chưa được giới thiệu cho đến python 2.7.

Lưu ý rằng không có phương pháp nào trong danh sách; bạn phải sử dụng chức năng đọc danh sách hoặc chức năng map().

Như vậy, bạn có thể sử dụng chức năng để xử lý map() dict của bạn cũng như:

my_dictionary = dict(map(lambda kv: (kv[0], f(kv[1])), my_dictionary.iteritems())) 

nhưng điều đó không có nghĩa là có thể đọc được, thực sự.

+4

+1: đây là những gì tôi sẽ làm. 'dict (zip (a, map (f, a.values ​​())))' ngắn hơn một chút, nhưng tôi phải suy nghĩ về những gì nó đang làm, và nhắc nhở bản thân rằng có, các khóa và giá trị được lặp lại theo cùng thứ tự nếu dict không thay đổi. Tôi không phải suy nghĩ gì về những gì dictcomp đang làm, và vì vậy đó là câu trả lời đúng. – DSM

+0

@ DSM: Vâng, 'zip (adict, map (f, adict.values ​​())))' lừa đòi hỏi quá nhiều sự hiểu biết từ người đọc mã thông thường, chưa kể đến một bàn tay ổn định trong việc thêm tất cả các tham số đóng! :-P –

+0

'{k: f (my_dictionary [k]) cho k trong my_dictionary}' ngắn hơn một chút, nhưng thú vị là nó cũng chậm hơn một chút (khi định thời gian với 'timeit', 500 dict và 'str()' cho 'f'). Không biết tại sao. – chiborg

13

Bạn có thể làm điều này tại chỗ, thay vì tạo ra một dict mới, có thể thích hợp hơn cho từ điển lớn (nếu bạn không cần bản sao).

def mutate_dict(f,d): 
    for k, v in d.iteritems(): 
     d[k] = f(v) 

my_dictionary = {'a':1, 'b':2} 
mutate_dict(lambda x: x+1, my_dictionary) 

kết quả trong my_dictionary chứa:

{'a': 2, 'b': 3} 
+1

Tuyệt vời, bạn nên đổi tên 'mapdict' thành' mutate_values_with' hoặc một cái gì đó để làm cho nó rõ ràng là bạn viết lại dict! :) – Tarrasch

+0

@Tarrash Đồng ý; Tôi đã đổi tên hàm. Cảm ơn. – gens

+1

'zip (d.keys(), d.values ​​())' hoạt động cho nhiều phiên bản thay vì 'iteritems()' – ytpillai

2

Trong khi câu trả lời ban đầu của tôi bị mất điểm (bằng cách cố gắng để giải quyết vấn đề này với giải pháp cho Accessing key in factory of defaultdict), tôi đã làm lại nó để đề xuất một giải pháp thực tế để câu hỏi hiện tại.

Ở đây là:

class walkableDict(dict): 
    def walk(self, callback): 
    try: 
     for key in self: 
     self[key] = callback(self[key]) 
    except TypeError: 
     return False 
    return True 

Cách sử dụng:

>>> d = walkableDict({ k1: v1, k2: v2 ... }) 
>>> d.walk(f) 

Ý tưởng là để phân lớp dict gốc để cho nó chức năng mong muốn: "bản đồ" một chức năng trên tất cả các giá trị.

Điểm cộng là từ điển này có thể được sử dụng để lưu trữ dữ liệu gốc như thể nó là dict, trong khi chuyển đổi bất kỳ dữ liệu nào theo yêu cầu gọi lại.

Tất nhiên, vui lòng đặt tên lớp và chức năng theo cách bạn muốn (tên được chọn trong câu trả lời này được lấy cảm hứng từ hàm array_walk() của PHP).

Lưu ý: Cả try - khối except lẫn return báo cáo là bắt buộc đối với các chức năng, họ đang có để tiếp tục bắt chước hành vi của của PHP array_walk.

+1

Điều này không giải quyết được câu hỏi OP vì phương thức' __missing__' sẽ không được gọi cho các khóa hiện có, mà chúng ta muốn chuyển đổi, trừ khi phương thức nhà máy đã sử dụng nguồn gốc dict như một dự phòng bằng cách nào đó, nhưng vì đó không phải là một phần của việc sử dụng ví dụ, tôi coi đây là một câu trả lời không thỏa đáng cho vấn đề ở bàn tay. – Kaos

+0

Phím hiện tại nào? –

+0

Từ OP: 'Cho từ điển {k1: v1, k2: v2 ...} ...'. Đó là, bạn đã có một 'dict' để bắt đầu với .. – Kaos

2

Do PEP-0469 đó đổi tên thành iteritems() để mục() và PEP-3113 trong đó loại bỏ tham số tuple giải nén, bằng Python 3.x bạn nên viết Martijn Pieters♦ answer như thế này:

my_dictionary = dict(map(lambda item: (item[0], f(item[1]), my_dictionary.items()))