Chuyển đổi hai danh sách thành một từ điển


1229

Hãy tưởng tượng rằng bạn có:

keys = ['name', 'age', 'food']
values = ['Monty', 42, 'spam']

Cách đơn giản nhất để sản xuất từ ​​điển sau đây là gì?

a_dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}

Câu trả lời:


2144

Như thế này:

>>> keys = ['a', 'b', 'c']
>>> values = [1, 2, 3]
>>> dictionary = dict(zip(keys, values))
>>> print(dictionary)
{'a': 1, 'b': 2, 'c': 3}

Voila :-) Hàm dicttạo và ziphàm của cặp đôi rất hữu ích: https://docs.python.org/3/l Library / fiances.html # func-dict


3
Điều đáng chú ý là dictionary = {zip(keys, values)}sẽ không hoạt động. Bạn phải tuyên bố rõ ràng làdict(...)
Fernando Wittmann

5
Không chắc chắn lý do tại sao bạn mong đợi nó, @FernandoWittmann. {thing}là đường cú pháp để xây dựng một set()yếu tố có chứa. {*iterable}là đường cú pháp để xây dựng một setchứa một số yếu tố. {k:v}hoặc {**mapping} sẽ xây dựng một dict, nhưng về mặt cú pháp khá khác biệt.
Dan Lenski

6
Cảm ơn các bình luận Dan. Bạn đúng rồi. Sự nhầm lẫn của tôi đã xảy ra bởi vì tôi thường sử dụng sintax {}cho từ điển. Trong thực tế, nếu chúng ta thử type({})đầu ra là dict. Nhưng thực sự, nếu chúng ta cố gắng type({thing})thì đầu ra là set.
Fernando Wittmann

Tôi đến đây trong trường hợp chúng ta có thể làm tốt hơn {k:v for k, v in zip(keys, values)}. Hóa ra chúng ta có thể. +1.
JG

140

Hãy tưởng tượng rằng bạn có:

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

Cách đơn giản nhất để sản xuất từ ​​điển sau đây là gì?

dict = {'name' : 'Monty', 'age' : 42, 'food' : 'spam'}

Hầu hết người biểu diễn, người dictxây dựng vớizip

new_dict = dict(zip(keys, values))

Trong Python 3, zip bây giờ trả về một trình vòng lặp lười biếng và đây là cách tiếp cận hiệu quả nhất.

dict(zip(keys, values))không đòi hỏi một thời gian tra cứu toàn cầu cho mỗi dictzip, nhưng nó không tạo ra bất kỳ không cần thiết trung dữ liệu cấu trúc hoặc phải đối phó với tra cứu địa phương trong ứng dụng chức năng.

Á hậu, dict hiểu:

Một ứng cử viên gần gũi để sử dụng hàm tạo dict là sử dụng cú pháp riêng của cách hiểu chính tả (không phải là cách hiểu danh sách , như những người khác đã đặt nhầm):

new_dict = {k: v for k, v in zip(keys, values)}

Chọn mục này khi bạn cần ánh xạ hoặc bộ lọc dựa trên các khóa hoặc giá trị.

Trong Python 2, ziptrả về một danh sách, để tránh tạo một danh sách không cần thiết, izipthay vào đó , hãy sử dụng (bí danh thành zip có thể giảm các thay đổi mã khi bạn chuyển sang Python 3).

from itertools import izip as zip

Vì vậy, đó vẫn là (2.7):

new_dict = {k: v for k, v in zip(keys, values)}

Python 2, lý tưởng cho <= 2.6

iziptừ itertoolstrở thành ziptrong Python 3. iziptốt hơn zip cho Python 2 (vì nó tránh việc tạo danh sách không cần thiết) và lý tưởng cho 2.6 trở xuống:

from itertools import izip
new_dict = dict(izip(keys, values))

Kết quả cho tất cả các trường hợp:

Trong tất cả trường hợp:

>>> new_dict
{'age': 42, 'name': 'Monty', 'food': 'spam'}

Giải trình:

Nếu chúng ta nhìn vào sự giúp đỡ trên, dictchúng ta thấy rằng nó cần nhiều dạng đối số:


>>> help(dict)

class dict(object)
 |  dict() -> new empty dictionary
 |  dict(mapping) -> new dictionary initialized from a mapping object's
 |      (key, value) pairs
 |  dict(iterable) -> new dictionary initialized as if via:
 |      d = {}
 |      for k, v in iterable:
 |          d[k] = v
 |  dict(**kwargs) -> new dictionary initialized with the name=value pairs
 |      in the keyword argument list.  For example:  dict(one=1, two=2)

Cách tiếp cận tối ưu là sử dụng một lần lặp trong khi tránh tạo các cấu trúc dữ liệu không cần thiết. Trong Python 2, zip tạo một danh sách không cần thiết:

>>> zip(keys, values)
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

Trong Python 3, tương đương sẽ là:

>>> list(zip(keys, values))
[('name', 'Monty'), ('age', 42), ('food', 'spam')]

và Python 3 zipchỉ đơn thuần tạo ra một đối tượng có thể lặp lại:

>>> zip(keys, values)
<zip object at 0x7f0e2ad029c8>

Vì chúng tôi muốn tránh tạo cấu trúc dữ liệu không cần thiết, chúng tôi thường muốn tránh Python 2 zip(vì nó tạo ra một danh sách không cần thiết).

Các lựa chọn thay thế ít hiệu quả hơn:

Đây là một biểu thức trình tạo được truyền cho hàm tạo dict:

generator_expression = ((k, v) for k, v in zip(keys, values))
dict(generator_expression)

hoặc tương đương:

dict((k, v) for k, v in zip(keys, values))

Và đây là một sự hiểu biết danh sách đang được chuyển đến hàm tạo dict:

dict([(k, v) for k, v in zip(keys, values)])

Trong hai trường hợp đầu tiên, một lớp tính toán không hoạt động (do đó không cần thiết) được đặt trên vòng lặp zip và trong trường hợp hiểu danh sách, một danh sách bổ sung được tạo ra một cách không cần thiết. Tôi hy vọng tất cả trong số họ sẽ ít hiệu suất hơn, và chắc chắn không nhiều hơn thế.

Đánh giá hiệu suất:

Trong 64 bit Python 3.8.2 do Nix cung cấp, trên Ubuntu 16.04, được sắp xếp theo thứ tự từ nhanh nhất đến chậm nhất:

>>> min(timeit.repeat(lambda: dict(zip(keys, values))))
0.6695233230129816
>>> min(timeit.repeat(lambda: {k: v for k, v in zip(keys, values)}))
0.6941362579818815
>>> min(timeit.repeat(lambda: {keys[i]: values[i] for i in range(len(keys))}))
0.8782548159942962
>>> 
>>> min(timeit.repeat(lambda: dict([(k, v) for k, v in zip(keys, values)])))
1.077607496001292
>>> min(timeit.repeat(lambda: dict((k, v) for k, v in zip(keys, values))))
1.1840861019445583

dict(zip(keys, values)) chiến thắng ngay cả với các bộ khóa và giá trị nhỏ, nhưng đối với các bộ lớn hơn, sự khác biệt về hiệu suất sẽ trở nên lớn hơn.

Một bình luận cho biết:

mincó vẻ như là một cách xấu để so sánh hiệu suất. Chắc chắn meanvà / hoặc maxsẽ là các chỉ số hữu ích hơn nhiều cho việc sử dụng thực tế.

Chúng tôi sử dụng minbởi vì các thuật toán này là xác định. Chúng tôi muốn biết hiệu suất của các thuật toán trong các điều kiện tốt nhất có thể.

