2008-09-06 10 views
307

Khi nào bạn nên sử dụng biểu thức trình tạo và khi nào bạn nên sử dụng tính năng đọc danh sách bằng Python?Biểu thức phát điện so với Danh sách biên tập

# Generator expression 
(x*2 for x in range(256)) 

# List comprehension 
[x*2 for x in range(256)] 
+16

có thể '[exp for x in iter]' chỉ là đường cho 'list ((exp for x in iter))'? hoặc là có một sự khác biệt thực hiện? – b0fh

+1

nó nghĩ rằng tôi đã có một câu hỏi có liên quan, vì vậy khi sử dụng năng suất chúng ta có thể sử dụng biểu thức máy phát từ một hàm hoặc chúng ta phải sử dụng năng suất cho một hàm để trả về đối tượng máy phát? –

+17

@ b0fh Câu trả lời rất muộn cho bình luận của bạn: trong Python2 có một sự khác biệt nhỏ, biến vòng lặp sẽ rò rỉ ra khỏi một danh sách hiểu, trong khi một biểu thức máy phát điện sẽ không bị rò rỉ. So sánh 'X = [x ** 2 cho x trong phạm vi (5)]; in x' với 'Y = danh sách (y ** 2 cho y trong phạm vi (5)); in y', thứ hai sẽ đưa ra một lỗi. Trong Python3, việc hiểu danh sách thực sự là đường cú pháp cho biểu thức máy phát được nạp vào 'list()' như bạn mong đợi, vì vậy biến vòng lặp sẽ không còn bị rò rỉ nữa (https://www.python.org/dev/) peps/pep-0289 # chi tiết). –

Trả lời

219

Câu trả lời của John rất tốt (việc hiểu danh sách tốt hơn khi bạn muốn lặp lại điều gì đó nhiều lần). Tuy nhiên, nó cũng đáng chú ý là bạn nên sử dụng một danh sách nếu bạn muốn sử dụng bất kỳ phương pháp danh sách nào. Ví dụ: mã sau đây sẽ không hoạt động:

Về cơ bản, hãy sử dụng biểu thức trình tạo nếu tất cả những gì bạn đang thực hiện đang lặp lại một lần. Nếu bạn muốn lưu trữ và sử dụng các kết quả được tạo ra, thì bạn có thể tốt hơn với một danh sách hiểu.

Vì hiệu suất là lý do phổ biến nhất để chọn cái khác, lời khuyên của tôi là đừng lo lắng về nó và chỉ cần chọn một; nếu bạn thấy rằng chương trình của bạn đang chạy quá chậm, thì chỉ khi đó bạn mới quay lại và lo lắng về việc điều chỉnh mã của bạn.

+58

Đôi khi bạn * có * để sử dụng máy phát điện - ví dụ, nếu bạn đang viết coroutines với lịch trình hợp tác bằng cách sử dụng năng suất. Nhưng nếu bạn đang làm điều đó, bạn có thể không hỏi câu hỏi này;) – ephemient

+10

Tôi biết điều này là cũ, nhưng tôi nghĩ rằng nó đáng chú ý là máy phát điện (và bất kỳ iterable) có thể được thêm vào danh sách với mở rộng: 'a = [ 1, 2, 3] b = [4, 5, 6] a.extend (b) '- a bây giờ sẽ là [1, 2, 3, 4, 5, 6]. (Bạn có thể thêm dòng mới trong các bình luận không?) – jarvisteve

+11

@jarvisteve ví dụ của bạn tuân theo các từ bạn đang nói. Ngoài ra còn có một điểm tốt ở đây. Danh sách có thể được mở rộng với máy phát điện, nhưng sau đó không có điểm trong việc làm cho nó một máy phát điện. Máy phát không thể được mở rộng với danh sách và máy phát không hoàn toàn có thể lặp lại được. Ví dụ: 'a = (x cho x trong phạm vi (0,10)), b = [1,2,3]'. 'a.extend (b)' ném một ngoại lệ. 'b.extend (a)' sẽ đánh giá tất cả a, trong trường hợp đó không có điểm nào trong việc biến nó thành một máy phát điện ngay từ đầu. –

73

Sử dụng tính toàn vẹn danh sách khi kết quả cần được lặp lại nhiều lần hoặc tốc độ tối đa. Sử dụng biểu thức trình tạo ở nơi phạm vi rộng hoặc vô hạn.

