การแม็พกับค่าในพจนานุกรมไพ ธ อน


243

Given พจนานุกรม{ k1: v1, k2: v2 ... }ฉันต้องการที่จะได้รับการบริการที่มีให้ฉันผ่านฟังก์ชั่น{ k1: f(v1), k2: f(v2) ... }f

มีฟังก์ชั่นดังกล่าวในตัวหรือไม่? หรือฉันต้องทำ

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

เป็นการดีที่ฉันจะเขียน

my_dictionary.map_values(f)

หรือ

my_dictionary.mutate_values_with(f)

นั่นคือมันไม่สำคัญสำหรับฉันถ้าพจนานุกรมต้นฉบับกลายพันธุ์หรือสร้างสำเนา


2
วิธีที่ดีกว่าในการเขียนตัวอย่างของคุณdict((k, f(v)) for k, v in mydict.iteritems())คือไม่มีวงเล็บเหลี่ยมซึ่งจะป้องกันการสร้างรายการกลางผ่านตัวสร้าง

คำตอบ:


354

ไม่มีฟังก์ชั่นดังกล่าว; วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใช้ความเข้าใจแบบ dict:

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

ใน python 2.7 ใช้.iteritems()วิธีการแทน.items()การบันทึกหน่วยความจำ ไวยากรณ์ของ dict comprehension ไม่ได้ถูกนำมาใช้จนกระทั่ง python 2.7

โปรดทราบว่าไม่มีวิธีการดังกล่าวในรายการอย่างใดอย่างหนึ่ง; คุณต้องใช้ list comprehension หรือmap()function

ด้วยเหตุนี้คุณสามารถใช้map()ฟังก์ชันสำหรับการประมวลผล dict ของคุณเช่นกัน:

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

แต่นั่นไม่สามารถอ่านได้จริงๆ


5
+1: นี่คือสิ่งที่ฉันจะทำเช่นกัน dict(zip(a, map(f, a.values())))สั้นลงเล็กน้อย แต่ฉันต้องคิดเกี่ยวกับสิ่งที่มันทำและเตือนตัวเองว่าใช่คีย์และค่าต่าง ๆ จะถูกทำซ้ำในลำดับเดียวกันหากทictไม่มีการเปลี่ยนแปลง ฉันไม่ต้องคิดเลยว่า dictcomp กำลังทำอะไรอยู่และดังนั้นจึงเป็นคำตอบที่ถูกต้อง
DSM

2
@chiborg: นั่นเป็นเพราะแทนที่จะมองขึ้นทุกคู่ค่าคีย์ในหนึ่งไปขณะนี้คุณกำลังใช้หมายเลขของปุ่มครั้งmy_dictionary.__getitem__โทร
Martijn Pieters

1
โปรดทราบว่าตั้งแต่PEP3113 (ใช้งานใน python 3.x) พารามิเตอร์ tuple ไม่ได้รับการสนับสนุนอีกต่อไป: lambda (k,v): (k, f(v))จะถูกเขียนใหม่เป็นอย่างเช่นlambda k_v: (k_v[0], f(k_v[1]))
normanius

1
ทำไมการเปิดพารามิเตอร์ที่ไม่ได้รับการบรรจุจึงได้รับการix? การปรับปรุงนั้นเป็นอย่างไร?
javadba

3
มาจากภาษา FP, Python จะดูน่าอึดอัดใจอย่างไม่น่าเชื่อ
juanchito


21

คุณสามารถทำสิ่งนี้แทนการสร้าง dict ใหม่ซึ่งอาจเหมาะสำหรับพจนานุกรมขนาดใหญ่ (ถ้าคุณไม่ต้องการสำเนา)

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)

ผลลัพธ์ในmy_dictionaryประกอบด้วย:

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

1
เย็นคุณอาจจะควรจะเปลี่ยนชื่อmapdictไปmutate_values_withหรือบางสิ่งบางอย่างที่จะทำให้มัน Crystal Clear ที่คุณเขียน Dict! :)
Tarrasch

2
zip(d.keys(), d.values())ใช้งานได้กับรุ่นอื่น ๆ มากกว่าiteritems()
ytpillai

1
@ytpillai 'zip' หรือ comprehensions ทำสำเนาแทนการเปลี่ยนค่า in-place ซึ่งเป็นจุดประสงค์ของคำตอบของฉัน คำตอบที่ได้รับการยอมรับเป็นคำตอบที่ดีที่สุดเมื่อสำเนาถูกต้อง
วงศ์

