Tại sao Python không có chức năng flatten tinh cho các danh sách?


39

Cả Erlang và Ruby đều có chức năng làm phẳng mảng. Có vẻ như một công cụ đơn giản và hữu ích để thêm vào một ngôn ngữ. Người ta có thể làm điều này:

>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> mess.flatten()
[1, 2, 3, 4, 5, 6]

Hoặc thậm chí:

>>> import itertools
>>> mess = [[1, [2]], 3, [[[4, 5]], 6]]
>>> list(itertools.flatten(mess))
[1, 2, 3, 4, 5, 6]

Thay vào đó, trong Python, người ta phải trải qua khó khăn khi viết một hàm để làm phẳng các mảng từ đầu. Điều này có vẻ ngớ ngẩn với tôi, làm phẳng các mảng là một điều phổ biến để làm. Nó giống như phải viết một hàm tùy chỉnh để nối hai mảng.

Tôi đã Googled điều này không có kết quả, vì vậy tôi đang hỏi ở đây; Có một lý do cụ thể tại sao một ngôn ngữ trưởng thành như Python 3, đi kèm với hàng trăm nghìn pin khác nhau, không cung cấp một phương pháp đơn giản để làm phẳng các mảng? Có ý tưởng bao gồm một chức năng như vậy đã được thảo luận và từ chối tại một số điểm?


2
@detly: Gần đây tôi đã bỏ lỡ việc làm phẳng khi sử dụng một số truy vấn để lấy dữ liệu từ các nguồn khác nhau. Mỗi truy vấn trả về một danh sách từ điển, vì vậy cuối cùng tôi có một danh sách các danh sách từ điển sẽ được chuyển thành một danh sách từ điển. Tôi đã sử dụng một vòng lặp + extendnhưng flatten sẽ thanh lịch hơn nhiều. Tuy nhiên, tôi sẽ làm tổn thương nếu mô hình này đủ phổ biến để biện minh cho việc làm phẳng trong thư viện chuẩn.
Giorgio

4
"Ý tôi là, hãy tưởng tượng nếu bạn đưa một lỗi vào mã của bạn mà vô tình thay đổi cấu trúc dữ liệu của bạn. Làm phẳng vẫn sẽ hoạt động, nhưng tạo ra kết quả hoàn toàn sai.": Đây là một lý do tại sao tôi thích ngôn ngữ gõ tĩnh. ;-)
Giorgio


2
@BryanOakley Xem bình luận trước đó (mặc dù không phải cho danh sách đa cấp, nói chung làm phẳng )
Izkata

3
Nó được tích hợp sẵn trong Mathemaica và tôi sử dụng nó rộng rãi.
Mỗi Alexandersson

Câu trả lời:


34

Các đề xuất cho một flattenchức năng được thêm vào thư viện tiêu chuẩn thỉnh thoảng xuất hiện trong danh sách gửi thư python-devpython- idea. Các nhà phát triển Python thường trả lời với các điểm sau:

  1. Một flatten một cấp (biến một iterable của iterable thành một iterable duy nhất) là một biểu thức một dòng tầm thường (x for y in z for x in y)và trong mọi trường hợp đã có trong thư viện chuẩn dưới tên itertools.chain.from_iterable.

  2. Các trường hợp sử dụng cho một flatten đa cấp đa mục đích là gì? Những điều này có thực sự đủ hấp dẫn để chức năng được thêm vào thư viện chuẩn không?

  3. Làm thế nào một phẳng đa cấp đa mục đích sẽ quyết định khi nào nên làm phẳng và khi nào để lại một mình? Bạn có thể nghĩ rằng một quy tắc như "làm phẳng bất cứ thứ gì hỗ trợ giao diện lặp" sẽ hoạt động, nhưng điều đó sẽ dẫn đến một vòng lặp vô hạn cho flatten('a').

Xem ví dụ Raymond Hettinger :

Nó đã được thảo luận về nauseam quảng cáo trên comp.lang.python. Mọi người dường như thích viết các phiên bản làm phẳng của riêng họ hơn là tìm các trường hợp sử dụng hợp pháp chưa có giải pháp tầm thường.

Một flattener mục đích chung cần một số cách để được nói những gì là nguyên tử và những gì có thể được chia nhỏ hơn nữa. Ngoài ra, không rõ ràng làm thế nào thuật toán nên được mở rộng để bao gồm các đầu vào với cấu trúc dữ liệu giống như cây với dữ liệu tại các nút cũng như các lá (preorder, postorder, inorder traversal, v.v.)


Nói rõ hơn, điều này có nghĩa là hàm một cấp flattencó thể được định nghĩa là lambda z: [x for y in z for x in y].
Christopher Martin

