Chức năng chuyển/giải nén (nghịch đảo của mã zip)?


342

Tôi có danh sách các mục 2 mục và tôi muốn chuyển đổi chúng thành 2 danh sách, nơi đầu tiên chứa mục đầu tiên trong mỗi bộ và danh sách thứ hai chứa mục thứ hai.

Ví dụ:

original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
# and I want to become... 
result = (['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

Có một hàm dựng sẵn nào đó?

+3

Câu trả lời tuyệt vời bên dưới, nhưng cũng xem [chuyển dịch của numpy] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html) 03 mar. 152015-03-03 13:17:26

+1

Xem câu trả lời hay để làm điều tương tự máy phát điện thay vì danh sách: [how-to-unzip-an-iterator] (http://stackoverflow.com/questions/30805000/how-to-unzip-an-iterator) 05 jan. 162016-01-05 20:31:56

546

zip là nghịch đảo của riêng nó! Miễn là bạn sử dụng toán tử * đặc biệt.

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

Cách làm việc này là bằng cách gọi zip với các đối số:

zip(('a', 1), ('b', 2), ('c', 3), ('d', 4)) 

... trừ các đối số được truyền cho zip trực tiếp (sau khi được chuyển đổi sang một tuple), do đó không cần phải lo lắng về số lượng các đối số nhận được quá lớn.

+6

Ồ, nếu nó đơn giản như vậy. Giải nén 'zip ([], [])' theo cách này không giúp bạn '[], []'. Nó giúp bạn '[]'. Nếu chỉ ... 24 feb. 142014-02-24 12:06:58

  0

@ user2357112 nó cung cấp cho bạn 'zip (* zip ([list1], [list2]))' cung cấp cho bạn '([list1, list2])'. 25 feb. 142014-02-25 21:50:13

  0

@cdhagmann: 'zip ([list1], [list2])' không bao giờ là thứ bạn muốn. Điều đó chỉ cung cấp cho bạn '[(list1, list2)]'. 25 feb. 142014-02-25 22:05:47

  0

@ user2357112 Tôi đã sử dụng '[list1]' để có nghĩa là bất kỳ danh sách nào có tên list1 và không phải là danh sách có danh sách chỉ có một danh sách như một mục nhập. Vì vậy, cho 'list1 = [1,2,3,4]' và 'list2 = [1,2,3,4]' thì 'zip (* zip (list1, list2))' cung cấp cho bạn '([1 , 2,3,4], [1,2,3,4]) ' 25 feb. 142014-02-25 22:31:06

+1

@cdhagmann: Bây giờ hãy thử với' list1 = []; list2 = [] '. 26 feb. 142014-02-26 02:53:43

  0

@cdhagmann bạn nhận được [(1, 2, 3, 4), (1, 2, 3, 4)] từ các lệnh của bạn. 03 jul. 142014-07-03 13:07:52

+1

Điều này không hoạt động trong Python3. Xem: http://stackoverflow.com/questions/24590614/python3-unzipping-a-list-of-tuples 05 jul. 142014-07-05 21:35:16

  0

zip không lưu giữ các phần tử trong vòng lặp dài hơn, do đó đệm là bắt buộc 21 sep. 142014-09-21 06:03:50

  0

'tuple (bản đồ (danh sách, zip (* ban đầu))) 'để có được chính xác kết quả được đề cập. 30 jan. 152015-01-30 04:26:57

+4

@Tommy Điều này không đúng. 'zip' hoạt động giống hệt nhau trong Python 3 ngoại trừ việc nó trả về một trình lặp thay vì một danh sách. Để có được kết quả tương tự như trên, bạn chỉ cần kết thúc cuộc gọi zip trong danh sách: 'list (zip (* [('a', 1), ('b', 2), ('c', 3), ('d', 4)])) 'sẽ xuất' [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] ' 11 mar. 152015-03-11 14:11:12

+1

thông báo: bạn có thể đáp ứng các vấn đề về bộ nhớ và hiệu suất với danh sách rất dài. 14 oct. 162016-10-14 12:44:09

  0

Đây là một câu trả lời tuyệt vời. :) 16 may. 172017-05-16 19:00:59


22

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

result = ([ a for a,b in original ], [ b for a,b in original ]) 

nên quy mô tốt hơn. Đặc biệt nếu Python làm tốt trên việc không mở rộng việc hiểu danh sách trừ khi cần thiết.

(Ngẫu nhiên, nó làm cho một 2-tuple (cặp) của danh sách, chứ không phải là một danh sách các hàng, như zip làm.)

Nếu máy phát điện thay vì danh sách thực tế là ok, điều này sẽ làm điều đó:

result = ((a for a,b in original), (b for a,b in original)) 

Máy phát không munch qua danh sách cho đến khi bạn yêu cầu từng phần tử, nhưng mặt khác, chúng giữ tham chiếu đến danh sách gốc.

+6

"Đặc biệt nếu Python làm tốt trên việc không mở rộng danh sách hiểu, trừ khi cần thiết." mmm ... thông thường, việc hiểu danh sách được mở rộng ngay lập tức - hay tôi có điều gì sai? 15 aug. 112011-08-15 19:52:57

  0

@glglgl: Không, có lẽ bạn đã đúng. Tôi chỉ hy vọng một số phiên bản tương lai có thể bắt đầu làm điều đúng đắn. (Nó không phải là không thể thay đổi, ngữ nghĩa tác dụng phụ cần thay đổi có lẽ đã được khuyến khích.) 15 oct. 122012-10-15 12:54:03

+8

Những gì bạn hy vọng để có được một expresion máy phát điện - mà đã tồn tại. 15 oct. 122012-10-15 13:12:19

  0

Không, những gì tôi hy vọng sẽ nhận được là yêu thích lâu năm "một trình biên dịch thông minh đầy đủ hơn" (hoặc thông dịch viên trong trường hợp này). Tôi không nghĩ rằng có bất cứ điều gì hợp lý mà sẽ bị phá vỡ bằng cách phân tích các bejeebus ra khỏi mã và làm một cái gì đó cực kỳ khác nhau. (như làm một bộ sưu tập lười) Python chưa bao giờ hứa tính năng này, và rất có thể sẽ không bao giờ có nó, nhưng tôi có thể thấy giấc mơ đó trong thiết kế. 18 oct. 122012-10-18 07:11:03

  0

+1 cho quy mô tốt hơn. 19 jul. 132013-07-19 10:59:09

+11

Điều này không 'quy mô tốt hơn' so với phiên bản 'zip (* x)'. 'zip (* x)' chỉ yêu cầu một thông qua vòng lặp, và không sử dụng các phần tử stack. 17 nov. 132013-11-17 16:38:20

+1

Cho dù nó "cân tốt hơn" hay không phụ thuộc vào vòng đời của dữ liệu gốc so với dữ liệu được chuyển đổi. Câu trả lời này chỉ tốt hơn việc sử dụng 'zip' nếu trường hợp sử dụng là dữ liệu được chuyển đổi được sử dụng và bị loại bỏ ngay lập tức, trong khi danh sách gốc vẫn còn trong bộ nhớ lâu hơn nữa. 15 nov. 152015-11-15 06:55:50


19

Nếu bạn có danh sách không có cùng độ dài, bạn có thể không muốn sử dụng zip theo câu trả lời của Patricks. Đây hoạt động:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4)]) 
[('a', 'b', 'c', 'd'), (1, 2, 3, 4)] 