+0

Đây có thể là một chủ đề nhỏ, nhưng tiếc là "không thể googlable" ... Điều gì sẽ "tối thượng" có nghĩa là trong bối cảnh này? Tôi không phải là một người nói tiếng Anh bản địa ... :) –

+2

@GuillermoAres đây là kết quả trực tiếp của "googling" cho ý nghĩa của tối quan trọng: * quan trọng hơn bất cứ điều gì khác; tối cao. * –

+0

Vì vậy, 'danh sách' nhanh hơn các biểu thức' trình tạo'? Từ đọc câu trả lời của dF, nó đi qua rằng nó là một cách khác xung quanh. –

133

Lặp lại biểu thức máy phát điện hoặc danh sách hiểu cũng sẽ làm tương tự. Tuy nhiên, việc đọc danh sách sẽ tạo toàn bộ danh sách trong bộ nhớ trước khi biểu thức máy phát sẽ tạo các mục khi đang di chuyển, vì vậy bạn có thể sử dụng nó cho các chuỗi rất lớn (và cũng vô hạn!).

+21

+1 cho vô hạn. Bạn không thể làm điều đó với một danh sách, bất kể bạn quan tâm đến hiệu suất như thế nào. –

+0

Bạn có thể tạo các trình tạo vô hạn bằng phương pháp đọc không? – Annan

+4

@Annan Chỉ khi bạn đã có quyền truy cập vào một trình tạo vô hạn khác. Ví dụ, 'itertools.count (n)' là một dãy số nguyên vô hạn, bắt đầu từ n, do đó, '(2 ** mục cho mục trong itertools.count (n))' sẽ là một chuỗi vô hạn các lũy thừa của ' 2' bắt đầu từ '2 ** n'. – Kevin

3

Đôi khi bạn có thể sử dụng chức năng tee từ itertools, nó trả về nhiều trình lặp cho cùng một trình tạo có thể được sử dụng độc lập.

40

Lợi ích của biểu thức trình tạo là nó sử dụng ít bộ nhớ hơn vì nó không xây dựng toàn bộ danh sách cùng một lúc. Biểu thức máy phát điện được sử dụng tốt nhất khi danh sách là một trung gian, chẳng hạn như tổng kết quả hoặc tạo ra một kết quả rõ ràng.

Ví dụ:

sum(x*2 for x in xrange(256)) 

