Trích xuất tập hợp con của các cặp khóa-giá trị từ đối tượng từ điển Python?


313

Tôi có một đối tượng từ điển lớn có một vài cặp giá trị chính (khoảng 16), nhưng tôi chỉ quan tâm đến 3 trong số chúng. Cách tốt nhất (ngắn nhất / hiệu quả / thanh lịch nhất) để đạt được điều đó là gì?

Điều tốt nhất tôi biết là:

bigdict = {'a':1,'b':2,....,'z':26} 
subdict = {'l':bigdict['l'], 'm':bigdict['m'], 'n':bigdict['n']}

Tôi chắc chắn có một cách thanh lịch hơn thế này. Ý tưởng?

Câu trả lời:


430

Bạn có thể thử:

dict((k, bigdict[k]) for k in ('l', 'm', 'n'))

... hoặc trong Con trăn 3Python phiên bản 2.7 trở lên (cảm ơn Fábio Diniz đã chỉ ra rằng nó cũng hoạt động trong 2.7) :

{k: bigdict[k] for k in ('l', 'm', 'n')}

Cập nhật: Như Håvard S chỉ ra, tôi giả sử rằng bạn biết các khóa sẽ có trong từ điển - hãy xem câu trả lời của anh ấy nếu bạn không thể đưa ra giả định đó. Ngoài ra, như timbo chỉ ra trong các nhận xét, nếu bạn muốn một khóa bị thiếu bigdictđể ánh xạ tới None, bạn có thể thực hiện:

{k: bigdict.get(k, None) for k in ('l', 'm', 'n')}

Nếu bạn đang sử dụng Python 3 và bạn chỉ muốn các khóa trong dict mới thực sự tồn tại trong bản gốc, bạn có thể sử dụng thực tế để xem các đối tượng thực hiện một số thao tác đã đặt:

{k: bigdict[k] for k in bigdict.keys() & {'l', 'm', 'n'}}

5
Sẽ thất bại nếu bigdictkhông chứak
Håvard S

7
Một chút khắc nghiệt để đánh giá thấp điều đó - có vẻ khá rõ ràng từ ngữ cảnh đối với tôi rằng nó được biết rằng các khóa này có trong từ điển ...
Mark Longair

9
{k: bigdict.get(k,None) for k in ('l', 'm', 'n')}sẽ xử lý tình huống thiếu khóa được chỉ định trong từ điển nguồn bằng cách đặt khóa trong lệnh mới thành Không có
timbo

9
@MarkLongair Tùy thuộc vào trường hợp sử dụng {k: bigdict [k] cho k in ('l', 'm', 'n') nếu k trong bigdict} có thể tốt hơn, vì nó chỉ lưu trữ các khóa thực sự có giá trị.
Briford Wylie

6
bigdict.keys() & {'l', 'm', 'n'} ==> bigdict.viewkeys() & {'l', 'm', 'n'} cho Python2.7
kxr

119

Ngắn hơn một chút, ít nhất là:

wanted_keys = ['l', 'm', 'n'] # The keys you want
dict((k, bigdict[k]) for k in wanted_keys if k in bigdict)

8
+1 cho hành vi thay thế loại trừ một khóa nếu nó không nằm trong bigdict, trái ngược với việc đặt nó thành Không có.
dhj

