พจนานุกรม Python: รับรายการค่าสำหรับรายการของคีย์


182

มีวิธีใช้แบบบิวด์อิน / อย่างรวดเร็วในการใช้รายการคีย์ในพจนานุกรมเพื่อรับรายการของรายการที่เกี่ยวข้องหรือไม่?

เช่นฉันมี:

>>> mydict = {'one': 1, 'two': 2, 'three': 3}
>>> mykeys = ['three', 'one']

ฉันmykeysจะใช้เพื่อรับค่าที่เกี่ยวข้องในพจนานุกรมเป็นรายการได้อย่างไร

>>> mydict.WHAT_GOES_HERE(mykeys)
[3, 1]

คำตอบ:


206

ความเข้าใจในรายการดูเหมือนจะเป็นวิธีที่ดีในการทำสิ่งนี้:

>>> [mydict[x] for x in mykeys]
[3, 1]

1
ถ้าmydictเป็นการเรียกใช้ฟังก์ชัน (ที่ส่งกลับค่า dict) ฟังก์ชันนี้จะเรียกใช้ฟังก์ชันหลายครั้งใช่ไหม
endolith

1
@endolith ใช่มันจะ
Eric Romrell

108

วิธีอื่นที่ไม่ใช่ list-comp:

  • สร้างรายการและโยนข้อยกเว้นหากไม่พบรหัส: map(mydict.__getitem__, mykeys)
  • สร้างรายการด้วยNoneหากไม่พบคีย์:map(mydict.get, mykeys)

หรือมิฉะนั้นการใช้operator.itemgetterสามารถส่งคืน tuple:

from operator import itemgetter
myvalues = itemgetter(*mykeys)(mydict)
# use `list(...)` if list is required

หมายเหตุ : ใน Python3 mapส่งคืนตัววนซ้ำมากกว่ารายการ ใช้list(map(...))สำหรับรายการ


54

เปรียบเทียบความเร็วเล็กน้อย:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
In[1]: l = [0,1,2,3,2,3,1,2,0]
In[2]: m = {0:10, 1:11, 2:12, 3:13}
In[3]: %timeit [m[_] for _ in l]  # list comprehension
1000000 loops, best of 3: 762 ns per loop
In[4]: %timeit map(lambda _: m[_], l)  # using 'map'
1000000 loops, best of 3: 1.66 µs per loop
In[5]: %timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
1000000 loops, best of 3: 1.65 µs per loop
In[6]: %timeit map(m.__getitem__, l)
The slowest run took 4.01 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 853 ns per loop
In[7]: %timeit map(m.get, l)
1000000 loops, best of 3: 908 ns per loop
In[33]: from operator import itemgetter
In[34]: %timeit list(itemgetter(*l)(m))
The slowest run took 9.26 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 739 ns per loop

ดังนั้นรายการความเข้าใจและ itemgetter เป็นวิธีที่เร็วที่สุดในการทำเช่นนี้

UPDATE: สำหรับรายการและแผนที่แบบสุ่มขนาดใหญ่ฉันได้ผลลัพธ์ที่แตกต่างกันเล็กน้อย:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 14:10:42) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
l = nprnd.randint(1000, size=10000)
m = dict([(_, nprnd.rand()) for _ in range(1000)])
from operator import itemgetter
import operator
f = operator.itemgetter(*l)
%timeit f(m)
%timeit list(itemgetter(*l)(m))
%timeit [m[_] for _ in l]  # list comprehension
%timeit map(m.__getitem__, l)
%timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
%timeit map(m.get, l)
%timeit map(lambda _: m[_], l)
1000 loops, best of 3: 1.14 ms per loop
1000 loops, best of 3: 1.68 ms per loop
100 loops, best of 3: 2 ms per loop
100 loops, best of 3: 2.05 ms per loop
100 loops, best of 3: 2.19 ms per loop
100 loops, best of 3: 2.53 ms per loop
100 loops, best of 3: 2.9 ms per loop

ดังนั้นในกรณีนี้ผู้ชนะที่ชัดเจนเป็นและคนนอกชัดเจนf = operator.itemgetter(*l); f(m)map(lambda _: m[_], l)

อัพเดทสำหรับ Python 3.6.4:

import numpy.random as nprnd
l = nprnd.randint(1000, size=10000)
m = dict([(_, nprnd.rand()) for _ in range(1000)])
from operator import itemgetter
import operator
f = operator.itemgetter(*l)
%timeit f(m)
%timeit list(itemgetter(*l)(m))
%timeit [m[_] for _ in l]  # list comprehension
%timeit list(map(m.__getitem__, l))
%timeit list(m[_] for _ in l)  # a generator expression passed to a list constructor.
%timeit list(map(m.get, l))
%timeit list(map(lambda _: m[_], l)
1.66 ms ± 74.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.1 ms ± 93.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.58 ms ± 88.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.36 ms ± 60.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.98 ms ± 142 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.7 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
3.14 ms ± 62.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

ดังนั้นผลลัพธ์ของ Python 3.6.4 ก็เกือบจะเหมือนกัน


15

นี่คือสามวิธี

เพิ่มขึ้นKeyErrorเมื่อไม่พบกุญแจ:

result = [mapping[k] for k in iterable]

ค่าเริ่มต้นสำหรับคีย์ที่หายไป

result = [mapping.get(k, default_value) for k in iterable]

ข้ามกุญแจที่หายไป

result = [mapping[k] for k in iterable if k in mapping]

found_keys = mapping.keys() & iterableให้TypeError: unsupported operand type(s) for &: 'list' and 'list'ในหลาม 2.7; `found_keys = [คีย์สำหรับคีย์ใน mapping.keys () ถ้าคีย์ใน iterable] ทำงานได้ดีที่สุด
NotGaeL


7

ลองสิ่งนี้:

mydict = {'one': 1, 'two': 2, 'three': 3}
mykeys = ['three', 'one'] # if there are many keys, use a set

[mydict[k] for k in mykeys]
=> [3, 1]

@PeterDeGlopper คุณสับสน items()เป็นที่ต้องการไม่จำเป็นต้องทำการค้นหาเพิ่มเติมไม่มีlen(mydict)*len(mykeys)การดำเนินการที่นี่! (โปรดสังเกตว่าฉันใช้ชุด)
ÓscarLópez

@ ÓscarLópezใช่แล้วคุณตรวจสอบทุกส่วนของพจนานุกรม iteritems ไม่ได้ให้ผลลัพธ์จนกว่าคุณต้องการดังนั้นจึงหลีกเลี่ยงการสร้างรายการตัวกลาง แต่คุณยังคงเรียกใช้ 'k ใน mykeys' (order len (mykeys) เนื่องจากเป็นรายการ) สำหรับทุก ๆ k ใน mydict สมบูรณ์โดยไม่จำเป็นเมื่อเทียบกับความเข้าใจในรายการที่ง่ายกว่าที่ใช้ทับคีย์ของฉัน
Peter DeGlopper

@ inspectorG4dget @PeterDeGlopper การดำเนินการของสมาชิกmykeysจะถูกตัดจำหน่ายเวลาคงที่ฉันใช้ชุดไม่ใช่รายการ
ÓscarLópez

2
การแปลงรายการของ OP เป็นชุดอย่างน้อยทำให้เป็นเส้นตรง แต่ยังคงเป็นเส้นตรงในโครงสร้างข้อมูลที่ไม่ถูกต้องรวมถึงลำดับที่เสียไป พิจารณากรณีของพจนานุกรม 10k และ 2 ปุ่มใน mykeys วิธีการแก้ปัญหาของคุณทำให้การทดสอบความเป็นสมาชิกแบบ 10k เทียบกับการค้นหาพจนานุกรมสองรายการเพื่อความเข้าใจในรายการอย่างง่าย โดยทั่วไปดูเหมือนว่าปลอดภัยที่จะสมมติว่าจำนวนของคีย์จะน้อยกว่าจำนวนองค์ประกอบในพจนานุกรมและหากไม่เป็นเช่นนั้นวิธีการของคุณจะไม่ใช้องค์ประกอบที่ซ้ำกัน
Peter DeGlopper


1

นุ่นทำสิ่งนี้ได้อย่างงดงามแม้ว่าความเข้าใจในรายการจะเป็น Pythonic ทางเทคนิคมากกว่า ฉันไม่มีเวลาที่จะเปรียบเทียบความเร็วตอนนี้ (ฉันจะกลับมาใหม่ในภายหลัง):

import pandas as pd
mydict = {'one': 1, 'two': 2, 'three': 3}
mykeys = ['three', 'one']
temp_df = pd.DataFrame().append(mydict)
# You can export DataFrames to a number of formats, using a list here. 
temp_df[mykeys].values[0]
# Returns: array([ 3.,  1.])

# If you want a dict then use this instead:
# temp_df[mykeys].to_dict(orient='records')[0]
# Returns: {'one': 1.0, 'three': 3.0}

-1

หรือเพียงแค่mydict.keys()นั้นเป็นวิธีการในตัวสำหรับพจนานุกรม สำรวจmydict.values()และmydict.items()และ

// อ๊ะโพสต์ OP สับสนฉัน


5
วิธีการในตัวมีประโยชน์ แต่ก็ไม่ได้ให้รายการของรายการที่เกี่ยวข้องจากรายการของคีย์ที่กำหนด คำตอบนี้ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถามนี้
stenix

-1

หลังจากปิด Python: วิธีที่มีประสิทธิภาพในการสร้างรายการจากค่า dict ด้วยลำดับที่กำหนด

ดึงกุญแจโดยไม่ต้องสร้างรายการ:

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import collections


class DictListProxy(collections.Sequence):
    def __init__(self, klist, kdict, *args, **kwargs):
        super(DictListProxy, self).__init__(*args, **kwargs)
        self.klist = klist
        self.kdict = kdict

    def __len__(self):
        return len(self.klist)

    def __getitem__(self, key):
        return self.kdict[self.klist[key]]


myDict = {'age': 'value1', 'size': 'value2', 'weigth': 'value3'}
order_list = ['age', 'weigth', 'size']

dlp = DictListProxy(order_list, myDict)

print(','.join(dlp))
print()
print(dlp[1])

ผลลัพธ์:

value1,value3,value2

value3

ซึ่งตรงกับคำสั่งที่กำหนดโดยรายการ


-2
reduce(lambda x,y: mydict.get(y) and x.append(mydict[y]) or x, mykeys,[])

ในกรณีที่มีกุญแจไม่ได้อยู่ใน Dict

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