Nhưng với danh sách dài khác nhau, zip truncates từng hạng mục với độ dài của danh sách ngắn:

>>> zip(*[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e')] 

Bạn có thể sử dụng bản đồ không có chức năng để điền vào kết quả sản phẩm nào với None:

>>> map(None, *[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e',)]) 
[('a', 'b', 'c', 'd', 'e'), (1, 2, 3, 4, None)] 

zip() nhanh hơn một chút.

+3

thú vị, bạn có thể giải thích cách 'map' hoạt động như thế không? 26 sep. 132013-09-26 15:53:09

+4

Bạn cũng có thể sử dụng 'izip_longest' 26 sep. 132013-09-26 16:52:55

+2

Được gọi là' zip_longest' cho người dùng python3. 08 mar. 162016-03-08 09:02:13

  0

@GrijeshChauhan Tôi biết điều này thực sự cũ, nhưng đó là một tính năng lạ được xây dựng trong: https://docs.python.org/2/library/functions.html#map "Nếu chức năng là Không, chức năng nhận dạng được giả định; nếu có nhiều đối số, map() trả về một danh sách gồm các bộ chứa các mục tương ứng từ tất cả các iterables (một loại hoạt động chuyển tiếp). Các đối số có thể lặp lại có thể là một chuỗi hoặc bất kỳ đối tượng nào có thể lặp lại; 14 jul. 172017-07-14 19:26:37

  0

@ cactus1 Cảm ơn, hữu ích, thời gian đó tôi đã học Python 15 jul. 172017-07-15 04:42:25


11

Tôi thích sử dụng zip(*iterable) (đó là đoạn mã bạn đang tìm kiếm) trong các chương trình của tôi như vậy:

def unzip(iterable): 
    return zip(*iterable) 

tôi thấy unzip dễ đọc hơn.


2

Đó là chỉ một cách khác để làm điều đó nhưng nó đã giúp tôi rất nhiều vì vậy tôi viết nó ở đây:

Có cấu trúc dữ liệu này:

X=[1,2,3,4] 
Y=['a','b','c','d'] 
XY=zip(X,Y) 

Hệ quả là:

In: XY 
Out: [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 

Các nhiều cách để giải nén nó và quay trở lại bản gốc là cái này theo ý kiến ​​của tôi:

x,y=zip(*XY) 

Nhưng điều này trả về một tuple vì vậy nếu bạn cần một mảng, bạn có thể sử dụng:

xy=(list(x),list(y)) 

7
>>> original = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] 
>>> tuple([list(tup) for tup in zip(*original)]) 
(['a', 'b', 'c', 'd'], [1, 2, 3, 4]) 

Cung cấp cho một tuple danh sách như trong câu hỏi.

list1, list2 = [list(tup) for tup in zip(*original)] 

Mở hai danh sách.