1
Ngoài ra: dict((k,bigdict.get(k,defaultVal) for k in wanted_keys)nếu bạn phải có tất cả các phím.
Thomas Andrew

2
Câu trả lời này được lưu bởi một "t".
sakurashinken

24
interesting_keys = ('l', 'm', 'n')
subdict = {x: bigdict[x] for x in interesting_keys if x in bigdict}

16

Một chút so sánh tốc độ cho tất cả các phương pháp được đề cập:

Python 2.7.11 |Anaconda 2.4.1 (64-bit)| (default, Jan 29 2016, 14:26:21) [MSC v.1500 64 bit (AMD64)] on win32
In[2]: import numpy.random as nprnd
keys = nprnd.randint(1000, size=10000)
bigdict = dict([(_, nprnd.rand()) for _ in range(1000)])

%timeit {key:bigdict[key] for key in keys}
%timeit dict((key, bigdict[key]) for key in keys)
%timeit dict(map(lambda k: (k, bigdict[k]), keys))
%timeit dict(filter(lambda i:i[0] in keys, bigdict.items()))
%timeit {key:value for key, value in bigdict.items() if key in keys}
100 loops, best of 3: 3.09 ms per loop
100 loops, best of 3: 3.72 ms per loop
100 loops, best of 3: 6.63 ms per loop
10 loops, best of 3: 20.3 ms per loop
100 loops, best of 3: 20.6 ms per loop

Như nó đã được dự kiến: hiểu từ điển là lựa chọn tốt nhất.


3 thao tác đầu tiên đang thực hiện một thao tác khác với hai thao tác cuối cùng và sẽ dẫn đến lỗi nếu keykhông tồn tại bigdict.
101

12

Câu trả lời này sử dụng cách hiểu từ điển tương tự như câu trả lời đã chọn, nhưng sẽ không ngoại trừ trên một mục bị thiếu.

phiên bản python 2:

{k:v for k, v in bigDict.iteritems() if k in ('l', 'm', 'n')}

phiên bản python 3:

{k:v for k, v in bigDict.items() if k in ('l', 'm', 'n')}

2
... nhưng nếu mệnh lệnh lớn là LỚN thì nó vẫn sẽ được lặp lại hoàn toàn (đây là thao tác O (n)), trong khi nghịch đảo sẽ chỉ lấy 3 mục (mỗi thao tác O (1)).
đồ trang sức bolsterlee

1
Câu hỏi là về một từ điển chỉ có 16 khóa
Meow

6

Có lẽ:

subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n']])

Python 3 thậm chí còn hỗ trợ như sau:

subdict={a:bigdict[a] for a in ['l','m','n']}

Lưu ý rằng bạn có thể kiểm tra sự tồn tại trong từ điển như sau:

subdict=dict([(x,bigdict[x]) for x in ['l', 'm', 'n'] if x in bigdict])

tôn trọng. cho trăn 3

subdict={a:bigdict[a] for a in ['l','m','n'] if a in bigdict}

Thất bại nếu akhông có trongbigdict
Håvard S

3

Được rồi, đây là điều đã làm phiền tôi vài lần, vì vậy cảm ơn bạn Jayesh đã hỏi nó.

Các câu trả lời ở trên có vẻ như là một giải pháp tốt như bất kỳ, nhưng nếu bạn đang sử dụng điều này trên toàn bộ mã của mình, sẽ rất hợp lý khi bọc chức năng IMHO. Ngoài ra, có hai trường hợp sử dụng có thể có ở đây: một trường hợp bạn quan tâm liệu tất cả các từ khóa có trong từ điển gốc hay không. và một nơi bạn không. Nó sẽ là tốt đẹp để đối xử cả hai như nhau.

Vì vậy, với giá trị hai xu của tôi, tôi khuyên bạn nên viết một lớp từ điển phụ, vd

class my_dict(dict):
    def subdict(self, keywords, fragile=False):
        d = {}
        for k in keywords:
            try:
                d[k] = self[k]
            except KeyError:
                if fragile:
                    raise
        return d

Bây giờ bạn có thể lấy ra một từ điển phụ với

orig_dict.subdict(keywords)

Ví dụ sử dụng:

#
## our keywords are letters of the alphabet
keywords = 'abcdefghijklmnopqrstuvwxyz'
#
## our dictionary maps letters to their index
d = my_dict([(k,i) for i,k in enumerate(keywords)])
print('Original dictionary:\n%r\n\n' % (d,))
#
## constructing a sub-dictionary with good keywords
oddkeywords = keywords[::2]
subd = d.subdict(oddkeywords)
print('Dictionary from odd numbered keys:\n%r\n\n' % (subd,))
#
## constructing a sub-dictionary with mixture of good and bad keywords
somebadkeywords = keywords[1::2] + 'A'
try:
    subd2 = d.subdict(somebadkeywords)
    print("We shouldn't see this message")
except KeyError:
    print("subd2 construction fails:")
    print("\toriginal dictionary doesn't contain some keys\n\n")
#
## Trying again with fragile set to false
try:
    subd3 = d.subdict(somebadkeywords, fragile=False)
    print('Dictionary constructed using some bad keys:\n%r\n\n' % (subd3,))
except KeyError:
    print("We shouldn't see this message")

Nếu bạn chạy tất cả các mã trên, bạn sẽ thấy (một cái gì đó giống như) đầu ra sau (xin lỗi vì định dạng):

Từ điển gốc:
{'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': 6, 'f': 5, 'i': 8, 'h': 7, 'k': 10, 'j': 9, 'm': 12, 'l': 11, 'o': 14, 'n': 13, 'q': 16, 'P': 15, 's': 18, 'r': 17, 'u': 20, 't': 19, 'w': 22, 'v': 21, 'y': 24, 'x ': 23,' z ': 25}

Từ điển từ các khóa được đánh số lẻ:
{'a': 0, 'c': 2, 'e': 4, 'g': 6, 'i': 8, 'k': 10, 'm': 12, ' o ': 14,' q ': 16,' s ': 18,' u ': 20,' w ': 22,' y ': 24}

xây dựng sub2 không thành công:
từ điển gốc không chứa một số khóa

Từ điển được xây dựng bằng một số khóa xấu:
{'b': 1, 'd': 3, 'f': 5, 'h': 7, 'j': 9, 'l': 11, 'n': 13, 'P': 15, 'r': 17, 't': 19, 'v': 21, 'x': 23, 'z': 25}


1
Phân lớp yêu cầu một đối tượng dict hiện có để được chuyển đổi thành loại lớp con, có thể tốn kém. Tại sao không chỉ viết một chức năng đơn giản subdict(orig_dict, keys, …)?
musiphil

3

Bạn cũng có thể sử dụng map(đây là một chức năng rất hữu ích để tìm hiểu bằng mọi cách):

sd = dict(map(lambda k: (k, l.get(k, None)), l))

Thí dụ:

large_dictionary = {'a1':123, 'a2':45, 'a3':344}
list_of_keys = ['a1', 'a3']
small_dictionary = dict(map(lambda key: (key, large_dictionary.get(key, None)), list_of_keys))

PS: Tôi đã mượn .get(key, None)câu trả lời trước đó :)


