Sự khác biệt giữa Trình tạo và Trình lặp của Python


538

Sự khác biệt giữa các trình vòng lặp và máy phát điện là gì? Một số ví dụ khi bạn sử dụng mỗi trường hợp sẽ hữu ích.

Câu trả lời:


543

iteratorlà một khái niệm tổng quát hơn: bất kỳ đối tượng nào có lớp có một nextphương thức ( __next__trong Python 3) và một __iter__phương thức thực hiện return self.

Mỗi trình tạo là một trình vòng lặp, nhưng không phải ngược lại. Một trình tạo được xây dựng bằng cách gọi một hàm có một hoặc nhiều yieldbiểu thức (các yieldcâu lệnh, trong Python 2.5 trở về trước) và là một đối tượng đáp ứng định nghĩa của đoạn trước đó về một iterator.

Bạn có thể muốn sử dụng một trình lặp tùy chỉnh, thay vì trình tạo, khi bạn cần một lớp có hành vi duy trì trạng thái hơi phức tạp hoặc muốn đưa ra các phương thức khác bên cạnh next( __iter____init__). Thông thường, một trình tạo (đôi khi, cho các nhu cầu đủ đơn giản, biểu thức của trình tạo ) là đủ và mã đơn giản hơn vì bảo trì trạng thái (trong giới hạn hợp lý) về cơ bản là "được thực hiện cho bạn" bởi khung bị treo và tiếp tục.

Ví dụ: một trình tạo như:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)

hoặc biểu thức trình tạo tương đương (genapi)

generator = (i*i for i in range(a, b))

sẽ lấy thêm mã để xây dựng như một trình vòng lặp tùy chỉnh:

class Squares(object):
    def __init__(self, start, stop):
       self.start = start
       self.stop = stop
    def __iter__(self): return self
    def next(self): # __next__ in Python 3
       if self.start >= self.stop:
           raise StopIteration
       current = self.start * self.start
       self.start += 1
       return current

iterator = Squares(a, b)

Nhưng, tất nhiên, với lớp Squaresbạn có thể dễ dàng cung cấp các phương thức bổ sung, tức là

    def current(self):
       return self.start

nếu bạn có bất kỳ nhu cầu thực tế nào cho chức năng bổ sung như vậy trong ứng dụng của bạn.


Làm thế nào để tôi sử dụng iterator, một khi tôi đã tạo ra nó?
Vincenzooo

@Vincenzooo phụ thuộc vào những gì bạn muốn làm với nó. Nó sẽ là một phần của for ... in ...:, được chuyển đến một chức năng hoặc bạn sẽ gọiiter.next()
Caleth

@Caleth Tôi đã hỏi về cú pháp chính xác, vì tôi đã gặp lỗi khi cố gắng sử dụng for..incú pháp. Có lẽ tôi đã bỏ lỡ điều gì đó, nhưng đó là một thời gian trước đây, tôi không nhớ nếu tôi đã giải quyết. Cảm ơn bạn!
Vincenzooo

136

Sự khác biệt giữa các trình vòng lặp và máy phát điện là gì? Một số ví dụ khi bạn sử dụng mỗi trường hợp sẽ hữu ích.

Tóm lại: Iterators là các đối tượng có phương thức __iter____next__( nexttrong Python 2). Trình tạo cung cấp một cách dễ dàng, tích hợp để tạo các phiên bản của Iterators.

Một hàm có năng suất trong nó vẫn là một hàm, khi được gọi, trả về một thể hiện của một đối tượng trình tạo:

def a_function():
    "when called, returns generator object"
    yield

Một biểu thức trình tạo cũng trả về một trình tạo:

a_generator = (i for i in range(0))

Đối với một ví dụ sâu hơn và ví dụ, tiếp tục đọc.

Máy phát điện một Iterator

Cụ thể, trình tạo là một kiểu con của iterator.

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

Chúng ta có thể tạo ra một số cách. Một cách rất phổ biến và đơn giản để làm như vậy là với một hàm.

Cụ thể, một hàm có năng suất trong nó là một hàm, khi được gọi sẽ trả về một trình tạo:

>>> def a_function():
        "just a function definition with yield in it"
        yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function()  # when called
>>> type(a_generator)           # returns a generator
<class 'generator'>

Và một máy phát điện, một lần nữa, là một Iterator:

>>> isinstance(a_generator, collections.Iterator)
True

Một Iterator một Iterable

Một Iterator là một Iterable,