Nếu hệ điều hành bị treo vì bất kỳ lý do gì, nó không liên quan gì đến những gì chúng tôi đang cố gắng so sánh, vì vậy chúng tôi cần loại trừ những loại kết quả đó khỏi phân tích của chúng tôi.

Nếu chúng ta sử dụng mean, những loại sự kiện đó sẽ làm sai lệch kết quả của chúng ta rất nhiều và nếu chúng ta sử dụng, maxchúng ta sẽ chỉ nhận được kết quả cực đoan nhất - sự kiện có khả năng bị ảnh hưởng bởi sự kiện đó.

Một bình luận cũng nói:

Trong python 3.6.8, sử dụng các giá trị trung bình, việc hiểu chính tả thực sự vẫn nhanh hơn, khoảng 30% cho các danh sách nhỏ này. Đối với các danh sách lớn hơn (số ngẫu nhiên 10k), dictcuộc gọi nhanh hơn khoảng 10%.

Tôi đoán chúng tôi có nghĩa là dict(zip(...với số ngẫu nhiên 10k. Điều đó có vẻ như một trường hợp sử dụng khá bất thường. Điều này có nghĩa là các cuộc gọi trực tiếp nhất sẽ chiếm ưu thế trong các bộ dữ liệu lớn và tôi sẽ không ngạc nhiên nếu các hệ điều hành bị treo chiếm ưu thế trong bao lâu để chạy thử nghiệm đó, làm lệch số của bạn. Và nếu bạn sử dụng meanhoặc maxtôi sẽ coi kết quả của bạn là vô nghĩa.

Hãy sử dụng kích thước thực tế hơn trên các ví dụ hàng đầu của chúng tôi:

import numpy
import timeit
l1 = list(numpy.random.random(100))
l2 = list(numpy.random.random(100))

Và chúng tôi thấy ở đây dict(zip(...thực sự chạy nhanh hơn cho các bộ dữ liệu lớn hơn khoảng 20%.

>>> min(timeit.repeat(lambda: {k: v for k, v in zip(l1, l2)}))
9.698965263989521
>>> min(timeit.repeat(lambda: dict(zip(l1, l2))))
7.9965161079890095

1
Vào giữa năm 2019 (python 3.7.3), tôi tìm thấy các thời gian khác nhau. %% timeit trả về 1,57 \ pm 0,009microsec cho dict(zip(headList, textList))& 1,95 \ pm 0,030 microsec cho {k: v for k, v in zip(headList, textList)}. Tôi sẽ đề nghị trước đây cho dễ đọc và tốc độ. Rõ ràng điều này nhận được ở đối số min () so với mean () cho thời gian.
Mark_Anderson

1
mincó vẻ như là một cách xấu để so sánh hiệu suất. Chắc chắn meanvà / hoặc maxsẽ là các chỉ số hữu ích hơn nhiều cho việc sử dụng thực tế.
ness101

1
Trong python 3.6.8, sử dụng các giá trị trung bình, việc hiểu chính tả thực sự vẫn nhanh hơn, khoảng 30% cho các danh sách nhỏ này. Đối với các danh sách lớn hơn (số ngẫu nhiên 10k), dictcuộc gọi nhanh hơn khoảng 10%.
ness101

@ naught101 - Tôi đã giải quyết ý kiến ​​của bạn trong câu trả lời của tôi.
Aaron Hall

3
Các số 10k chỉ là một cách nhanh chóng để tạo ra 2 danh sách dài các yếu tố độc đáo. Việc tạo danh sách được thực hiện ngoài các ước tính thời gian. / / Tại sao bạn nghĩ trung bình hoặc tối đa là vô dụng? Nếu bạn đang làm điều này nhiều lần, thì thời gian trung bình của bạn là ~ n * trung bình và giới hạn trên của ~ n * max. Tối thiểu của bạn cung cấp một giới hạn thấp hơn, nhưng hầu hết mọi người quan tâm đến hiệu suất trung bình hoặc trường hợp xấu nhất. Nếu có phương sai cao, mức tối thiểu của bạn sẽ hoàn toàn không thể hiện được trong hầu hết các trường hợp. Làm thế nào tối thiểu có ý nghĩa hơn trong một kịch bản thế giới thực?
nè 101

128

Thử cái này:

>>> import itertools
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> adict = dict(itertools.izip(keys,values))
>>> adict
{'food': 'spam', 'age': 42, 'name': 'Monty'}

Trong Python 2, việc tiêu thụ bộ nhớ cũng tiết kiệm hơn so với zip.


18
Đúng với Python2, nhưng trong Python 3, zipđã tiết kiệm được bộ nhớ. docs.python.org/3/library/functions.html#zip Trong thực tế, bạn có thể thấy rằng sixsử dụng zipbằng Python 3 để thay thế itertools.izipbằng Python 2 pythonhosted.org/six .
Pedro Cattori

35
>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> dict(zip(keys, values))
{'food': 'spam', 'age': 42, 'name': 'Monty'}

28

Bạn cũng có thể sử dụng khả năng hiểu từ điển trong Python ≥ 2.7:

>>> keys = ('name', 'age', 'food')
>>> values = ('Monty', 42, 'spam')
>>> {k: v for k, v in zip(keys, values)}
{'food': 'spam', 'age': 42, 'name': 'Monty'}

17

Một cách tự nhiên hơn là sử dụng hiểu từ điển

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')    
dict = {keys[i]: values[i] for i in range(len(keys))}

đôi khi đó là cách nhanh nhất và đôi khi chậm nhất để chuyển đổi thành dictđối tượng, tại sao nó lại như vậy?, cảm ơn anh bạn.
Haritsinh Gohil


10

với Python 3.x, dùng để hiểu chính tả

keys = ('name', 'age', 'food')
values = ('Monty', 42, 'spam')

dic = {k:v for k,v in zip(keys, values)}

print(dic)

Thông tin thêm về nhận thức chính tả ở đây , một ví dụ là có:

>>> print {i : chr(65+i) for i in range(4)}
    {0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}

8

Đối với những người cần mã đơn giản và không quen thuộc với zip:

List1 = ['This', 'is', 'a', 'list']
List2 = ['Put', 'this', 'into', 'dictionary']

Điều này có thể được thực hiện bởi một dòng mã:

d = {List1[n]: List2[n] for n in range(len(List1))}

6
thất bại lớn tiếng nếu List1dài hơnList2
Jean-François Fabre

@ Jean-FrançoisFabre Nó có thực sự quan trọng không? lý do mà chúng ta nên tạo ra hai danh sách với độ dài khác nhau để xây dựng một từ điển là gì?
loved.by.Jesus

có lẽ là không, nhưng sau đây for n in range(len(List1))là một mô hình chống
Jean-François Fabre

3
  • 2018-04-18

Giải pháp tốt nhất vẫn là:

In [92]: keys = ('name', 'age', 'food')
...: values = ('Monty', 42, 'spam')
...: 

In [93]: dt = dict(zip(keys, values))
In [94]: dt
Out[94]: {'age': 42, 'food': 'spam', 'name': 'Monty'}

Chuyển nó:

    lst = [('name', 'Monty'), ('age', 42), ('food', 'spam')]
    keys, values = zip(*lst)
    In [101]: keys
    Out[101]: ('name', 'age', 'food')
    In [102]: values
    Out[102]: ('Monty', 42, 'spam')

2

bạn có thể sử dụng mã dưới đây:

dict(zip(['name', 'age', 'food'], ['Monty', 42, 'spam']))

Nhưng hãy chắc chắn rằng độ dài của các danh sách sẽ giống nhau. Nếu độ dài không giống nhau. Sau đó, chức năng zip biến thành cái dài hơn.


2

Tôi đã có nghi ngờ này trong khi tôi đang cố gắng giải quyết một vấn đề liên quan đến đồ thị. Vấn đề tôi gặp phải là tôi cần xác định một danh sách kề kề trống và muốn khởi tạo tất cả các nút với một danh sách trống, đó là khi tôi nghĩ làm thế nào để kiểm tra xem nó có đủ nhanh không, ý tôi là nó có đáng để thực hiện thao tác zip không thay vì cặp khóa-giá trị đơn giản. Sau hầu hết thời gian, yếu tố thời gian là một công cụ phá băng quan trọng. Vì vậy, tôi thực hiện thao tác thời gian cho cả hai phương pháp.

import timeit
def dictionary_creation(n_nodes):
    dummy_dict = dict()
    for node in range(n_nodes):
        dummy_dict[node] = []
    return dummy_dict


def dictionary_creation_1(n_nodes):
    keys = list(range(n_nodes))
    values = [[] for i in range(n_nodes)]
    graph = dict(zip(keys, values))
    return graph


def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

iteration = wrapper(dictionary_creation, n_nodes)
shorthand = wrapper(dictionary_creation_1, n_nodes)

for trail in range(1, 8):
    print(f'Itertion: {timeit.timeit(iteration, number=trails)}\nShorthand: {timeit.timeit(shorthand, number=trails)}')

Với n_nodes = 10.000.000 tôi nhận được,

Lặp lại: 2.825081646999024 Tốc ký: 3.535717916001886

Lặp lại: 5.051560923002398 Tốc ký: 6.255070794999483

Lặp lại: 6.52859034499852 Tốc ký: 8.221581164998497

Lặp lại: 8.683652416999394 Tốc ký: 12.599181543999293

Lặp lại: 11,587241565001023 Tốc ký: 15,27298851100204

Lặp lại: 14.816342867001367 Tốc ký: 17.162912737003353

Lặp lại: 16.645022411001264 Tốc ký: 19.976680120998935

Bạn có thể thấy rõ sau một điểm nhất định, cách tiếp cận lặp lại ở bước thứ n_ vượt qua thời gian của cách tiếp cận tốc ký ở bước thứ n-1_th.


1

Đây cũng là một ví dụ về việc thêm một giá trị danh sách trong từ điển của bạn

list1 = ["Name", "Surname", "Age"]
list2 = [["Cyd", "JEDD", "JESS"], ["DEY", "AUDIJE", "PONGARON"], [21, 32, 47]]
dic = dict(zip(list1, list2))
print(dic)

luôn đảm bảo "Khóa" (list1) của bạn luôn ở tham số đầu tiên.

{'Name': ['Cyd', 'JEDD', 'JESS'], 'Surname': ['DEY', 'AUDIJE', 'PONGARON'], 'Age': [21, 32, 47]}

0

Giải pháp như hiểu từ điển với liệt kê:

dict = {item : values[index] for index, item in enumerate(keys)}

Giải pháp như vòng lặp với liệt kê:

dict = {}
for index, item in enumerate(keys):
    dict[item] = values[index]

0

Bạn cũng có thể thử với một danh sách kết hợp hai danh sách;)

a = [1,2,3,4]
n = [5,6,7,8]

x = []
for i in a,n:
    x.append(i)

print(dict(zip(x[0], x[1])))

-1

phương pháp không có chức năng zip

l1 = [1,2,3,4,5]
l2 = ['a','b','c','d','e']
d1 = {}
for l1_ in l1:
    for l2_ in l2:
        d1[l1_] = l2_
        l2.remove(l2_)
        break  

print (d1)


{1: 'd', 2: 'b', 3: 'e', 4: 'a', 5: 'c'}

Xin chào xiyurui, Đầu vào (l1 và l2) phải là một danh sách. Nếu bạn gán l1 và l2 là một tập hợp, nó có thể không giữ nguyên thứ tự chèn. đối với tôi, tôi đã nhận được đầu ra là {1: 'a', 2: 'c', 3: 'd', 4: 'b', 5: 'e'}
Nurnaaz
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.