1
"Một flattener mục đích chung cần một số cách để nói thế nào là nguyên tử và những gì có thể được chia nhỏ hơn nữa.": Điều này nghe có vẻ như là một vấn đề có thể được giải quyết bằng OOP: mỗi đối tượng có thể có một flattenphương thức. Việc thực hiện phương thức này sẽ gọi đệ quy flattentrên thành phần con của nó, nếu đối tượng là một hỗn hợp. Thật không may, AFAIK không phải mọi giá trị đều là một đối tượng trong Python. Trong Ruby nó nên hoạt động mặc dù.
Giorgio

1
một người trợ giúp làm phẳng cho một lần làm phẳng một cấp thay vì tiếp tục "for in in" đã là một trường hợp đủ tốt IMO. dễ đọc
dtc

2
@Giorgio Python tránh xa các phương thức như vậy. Các giao thức được ưa thích và tôi thấy chúng hoạt động trơn tru hơn rất nhiều so với thiết kế OOP vì bạn thường không cần phải thực hiện nhiều.
jpmc26

8

Nó không đi kèm với một phương pháp như vậy nhưng nó không gọi là làm phẳng. Nó được gọi là " chuỗi ". Nó trả về một trình vòng lặp mà sau đó bạn cần sử dụng hàm list () để biến nó trở lại thành một danh sách. Nếu bạn không muốn sử dụng *, bạn có thể sử dụng phiên bản "from_iterator" thứ hai. Nó hoạt động tương tự trong Python 3. Nó sẽ thất bại nếu đầu vào danh sách không phải là danh sách danh sách.

[[1], [2, 3], [3, 4, 5]] #yes
[1, 2, [5, 6]] #no

Đã có lúc một phương thức làm phẳng trong mô-đun trình biên dịch.ast nhưng điều này không được dùng trong 2.6 và sau đó bị xóa trong 3.0. Đệ quy độ sâu tùy ý, cần thiết cho các danh sách lồng nhau tùy ý không hoạt động tốt với độ sâu đệ quy tối đa bảo thủ của Python. Lý do cho việc loại bỏ trình biên dịch phần lớn là do nó là một mớ hỗn độn . Trình biên dịch đã được chuyển thành ast nhưng flatten bị bỏ lại phía sau.

Độ sâu tùy ý có thể đạt được với các mảng của numpy và thư viện đó được làm phẳng.


Các chain.from_iteratorchức năng, như bạn nói, chỉ có thể được sử dụng để làm phẳng danh sách hai chiều. Một actualy chức năng làm phẳng, mà chấp nhận bất kỳ một lượng danh sách lồng nhau và trả về một danh sách một chiều, sẽ vẫn ồ ạt hữu ích trong nhiều trường hợp (ít nhất là trong quan điểm của tôi)
Hubro

2
@Hubro: "trong rất nhiều trường hợp" - bạn có thể kể tên sáu không?
Gareth Rees

1
@GarethRees: Tôi đã đưa ra một vài ví dụ ở đây: programmers.stackexchange.com/questions/254279/...
Hubro

Tôi cũng sẽ đi xa hơn khi lập luận rằng nếu những ngôn ngữ khác trên thực tế cung cấp một tính năng như vậy để làm phẳng một danh sách theo cách rất đơn giản được mô tả, thì đó là một trong những lập luận hấp dẫn nhất để hỗ trợ thêm khả năng đơn giản đó cho Python.
Bobort

Nó trả về một trình vòng lặp hoặc một trình tạo?
jpmc26

-1

... có lẽ bởi vì không khó để tự viết một cái

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

... và sau đó làm phẳng tất cả những gì bạn muốn :)

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]
>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 

8
Người hỏi nhận thức được điều đó: "trong Python, người ta phải trải qua khó khăn khi viết một hàm để làm phẳng các mảng từ đầu". Điều này thậm chí không cố gắng giải quyết câu hỏi được hỏi, "Điều này có vẻ ngớ ngẩn đối với tôi, làm phẳng các mảng là một việc phổ biến như vậy. Nó giống như phải viết một hàm tùy chỉnh để ghép hai mảng."
gnat

1
Hết chủ đề ... Nhưng cực hay :-) !!
SeF

câu trả lời này giống như nói với OP rằng anh ta không phải là nhà phát triển giỏi vì anh ta không biết cách tự viết mã cho hàm. Tôi khuyên bạn nên sửa đổi phần đầu câu trả lời của mình vì đây là mã hữu ích cho những người vấp phải câu hỏi, ngay cả khi lạc đề
Federico Bonelli
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.