>>> issubclass(collections.Iterator, collections.Iterable)
True

trong đó yêu cầu một __iter__phương thức trả về Iterator:

>>> collections.Iterable()
Traceback (most recent call last):
  File "<pyshell#79>", line 1, in <module>
    collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__

Một số ví dụ về iterables là các bộ dữ liệu, danh sách, từ điển, bộ, bộ đóng băng, chuỗi, chuỗi byte, mảng byte, phạm vi và bộ nhớ:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

Trình vòng lặp yêu cầu một nexthoặc __next__phương thức

Trong Python 2:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<pyshell#80>", line 1, in <module>
    collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next

Và trong Python 3:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__

Chúng ta có thể lấy các trình vòng lặp từ các đối tượng tích hợp (hoặc các đối tượng tùy chỉnh) với iterchức năng:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

Các __iter__phương pháp được gọi là khi bạn cố gắng sử dụng một đối tượng với một vòng lặp for. Sau đó, __next__phương thức được gọi trên đối tượng iterator để lấy từng mục ra cho vòng lặp. Trình lặp tăng lên StopIterationkhi bạn đã sử dụng hết nó và nó không thể được sử dụng lại tại thời điểm đó.

Từ tài liệu

Từ phần Kiểu máy phát điện của phần Kiểu lặp trong tài liệu Kiểu tích hợp :

Các trình tạo của Python cung cấp một cách thuận tiện để thực hiện giao thức iterator. Nếu __iter__()phương thức của đối tượng chứa được triển khai như một trình tạo, nó sẽ tự động trả về một đối tượng lặp (về mặt kỹ thuật, một đối tượng trình tạo) cung cấp các phương thức __iter__()next()[ __next__()trong Python 3]. Thông tin thêm về máy phát điện có thể được tìm thấy trong tài liệu cho biểu thức năng suất.

(Nhấn mạnh thêm.)

Vì vậy, từ đó chúng ta biết rằng Generators là một loại Iterator (tiện lợi).

Ví dụ đối tượng lặp

Bạn có thể tạo đối tượng thực hiện giao thức Iterator bằng cách tạo hoặc mở rộng đối tượng của riêng bạn.

class Yes(collections.Iterator):

    def __init__(self, stop):
        self.x = 0
        self.stop = stop

    def __iter__(self):
        return self

    def next(self):
        if self.x < self.stop:
            self.x += 1
            return 'yes'
        else:
            # Iterators must raise when done, else considered broken
            raise StopIteration

    __next__ = next # Python 3 compatibility

Nhưng đơn giản hơn là sử dụng Trình tạo để thực hiện việc này:

def yes(stop):
    for _ in range(stop):
        yield 'yes'

Hoặc có lẽ đơn giản hơn, một Expression Expression (hoạt động tương tự như việc hiểu danh sách):

yes_expr = ('yes' for _ in range(stop))

Tất cả đều có thể được sử dụng theo cùng một cách:

>>> stop = 4             
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), 
                             ('yes' for _ in range(stop))):
...     print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...     
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes

Phần kết luận

Bạn có thể sử dụng giao thức Iterator trực tiếp khi bạn cần mở rộng một đối tượng Python như một đối tượng có thể được lặp lại.

Tuy nhiên, trong phần lớn các trường hợp, bạn phù hợp nhất để sử dụng yieldđể xác định hàm trả về Trình tạo vòng lặp hoặc xem xét Biểu thức trình tạo.

Cuối cùng, lưu ý rằng máy phát điện cung cấp nhiều chức năng hơn như coroutines. Tôi giải thích về Máy phát điện, cùng với yieldtuyên bố, chuyên sâu về câu trả lời của tôi về "Từ khóa năng suất trực tuyến làm gì?".


41

Lặp lại:

Iterator là các đối tượng sử dụng next()phương thức để nhận giá trị tiếp theo của chuỗi.

Máy phát điện:

Trình tạo là một hàm tạo hoặc tạo ra một chuỗi các giá trị bằng yieldphương thức.

Mỗi next()phương thức gọi đối tượng trình tạo (ví dụ fnhư trong ví dụ dưới đây) được trả về bởi hàm trình tạo (ví dụ: foo()hàm trong ví dụ bên dưới), tạo ra giá trị tiếp theo theo thứ tự.

