Ánh xạ các giá trị trong từ điển python


243

Đưa ra một từ điển { k1: v1, k2: v2 ... }tôi muốn được { k1: f(v1), k2: f(v2) ... }cung cấp tôi vượt qua một chức năng f.

Có bất kỳ chức năng được xây dựng như vậy? 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ẽ viết

my_dictionary.map_values(f)

hoặc là

my_dictionary.mutate_values_with(f)

Đó 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
Một cách tốt hơn để viết ví dụ của bạn sẽ là dict((k, f(v)) for k, v in mydict.iteritems()), tức là không có dấu ngoặc vuông, điều đó sẽ ngăn việc tạo một danh sách trung gian thông qua một trình tạo.
mất

Câu trả lời:


354

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

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

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

Lưu ý rằng không có phương pháp như vậy trong danh sách; bạn phải sử dụng map()chức năng hiểu danh sách hoặc chức năng.

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

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

nhưng đó không phải là có thể đọc được, thực sự.


5
+1: đây là những gì tôi sẽ làm quá. 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à tự 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 một thứ tự nếu lệnh không thay đổi. Tôi không cần phải suy nghĩ gì về việc dictcomp đang làm gì, và đó là câu trả lời đúng.
DSM

2
@chiborg: đó là vì thay vì tìm kiếm tất cả các cặp khóa-giá trị trong một lần, bạn hiện đang sử dụng các my_dictionary.__getitem__cuộc gọi số lần phím .
Martijn Pieters

1
Lưu ý rằng vì PEP3113 (được triển khai trong python 3.x) các tham số tuple không được hỗ trợ nữa: lambda (k,v): (k, f(v))sẽ được viết lại thành một cái gì đó giống nhưlambda k_v: (k_v[0], f(k_v[1]))
Normanius

1
Tại sao giải nén tham số được nixed? Làm thế nào là một cải tiến ?
javadba

3
đến từ một ngôn ngữ FP, Python có vẻ khó xử vô cùng.
juanchito


21

Bạn có thể thực hiện việc này tại chỗ, thay vì tạo một từ điển mới, có thể thích hợp hơn cho các từ điển lớn (nếu bạn không cần một 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 việc my_dictionarychứa:

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

1
Thật tuyệt, bạn có thể nên đổi tên mapdictthành mutate_values_withhoặc một cái gì đó để làm cho nó rõ ràng rằng bạn viết lại chính tả! :)
Tarrasch

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

1
@ytpillai 'zip' hoặc hiểu được tạo một bản sao, thay vì thay đổi các giá trị tại chỗ, đó là mục đích của câu trả lời của tôi. Câu trả lời được chấp nhận là câu trả lời tốt nhất khi bản sao ổn.
gens

1
Tôi xin lỗi, tôi không nhận ra bạn muốn sử dụng phương pháp vật phẩm. Tuy nhiên, một cải tiến khác cũng có thể xảy ra (đối với người dùng không phải là Python 2.7){k:f(v) for k,v in iter(d.items())}
ytpillai

1
Tiết kiệm không gian bằng cách tạo một trình vòng lặp
ytpillai


4

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 bằng giải pháp cho khóa Truy cập trong nhà máy của defaultdict ), tôi đã làm lại nó để đề xuất một giải pháp thực tế cho 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

Sử dụng:

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

Ý tưởng là phân lớp dict gốc để cung cấp cho nó chức năng mong muốn: "ánh xạ" một hàm 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à một dict, trong khi chuyển đổi bất kỳ dữ liệu nào theo yêu cầu với một cuộc gọi lại.

Tất nhiên, hãy thoải mái đặt tên cho lớp và hàm 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ừ array_walk()chức năng của PHP ).

Lưu ý: Cả khối try- exceptreturncâu lệnh đều không bắt buộc đối với chức năng, chúng ở đó để bắt chước thêm hành vi của PHP array_walk.


1
Điều này không giải quyết được câu hỏi OP vì __missing__phương thức này sẽ không được gọi cho các khóa hiện có mà chúng tôi muốn chuyển đổi, trừ khi phương thức xuất xưởng được sử dụng dict gốc như một cách 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 đề hiện tại.
Kaos

Những khóa hiện có?
7heo.tk

Từ OP : Given a dictionary { k1: v1, k2: v2 ... } .... Đó là, bạn đã có một dictkhởi đầu với ..
Kaos

Tôi muốn nói rằng cả hai chúng ta đều đúng; nhưng tôi tin rằng cả hai chúng ta đều sai. Bạn nói đúng rằng câu trả lời của tôi không trả lời câu hỏi; nhưng không phải vì lý do bạn viện dẫn Tôi chỉ đơn giản là bỏ lỡ điểm, đưa ra một cách để có được {v1: f(v1), v2: f(v2), ...}đưa ra [v1, v2, ...], và không đưa ra một lệnh. Tôi sẽ chỉnh sửa câu trả lời của mình để sửa nó.
7heo.tk

2

Để tránh thực hiện lập chỉ mục từ bên trong lambda, như:

rval = dict(map(lambda kv : (kv[0], ' '.join(kv[1])), rval.iteritems()))

Bạn cũng có thể làm:

rval = dict(map(lambda(k,v) : (k, ' '.join(v)), rval.iteritems()))

Đó là một thao tác thông minh trong chính 2 tuple trong ví dụ thứ hai. Tuy nhiên, nó sử dụng tự động giải nén bộ giải nén trong lambda, không còn được hỗ trợ trong Python 3. Do đó lambda(k,v)sẽ không hoạt động. Xem stackoverflow.com/questions/21892989/ từ
Jonathan Komar

0

Chỉ cần đi qua trường hợp sử dụng này. Tôi đã triển khai câu trả lời của gens , thêm một cách tiếp cận đệ quy để xử lý các giá trị cũng là các giá trị:

def mutate_dict_in_place(f, d):
    for k, v in d.iteritems():
        if isinstance(v, dict):
            mutate_dict_in_place(f, v)
        else:
            d[k] = f(v)

# Exemple handy usage
def utf8_everywhere(d):
    mutate_dict_in_place((
        lambda value:
            value.decode('utf-8')
            if isinstance(value, bytes)
            else value
        ),
        d
    )

my_dict = {'a': b'byte1', 'b': {'c': b'byte2', 'd': b'byte3'}}
utf8_everywhere(my_dict)
print(my_dict)

Điều này có thể hữu ích khi xử lý các tệp json hoặc yaml mã hóa chuỗi dưới dạng byte trong Python 2

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.