2013-08-02 29 views
6

Tôi có hai triển khai những tính toán chiều dài của một máy phát điện hữu hạn, trong khi vẫn giữ các dữ liệu để chế biến tiếp:Chiều dài của một máy phát điện hữu hạn

def count_generator1(generator): 
    '''- build a list with the generator data 
     - get the length of the data 
     - return both the length and the original data (in a list) 
     WARNING: the memory use is unbounded, and infinite generators will block this''' 
    l = list(generator) 
    return len(l), l 

def count_generator2(generator): 
    '''- get two generators from the original generator 
     - get the length of the data from one of them 
     - return both the length and the original data, as returned by tee 
     WARNING: tee can use up an unbounded amount of memory, and infinite generators will block this''' 
    for_length, saved = itertools.tee(generator, 2) 
    return sum(1 for _ in for_length), saved 

Cả hai đều có những hạn chế, cả hai đều thực hiện công việc. Ai đó có thể bình luận về họ, hoặc thậm chí cung cấp một lựa chọn tốt hơn?

+3

Không có cách nào để biết độ dài của một trình tạo lặp lại mà không tốn toàn bộ điều. –

+0

Tôi biết. Đó không phải là câu hỏi – dangonfast

+2

lưu ý: nếu nếu bạn không cần độ dài chính xác thì bạn có thể sử dụng ['operator.length_hint()' (Python 3.4+)] (http://docs.python.org/dev/library /operator#operator.length_hint) trả về độ dài ước tính mà không cần dùng trình lặp. Xem [PEP 424 - Phương pháp hiển thị gợi ý độ dài] (http://www.python.org/dev/peps/pep-0424/) – jfs

Trả lời

11

Nếu bạn phải làm điều này, phương pháp đầu tiên tốt hơn nhiều - khi bạn tiêu thụ tất cả các giá trị, itertools.tee() sẽ phải lưu trữ tất cả các giá trị, nghĩa là danh sách sẽ hiệu quả hơn.

Để trích dẫn từ the docs:

itertool này có thể yêu cầu lưu trữ phụ đáng kể (tùy thuộc vào bao nhiêu dữ liệu tạm thời cần phải được lưu trữ). Nói chung, nếu một bộ chuyển đổi sử dụng hầu hết hoặc tất cả dữ liệu trước khi một trình lặp khác bắt đầu, thì nhanh hơn để sử dụng danh sách() thay vì tee().

+0

Vâng, trong cả hai trường hợp, tôi tiêu thụ máy phát và lưu trữ toàn bộ dữ liệu. Đầu tiên, bằng cách tạo ra một 'danh sách', thứ hai chỉ vì' tee' phải làm tương tự (hoặc một điều tương tự). Tôi nghĩ rằng nhận được chiều dài của danh sách là nhanh hơn (đã là một phần của đối tượng danh sách?), Đó là lý do tại sao tôi có xu hướng thích phương pháp đầu tiên. Từ quan điểm của bộ nhớ tiêu thụ, cả hai có vẻ tương đương, phải không? – dangonfast

+0

@gonvaled Việc sử dụng bộ nhớ có thể sẽ giống nhau, nhưng khi tôi trích dẫn từ tài liệu, việc tạo danh sách sẽ nhanh hơn. –

+0

ok, đó cũng là ấn tượng của tôi. Cảm ơn. – dangonfast

2

Tôi chạy Windows 64-bit Python 3.4.3 timeit trên một vài phương pháp tôi có thể nghĩ ra:

>>> from timeit import timeit 
>>> from textwrap import dedent as d 
>>> timeit(
...  d(""" 
...  count = -1 
...  for _ in s: 
...   count += 1 
...  count += 1 
...  """), 
...  "s = range(1000)", 
...) 
50.70772041983173 
>>> timeit(
...  d(""" 
...  count = -1 
...  for count, _ in enumerate(s): 
...   pass 
...  count += 1 
...  """), 
...  "s = range(1000)", 
...) 
42.636973504498656 
>>> timeit(
...  d(""" 
...  count, _ = reduce(f, enumerate(range(1000)), (-1, -1)) 
...  count += 1 
...  """), 
...  d(""" 
...  from functools import reduce 
...  def f(_, count): 
...   return count 
...  s = range(1000) 
...  """), 
...) 
121.15513102540672 
>>> timeit("count = sum(1 for _ in s)", "s = range(1000)") 
58.179126025925825 
>>> timeit("count = len(tuple(s))", "s = range(1000)") 
19.777029680237774 
>>> timeit("count = len(list(s))", "s = range(1000)") 
18.145157531932 
>>> timeit("count = len(list(1 for _ in s))", "s = range(1000)") 
57.41422175998332 

Shockingly, cách tiếp cận nhanh nhất là sử dụng một list (thậm chí không phải là một tuple) để xả iterator và nhận độ dài từ đó:

>>> timeit("count = len(list(s))", "s = range(1000)") 
18.145157531932 

Tất nhiên, điều này có nguy cơ các vấn đề bộ nhớ. Cách thay thế bộ nhớ thấp nhất là sử dụng liệt kê trên NOOP for-vòng:

>>> timeit(
...  d(""" 
...  count = -1 
...  for count, _ in enumerate(s): 
...   pass 
...  count += 1 
...  """), 
...  "s = range(1000)", 
...) 
42.636973504498656 

Chúc mừng!