Khi một hàm tạo được gọi, nó trả về một đối tượng trình tạo mà không bắt đầu thực hiện hàm. Khi next()phương thức được gọi lần đầu tiên, hàm bắt đầu thực thi cho đến khi đạt được câu lệnh năng suất trả về giá trị mang lại. Sản lượng theo dõi tức là ghi nhớ thực hiện cuối cùng. Và next()cuộc gọi thứ hai tiếp tục từ giá trị trước đó.

Ví dụ sau đây cho thấy sự tương tác giữa năng suất và gọi phương thức tiếp theo trên đối tượng trình tạo.

>>> def foo():
...     print "begin"
...     for i in range(3):
...         print "before yield", i
...         yield i
...         print "after yield", i
...     print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0            # Control is in for loop
0
>>> f.next()
after yield 0             
before yield 1            # Continue for loop
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

3
Chỉ cần năng suất của FYI không phải là phương pháp, đó là từ khóa
Jay Parikh

25

Thêm một câu trả lời vì không có câu trả lời nào hiện có giải quyết cụ thể sự nhầm lẫn trong tài liệu chính thức.

Các hàm tạo là các hàm thông thường được định nghĩa bằng cách sử dụngyieldthay vìreturn. Khi được gọi, một hàm tạo sẽ trả về một đối tượng trình tạo , đó là một loại trình vòng lặp - nó có mộtnext()phương thức. Khi bạn gọinext(), giá trị tiếp theo mang lại bởi hàm tạo được trả về.

Hàm hoặc đối tượng có thể được gọi là "trình tạo" tùy thuộc vào tài liệu nguồn Python nào bạn đọc. Các Python thuật ngữ nói chức năng máy phát điện, trong khi Python wiki ngụ ý đối tượng máy phát điện. Các Python hướng dẫn rõ rệt quản lý để bao hàm cả tập quán trong khoảng thời gian ba câu:

Trình tạo là một công cụ đơn giản và mạnh mẽ để tạo các trình vòng lặp. Chúng được viết như các hàm thông thường nhưng sử dụng câu lệnh lợi suất bất cứ khi nào chúng muốn trả về dữ liệu. Mỗi lần tiếp theo () được gọi trên nó, trình tạo lại tiếp tục ở nơi nó rời đi (nó nhớ tất cả các giá trị dữ liệu và câu lệnh nào được thực hiện lần cuối).

Hai câu đầu xác định trình tạo với các hàm tạo, trong khi câu thứ ba xác định chúng với các đối tượng trình tạo.

Bất chấp tất cả sự nhầm lẫn này, người ta có thể tìm kiếm tài liệu tham khảo ngôn ngữ Python cho từ rõ ràng và cuối cùng:

Biểu thức năng suất chỉ được sử dụng khi xác định hàm tạo và chỉ có thể được sử dụng trong phần thân của định nghĩa hàm. Sử dụng biểu thức năng suất trong định nghĩa hàm là đủ để khiến định nghĩa đó tạo ra hàm tạo thay vì hàm bình thường.

Khi một hàm tạo được gọi, nó trả về một iterator được gọi là một trình tạo. Máy phát đó sau đó kiểm soát việc thực hiện chức năng của máy phát.

Vì vậy, trong cách sử dụng chính thức và chính xác, "trình tạo" không đủ tiêu chuẩn có nghĩa là đối tượng trình tạo, không phải là hàm tạo.

Các tham chiếu trên dành cho Python 2 nhưng tham chiếu ngôn ngữ Python 3 nói điều tương tự. Tuy nhiên, thuật ngữ Python 3 nói rằng

trình tạo ... Thường đề cập đến chức năng của trình tạo, nhưng có thể đề cập đến trình lặp của trình tạo trong một số ngữ cảnh. Trong trường hợp ý nghĩa dự định không rõ ràng, sử dụng thuật ngữ đầy đủ sẽ tránh sự mơ hồ.


Tôi không nghĩ có nhiều nhầm lẫn giữa các hàm tạo và các đối tượng của trình tạo, vì cùng một lý do thường không có sự nhầm lẫn giữa các lớp và các thể hiện của chúng. Trong cả hai trường hợp, bạn gọi một cái để lấy cái kia và trong cuộc trò chuyện thông thường (hoặc tài liệu được viết nhanh), bạn có thể sử dụng tên lớp hoặc từ "trình tạo" cho một trong hai. Bạn chỉ cần rõ ràng về "chức năng của trình tạo" so với "đối tượng trình tạo" trong các tình huống hiếm gặp mà bạn đang nói về vấn đề gì.
Blckknght