1
คำขอโทษของฉันฉันไม่ทราบว่าคุณต้องการใช้วิธีการรายการ อย่างไรก็ตามการปรับปรุงอื่นที่เป็นไปได้เช่นกัน (สำหรับผู้ที่ไม่ใช่ Python 2.7){k:f(v) for k,v in iter(d.items())}
ytpillai

1
ประหยัดพื้นที่ด้วยการทำซ้ำ
ytpillai

13

เนื่องจากPEP-0469ซึ่งเปลี่ยนชื่อ iteritems () เป็น items () และPEP-3113ซึ่งลบพารามิเตอร์ Tuple ออกจากกล่องใน Python 3.x คุณควรเขียนMartijn Pieters ♦คำตอบดังนี้:

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

4

ในขณะที่คำตอบเดิมของฉันพลาดจุดนี้ (โดยพยายามแก้ไขปัญหานี้ด้วยวิธีแก้ปัญหาการเข้าถึงคีย์ในโรงงานที่เป็น defaultdict ) ฉันได้ทำใหม่เพื่อเสนอวิธีแก้ไขปัญหาจริงกับคำถามปัจจุบัน

นี่มันคือ:

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

การใช้งาน:

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

ความคิดคือ subclass ต้นฉบับ dict เพื่อให้การทำงานที่ต้องการ: "การทำแผนที่" ฟังก์ชั่นมากกว่าค่าทั้งหมด

จุดบวกคือพจนานุกรมนี้สามารถใช้ในการจัดเก็บข้อมูลต้นฉบับราวกับว่ามันเป็นdictในขณะที่การแปลงข้อมูลใด ๆ ตามคำขอด้วยการโทรกลับ

แน่นอนว่าอย่าลังเลที่จะตั้งชื่อคลาสและฟังก์ชั่นในแบบที่คุณต้องการ (ชื่อที่เลือกในคำตอบนี้ได้รับแรงบันดาลใจจากarray_walk()ฟังก์ชั่นของ PHP )

หมายเหตุ: ทั้งtry- exceptบล็อกหรือreturnงบมีผลบังคับใช้สำหรับการทำงานที่พวกเขาจะมีการเพิ่มเติมเลียนแบบพฤติกรรมของ array_walkPHP


1
สิ่งนี้ล้มเหลวในการแก้คำถาม OP เนื่องจาก__missing__วิธีการจะไม่ถูกเรียกใช้สำหรับคีย์ที่มีอยู่ซึ่งเราต้องการแปลงเว้นแต่ว่าวิธีการที่ผ่านมาจากโรงงานจะใช้ต้นกำเนิด dict เป็นทางเลือก แต่อย่างใด แต่ไม่ได้เป็นส่วนหนึ่งของการใช้ตัวอย่าง ฉันคิดว่านี่เป็นคำตอบที่ไม่น่าพอใจสำหรับปัญหาที่เกิดขึ้น
Kaos

กุญแจที่มีอยู่ไหน
7heo.tk

จาก OP Given a dictionary { k1: v1, k2: v2 ... } ...นี้: นั่นคือคุณต้องdictเริ่มต้นด้วย ..
Kaos

ฉันอยากจะบอกว่าพวกเราทั้งคู่พูดถูก แต่ฉันเชื่อว่าเราผิดทั้งคู่ คุณพูดถูกที่ฉันตอบไม่ได้ แต่ไม่ใช่ด้วยเหตุผลที่คุณเรียกใช้ ฉันเพียงแค่พลาดจุดให้วิธีที่จะได้{v1: f(v1), v2: f(v2), ...}รับ[v1, v2, ...]และไม่ได้รับพจน์ ฉันจะแก้ไขคำตอบของฉันเพื่อแก้ไขให้ถูกต้อง
7heo.tk

2

เพื่อหลีกเลี่ยงการทำดัชนีจากแลมบ์ดาภายในเช่น:

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

คุณยังสามารถทำสิ่งต่อไปนี้

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

นั่นเป็นการจัดการที่ฉลาดภายใน 2 ทูเปิลเองในตัวอย่างที่สอง อย่างไรก็ตามมันใช้ประโยชน์จาก tuple อัตโนมัติที่แกะออกมาภายใน lambda ซึ่งไม่รองรับใน Python 3 อีกต่อไปดังนั้นlambda(k,v)จะไม่ทำงาน ดูstackoverflow.com/questions/21892989/…
Jonathan Komar

0

เพิ่งมาข้ามกรณีการใช้งานนี้ ฉันใช้คำตอบของ gensเพิ่มวิธีแบบเรียกซ้ำสำหรับการจัดการค่าที่ dicts:

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)

สิ่งนี้มีประโยชน์เมื่อจัดการกับไฟล์ json หรือ yaml ที่เข้ารหัสสตริงเป็นไบต์ใน Python 2

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.