Python – Iterator và Generator

4.8/5 - (26 votes)

Trong phần này chúng ta sẽ tìm hiểu về Iterator và Generator.

Iterator là một khái niệm chỉ các lớp dạng danh sách cho phép chúng ta duyệt qua chúng mà không cần quan tâm đến kiểu dữ liệu đó là gì.

Để một lớp được gọi là Iterator thì lớp đó phải hỗ trợ 2 phương thức là __iter__()__next__(). Cả 2 phương thức đó được gọi chung là giao thức iterator (Iterator Protocol). Trong đó phương thức __iter__() chỉ đơn giản là trả về đối tượng tham chiếu tới chính nó, còn phương thức __next__() trả về phần tử tiếp theo có trong danh sách cho đến phần tử cuối cùng thì giải phóng exception StopIteration.

Hầu hết các kiểu dữ liệu dạng danh sách trong Python như kiểu list, tuple, string, dictionary đều hỗ trợ giao thức Iterator.

str = "formidable"
it = iter(str)

print (it.__next__())
print (it.__next__())
print (it.__next__())

print (list(it))

Trong đoạn code trên, chúng ta sử dụng tính tăng iterator của một string. Hàm iter() sẽ gọi đến phương thức __iter__() có sẵn trong lớp string để lấy về đối tượng tham chiếu đến chính nó. Phương thức __next__() sẽ trả về phần tử tiếp theo trong danh sách. Ngoài ra bạn còn có thể dùng các hàm như list(), tuple() để tạo các đối tượng danh sách mới dựa trên một iterator.

f
o
r
['m', 'i', 'd', 'a', 'b', 'l', 'e']

Trong ví dụ dưới đây, chúng ta sẽ viết một lớp riêng có hỗ trợ giao thức Iterator.

class seq:
   def __init__(self):
      self.x = 0

   def __next__(self):
      self.x += 1
      return self.x**self.x

   def __iter__(self):
      return self


s = seq()
n = 0

for i in s:
   print (i)
   n += 1
   if n > 10:
      break

Trong đoạn code trên, chúng ta tạo một dãy số dài vô tận. Khi chúng ta sử dụng vòng lặp for, vòng lặp sẽ tự động gọi hàm iter() lên đối tượng của chúng ta để lấy về một đối tượng tham chiếu tới chính nó, và cứ mỗi lần lặp nó lại gọi đến phương thức __next__() đế lấy phần tử tiếp theo trong dãy.

def __next__(self):
    self.x += 1
    return self.x**self.x

Phương thức __next__() trả về phần tử tiếp theo trong dãy.

 def __iter__(self):
    return self

Phương thức __iter__ trả về đối tượng tham chiếu đến chính nó (hay còn gọi là đối tượng iterator).

 if n > 10:
      break

Do chúng ta tạo một danh sách dài vô tận nên phải có câu lệnh để ngắt vòng lặp.

1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611

Trong ví dụ dưới đây, chúng ta sẽ ngắt vòng lặp bằng cách giải phóng exception StopIteration.

class seq14:
   def __init__(self):
      self.x = 0

   def __next__(self):
      self.x += 1
      if self.x > 14:
         raise StopIteration
      return self.x**self.x

   def __iter__(self):
      return self


s = seq14()

for i in s:
   print (i)

Trong đoạn code trên chúng ta in ra 14 số đầu tiên trong dãy.

if self.x > 14:
    raise StopIteration

StopIteration sẽ ngắt vòng lặp.

Generator

Generator chẳng qua cũng chỉ là Iterator, chúng cũng tạo ra một đối tượng kiểu danh sách, nhưng bạn chỉ có thể duyệt qua các phần tử của generator một lần duy nhất vì generator không lưu dữ liệu trong bộ nhớ mà cứ mỗi lần lặp thì chúng sẽ tạo phần tử tiếp theo trong dãy và trả về phần tử đó.

Đoạn code dưới đây ví dụ cách định nghĩa một generator.

def gen():
  x, y = 1, 2
  yield x, y
  x += 1
  yield x, y


it = gen()

print (it.__next__())
print (it.__next__())

try:
  print (it.__next__())
except StopIteration:
  print ("Iteration finished")

Để định nghĩa một generator thì chúng ta dùng từ khóa def giống như khi định nghĩa một hàm. Trong generator chúng ta dùng từ khóa yield để trả về các phần tử.

Từ khóa yield có chức năng y hệt như từ khóa return, chỉ khác ở chỗ là khi bạn gọi phương thức __next__() lần thứ nhất thì generator thực hiện các công việc tính toán giá trị rồi gặp từ khóa yield nào thì nó sẽ trả về các phần tử tại ví trí đó, khi bạn gọi phương thức __next__() lần thứ hai thì generator không bắt đầu chạy tại vị trí đầu tiên mà bắt đầu ngay phía sau từ khóa yield thứ nhất. Cứ như thế generator tạo ra các phần tử trong dãy, cho đến khi không còn gặp từ khóa yield nào nữa thì giải phóng exception StopIteration.

(1, 2)
(2, 2)
Iteration finished

 

0 0 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Inline Feedbacks
View all comments
Nguyen Cuong
Nguyen Cuong
6 năm trước

Mình không hiểu vòng lặp for i in s. liệu nó có phải là một comprehension của python không? Mong ad giúp mình!