6
1. Bất kể lý do lý thuyết tại sao không nên nhầm lẫn, nhận xét về các câu trả lời khác cho câu hỏi này từ chối và mâu thuẫn với nhau mà không giải quyết, cho thấy sự nhầm lẫn thực sự tồn tại. 2. Sự không chính xác thông thường là tốt nhưng một nguồn chính xác, có thẩm quyền ít nhất phải là một trong những lựa chọn về SO. Tôi sử dụng cả chức năng và đối tượng của trình tạo rộng rãi trong dự án hiện tại của mình và sự khác biệt rất quan trọng khi thiết kế và mã hóa. Thật tốt khi biết sử dụng thuật ngữ nào bây giờ, vì vậy tôi không phải thay đổi hàng tá tên và nhận xét sau này.
Paul

2
Hãy tưởng tượng một tài liệu toán học trong đó không có sự phân biệt nào được thực hiện giữa một hàm và giá trị trả về của nó. Thỉnh thoảng rất thuận tiện để giới thiệu chúng một cách không chính thức, nhưng nó làm tăng nguy cơ mắc nhiều lỗi khác nhau. Toán học hiện đại tiên tiến sẽ bị cản trở đáng kể và không cần thiết nếu sự khác biệt không được chính thức hóa trong quy ước, ngôn ngữ và ký hiệu.
Paul

2
Các hàm bậc cao đi qua các máy phát hoặc các hàm tạo có thể nghe có vẻ lạ, nhưng đối với tôi chúng đã được đưa ra. Tôi đang làm việc trong Apache Spark và nó thực thi một phong cách lập trình rất chức năng. Các chức năng phải tạo, truyền vào và phát ra tất cả các loại đối tượng để hoàn thành công việc. Tôi đã có một số tình huống mà tôi đã mất theo dõi loại "máy phát điện" mà tôi đang làm việc. Gợi ý trong tên biến và nhận xét, sử dụng thuật ngữ nhất quán và chính xác, đã giúp làm sáng tỏ sự nhầm lẫn. Sự tối nghĩa của một Pythonist có thể là trung tâm của thiết kế dự án của người khác!
Paul

1
@Paul, cảm ơn vì đã viết câu trả lời này. Sự nhầm lẫn này rất quan trọng vì sự khác biệt giữa đối tượng trình tạo và hàm tạo là sự khác biệt giữa nhận hành vi mong muốn và phải tra cứu trình tạo.
blujay

15

Mọi người đều có một câu trả lời thực sự hay và dài dòng với các ví dụ và tôi thực sự đánh giá cao nó. Tôi chỉ muốn đưa ra một vài câu trả lời ngắn gọn cho những người vẫn chưa hoàn toàn rõ ràng về mặt khái niệm:

Nếu bạn tạo iterator của riêng mình, nó có một chút liên quan - bạn phải tạo một lớp và ít nhất là thực hiện iter và các phương thức tiếp theo. Nhưng điều gì sẽ xảy ra nếu bạn không muốn trải qua rắc rối này và muốn nhanh chóng tạo ra một trình vòng lặp. May mắn thay, Python cung cấp một cách rút gọn để xác định một trình vòng lặp. Tất cả những gì bạn cần làm là xác định một hàm có ít nhất 1 lệnh gọi và bây giờ khi bạn gọi hàm đó, nó sẽ trả về " một cái gì đó " sẽ hoạt động như một trình vòng lặp (bạn có thể gọi phương thức tiếp theo và sử dụng nó trong một vòng lặp for). Đây một cái gì đó có một cái tên bằng Python gọi Generator

Hy vọng rằng làm rõ một chút.


10

Các câu trả lời trước đã bỏ qua phần bổ sung này: một trình tạo có một closephương thức, trong khi các trình vòng lặp điển hình thì không. Các closetrigger phương pháp một StopIterationngoại lệ trong các máy phát điện, có thể được bắt gặp trong một finallyđiều khoản trong iterator rằng, để có được một cơ hội để chạy một số dọn dẹp. Sự trừu tượng hóa này làm cho nó có thể sử dụng nhiều nhất trong các vòng lặp lớn hơn đơn giản. Người ta có thể đóng một trình tạo vì người ta có thể đóng một tệp mà không phải bận tâm về những gì bên dưới.

Điều đó nói rằng, câu trả lời cá nhân của tôi cho câu hỏi đầu tiên sẽ là: iterizable chỉ có một __iter__phương thức, các trình vòng lặp thông thường chỉ có một __next__phương thức, các trình tạo có cả __iter__một __next__và một bổ sung close.