dict(((k, some_func(k) for k in some_list_of_keys)) 

Ưu điểm có là danh sách không hoàn toàn được tạo ra, và do đó ít bộ nhớ được sử dụng (và cũng nên nhanh hơn)

Bạn nên, tuy nhiên, sử dụng danh sách hiểu khi sản phẩm cuối cùng mong muốn là một danh sách. Bạn sẽ không lưu bất kỳ memeory bằng cách sử dụng biểu thức máy phát điện, vì bạn muốn danh sách được tạo ra. Bạn cũng có được lợi ích của việc có thể sử dụng bất kỳ chức năng danh sách như sắp xếp hoặc đảo ngược.

Ví dụ:

reversed([x*2 for x in xrange(256)]) 
+9

Có một gợi ý đặt cho bạn ngay trong ngôn ngữ mà biểu thức máy phát điện có nghĩa là để được sử dụng theo cách đó. Mất dấu ngoặc! 'sum (x * 2 cho x trong xrange (256))' – u0b34a0f6ae

+3

Điều đó phải là 'dict (((k, some_func (k)) cho k trong some_list_of_keys)'. Tất nhiên cú pháp đọc/đọc dict 2.7+ thậm chí là –

+6

'sắp xếp' và' đảo ngược' hoạt động tốt trên bất kỳ biểu thức máy phát lặp lại nào được bao gồm – marr75

43

Điểm quan trọng là các danh sách hiểu biết tạo ra một danh sách mới. Máy phát tạo ra một đối tượng có thể lặp lại sẽ "lọc" tài liệu nguồn khi bạn sử dụng các bit.

Hãy tưởng tượng bạn có tệp nhật ký 2TB có tên "hugefile.txt" và bạn muốn nội dung và độ dài cho tất cả các dòng bắt đầu bằng từ "ENTRY".

Vì vậy, bạn hãy thử bắt đầu ra bằng cách viết một danh sách hiểu:

logfile = open("hugefile.txt","r") 
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")] 

này slurps lên toàn bộ tập tin, xử lý mỗi dòng, và lưu trữ các dòng tương ứng trong mảng của bạn. Do đó, mảng này có thể chứa tối đa 2TB nội dung. Đó là rất nhiều RAM, và có lẽ không thực tế cho mục đích của bạn.

Vì vậy, thay vào đó, chúng tôi có thể sử dụng trình tạo để áp dụng "bộ lọc" cho nội dung của chúng tôi. Không có dữ liệu nào thực sự được đọc cho đến khi chúng tôi bắt đầu lặp lại kết quả.

logfile = open("hugefile.txt","r") 
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY")) 

Thậm chí chưa có một dòng nào được đọc từ tệp của chúng tôi. Trên thực tế, giả sử chúng tôi muốn lọc kết quả của chúng tôi hơn nữa:

long_entries = ((line,length) for (line,length) in entry_lines if length > 80) 

Vẫn chưa đọc, nhưng bây giờ chúng tôi đã chỉ định hai máy phát sẽ thực hiện theo dữ liệu của chúng tôi.

Hãy viết ra dòng lọc của chúng tôi vào tập tin khác:

outfile = open("filtered.txt","a") 
for entry,length in long_entries: 
    outfile.write(entry) 

Bây giờ chúng ta đọc các tập tin đầu vào. Vì vòng lặp for của chúng tôi tiếp tục yêu cầu các dòng bổ sung, máy phát điện long_entries yêu cầu máy phát điện từ máy phát điện entry_lines, chỉ trả về những người có độ dài lớn hơn 80 ký tự. Và lần lượt, các dòng yêu cầu máy phát điện entry_lines (được lọc như được chỉ ra) từ trình lặp lặp logfile, lần lượt đọc tệp.

Vì vậy, thay vì "đẩy" dữ liệu vào hàm đầu ra của bạn dưới dạng danh sách được điền đầy đủ, bạn sẽ cho hàm đầu ra cách "kéo" dữ liệu khi cần. Đây là trường hợp của chúng tôi hiệu quả hơn nhiều, nhưng không hoàn toàn linh hoạt. Máy phát điện là một cách, một đường chuyền; dữ liệu từ tệp nhật ký mà chúng tôi đọc đã bị hủy ngay lập tức, vì vậy chúng tôi không thể quay lại dòng trước đó. Mặt khác, chúng ta không phải lo lắng về việc giữ dữ liệu xung quanh khi chúng ta hoàn thành nó.

4

Tôi đang sử dụng Hadoop Mincemeat module. Tôi nghĩ rằng đây là một ví dụ tuyệt vời để có một lưu ý của:

import mincemeat 

def mapfn(k,v): 
    for w in v: 
     yield 'sum',w 
     #yield 'count',1 


def reducefn(k,v): 
    r1=sum(v) 
    r2=len(v) 
    print r2 
    m=r1/r2 
    std=0 
    for i in range(r2): 
     std+=pow(abs(v[i]-m),2) 
    res=pow((std/r2),0.5) 
    return r1,r2,res 

Ở đây, máy phát điện được số ra của một tập tin văn bản (lớn như 15GB) và áp dụng toán học đơn giản trên những số liệu sử dụng Hadoop của bản đồ giảm. Nếu tôi không sử dụng chức năng lợi nhuận, nhưng thay vào đó là một sự hiểu biết danh sách, nó sẽ mất nhiều thời gian hơn để tính tổng và trung bình (chưa kể đến độ phức tạp của không gian).

Hadoop là một ví dụ tuyệt vời cho việc sử dụng tất cả các ưu điểm của Máy phát điện.

9

Khi tạo một máy phát điện từ một đối tượng có thể thay đổi (như một danh sách) lưu ý rằng các máy phát điện sẽ được đánh giá về tình trạng của danh sách tại thời điểm của việc sử dụng máy phát điện, không vào thời điểm của việc tạo ra các máy phát điện:

>>> mylist = ["a", "b", "c"] 
>>> gen = (elem + "1" for elem in mylist) 
>>> mylist.clear() 
>>> for x in gen: print (x) 
# nothing 

Nếu có bất kỳ cơ hội nào trong danh sách của bạn bị sửa đổi (hoặc đối tượng có thể thay đổi bên trong danh sách đó) nhưng bạn cần trạng thái khi tạo trình tạo, bạn cần sử dụng tính năng đọc danh sách.