1

Còn một câu nữa (tôi thích câu trả lời của Mark Longair)

di = {'a':1,'b':2,'c':3}
req = ['a','c','w']
dict([i for i in di.iteritems() if i[0] in di and i[0] in req])

nó chậm đối lớn dict của
kxr

0

giải pháp

from operator import itemgetter
from typing import List, Dict, Union


def subdict(d: Union[Dict, List], columns: List[str]) -> Union[Dict, List[Dict]]:
    """Return a dict or list of dicts with subset of 
    columns from the d argument.
    """
    getter = itemgetter(*columns)

    if isinstance(d, list):
        result = []
        for subset in map(getter, d):
            record = dict(zip(columns, subset))
            result.append(record)
        return result
    elif isinstance(d, dict):
        return dict(zip(columns, getter(d)))

    raise ValueError('Unsupported type for `d`')

ví dụ sử dụng

# pure dict

d = dict(a=1, b=2, c=3)
print(subdict(d, ['a', 'c']))

>>> In [5]: {'a': 1, 'c': 3}
# list of dicts

d = [
    dict(a=1, b=2, c=3),
    dict(a=2, b=4, c=6),
    dict(a=4, b=8, c=12),
]

print(subdict(d, ['a', 'c']))

>>> In [5]: [{'a': 1, 'c': 3}, {'a': 2, 'c': 6}, {'a': 4, 'c': 12}]
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.