Đối với câu hỏi thứ hai, câu trả lời cá nhân của tôi sẽ là: trong giao diện công cộng, tôi có xu hướng thích máy phát điện hơn, vì nó có khả năng phục hồi tốt hơn: closephương pháp có khả năng kết hợp tốt hơn yield from. Tại địa phương, tôi có thể sử dụng các trình vòng lặp, nhưng chỉ khi đó là một cấu trúc đơn giản và phẳng (các trình vòng lặp không dễ sáng tác) và nếu có lý do để tin rằng chuỗi này khá ngắn đặc biệt là nếu nó có thể bị dừng trước khi nó kết thúc. Tôi có xu hướng xem các trình vòng lặp như một nguyên thủy cấp thấp, ngoại trừ theo nghĩa đen.

Đối với các vấn đề dòng điều khiển, máy phát điện là một khái niệm quan trọng như lời hứa: cả hai đều trừu tượng và có thể ghép lại được.


Bạn có thể đưa ra một ví dụ để minh họa ý bạn khi nói về sáng tác? Ngoài ra, bạn có thể giải thích những gì bạn có trong đầu khi nói về " các trình vòng lặp điển hình " không?
bli

1
Một câu trả lời khác ( stackoverflow.com/a/28353158/1878788 ) nói rằng "một trình vòng lặp là một vòng lặp". Vì một iterable có một __iter__phương thức, tại sao một iterator chỉ có thể có __next__? Nếu chúng được cho là lặp đi lặp lại, tôi sẽ mong chúng cũng nhất thiết phải có __iter__.
bli

1
@bli: AFAICS câu trả lời này ở đây đề cập đến PEP234 tiêu chuẩn , vì vậy nó là chính xác, trong khi câu trả lời khác đề cập đến một số thực hiện, vì vậy nó là nghi vấn. Tiêu chuẩn chỉ yêu cầu một __iter__iterables để trả về một iterator, chỉ yêu cầu một nextphương thức ( __next__trong Python3). Xin đừng nhầm lẫn các tiêu chuẩn (đối với việc gõ vịt) với cách triển khai của chúng (cách một trình thông dịch Python cụ thể thực hiện nó). Điều này hơi giống với sự nhầm lẫn giữa các hàm tạo (định nghĩa) và các đối tượng của trình tạo (thực hiện). ;)
Tino

7

Chức năng máy phát điện, đối tượng máy phát điện, máy phát điện:

Hàm Generator giống như một hàm thông thường trong Python nhưng nó chứa một hoặc nhiều yieldcâu lệnh. Các hàm tạo là một công cụ tuyệt vời để tạo các đối tượng Iterator dễ dàng nhất có thể. Các Iterator đối tượng returend bởi chức năng máy phát điện còn được gọi là Generator đối tượng hoặc Generator .

Trong ví dụ này tôi đã tạo ra một hàm Generator trả về một đối tượng Generator <generator object fib at 0x01342480>. Cũng giống như các trình vòng lặp khác, các đối tượng Trình tạo có thể được sử dụng trong một forvòng lặp hoặc với hàm tích next()hợp trả về giá trị tiếp theo từ trình tạo.

def fib(max):
    a, b = 0, 1
    for i in range(max):
        yield a
        a, b = b, a + b
print(fib(10))             #<generator object fib at 0x01342480>

for i in fib(10):
    print(i)               # 0 1 1 2 3 5 8 13 21 34


print(next(myfib))         #0
print(next(myfib))         #1
print(next(myfib))         #1
print(next(myfib))         #2

Vì vậy, một hàm tạo là cách dễ nhất để tạo một đối tượng Iterator.

Lặp lại :

Mỗi đối tượng máy phát là một trình vòng lặp nhưng không phải ngược lại. Một đối tượng iterator tùy chỉnh có thể được tạo nếu lớp của nó thực hiện __iter____next__phương thức (còn được gọi là giao thức iterator).

Tuy nhiên, việc sử dụng chức năng trình tạo để tạo vòng lặp dễ dàng hơn nhiều vì chúng đơn giản hóa việc tạo của chúng, nhưng Trình lặp tùy chỉnh cho phép bạn tự do hơn và bạn cũng có thể thực hiện các phương thức khác theo yêu cầu của mình như trong ví dụ dưới đây.

class Fib:
    def __init__(self,max):
        self.current=0
        self.next=1
        self.max=max
        self.count=0

    def __iter__(self):
        return self

    def __next__(self):
        if self.count>self.max:
            raise StopIteration
        else:
            self.current,self.next=self.next,(self.current+self.next)
            self.count+=1
            return self.next-self.current

    def __str__(self):
        return "Generator object"

itobj=Fib(4)
print(itobj)               #Generator object

for i in Fib(4):  
    print(i)               #0 1 1 2

print(next(itobj))         #0
print(next(itobj))         #1
print(next(itobj))         #1

6

Ví dụ từ Ned Batchelder rất được khuyến nghị cho các trình vòng lặp và trình tạo

Một phương thức không có trình tạo mà làm một số thứ chẵn

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them

trong khi bằng cách sử dụng một máy phát điện

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
  • Chúng tôi không cần bất kỳ danh sách cũng như returntuyên bố
  • Hiệu quả cho luồng có chiều dài lớn / vô hạn ... nó chỉ đi và mang lại giá trị

Gọi evensphương thức (trình tạo) là như bình thường

num = [...]
for n in evens(num):
   do_smth(n)
  • Máy phát điện cũng được sử dụng để phá vỡ vòng lặp kép

Lặp lại

Một cuốn sách đầy các trang là một lần lặp , Một dấu trang là một trình vòng lặp

và dấu trang này không có gì để làm ngoài việc di chuyển next

litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration  (Exception) as we got end of the iterator

Để sử dụng Trình tạo ... chúng ta cần một hàm

Để sử dụng Iterator ... chúng ta cần nextiter

Như đã nói:

Hàm Generator trả về một đối tượng lặp

Toàn bộ lợi ích của Iterator:

Lưu trữ một yếu tố một lần trong bộ nhớ


Về đoạn mã đầu tiên của bạn, tôi muốn biết những gì khác 'luồng' có thể hơn danh sách []?
Iqra.

5

Bạn có thể so sánh cả hai cách tiếp cận cho cùng một dữ liệu:

def myGeneratorList(n):
    for i in range(n):
        yield i

def myIterableList(n):
    ll = n*[None]
    for i in range(n):
        ll[i] = i
    return ll

# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
    print("{} {}".format(i1, i2))

# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

Ngoài ra, nếu bạn kiểm tra dấu chân bộ nhớ, trình tạo sẽ chiếm ít bộ nhớ hơn vì không cần lưu trữ tất cả các giá trị trong bộ nhớ cùng một lúc.


1

Tôi đang viết riêng cho người mới sử dụng Python theo một cách rất đơn giản, mặc dù sâu bên dưới Python làm rất nhiều việc.

Hãy bắt đầu với những điều rất cơ bản:

Hãy xem xét một danh sách,

l = [1,2,3]

Hãy viết một hàm tương đương:

def f():
    return [1,2,3]

o / p của print(l): [1,2,3]& o / p củaprint(f()) : [1,2,3]

Hãy tạo danh sách l iterable: Trong danh sách python luôn có thể lặp lại có nghĩa là bạn có thể áp dụng iterator bất cứ khi nào bạn muốn.

Hãy áp dụng iterator trong danh sách:

iter_l = iter(l) # iterator applied explicitly

Hãy tạo một hàm lặp, tức là viết một hàm tạo tương đương. Trong python ngay khi bạn giới thiệu từ khóa yield; nó trở thành một hàm tạo và iterator sẽ được áp dụng hoàn toàn.

Lưu ý: Mọi trình tạo luôn luôn có thể lặp lại với trình lặp lặp ẩn được áp dụng và ở đây trình lặp ẩn là mấu chốt Vì vậy, hàm tạo sẽ là:

def f():
  yield 1 
  yield 2
  yield 3

iter_f = f() # which is iter(f) as iterator is already applied implicitly

Vì vậy, nếu bạn đã quan sát, ngay khi bạn thực hiện chức năng tạo fa, nó đã được lặp lại (f)

Hiện nay,

l là danh sách, sau khi áp dụng phương thức iterator "iter", nó trở thành, iter (l)

f đã là iter (f), sau khi áp dụng phương thức iterator "iter", nó trở thành, iter (iter (f)), một lần nữa là iter (f)

Thật là bạn đang truyền int cho int (x) đã là int và nó sẽ vẫn là int (x).

Ví dụ: o / p của:

print(type(iter(iter(l))))

<class 'list_iterator'>

Không bao giờ quên đây là Python và không phải C hay C ++

Do đó, kết luận từ lời giải thích trên là:

danh sách l ~ = iter (l)

Hàm tạo f == iter (f)

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.