images
25/09/2020 06:47 am

Python với Iterator và Generator

Iterator là kiểu tổ chức dữ liệu trong Python cho phép bạn làm việc với một danh sách theo thứ tự tuần tự. Còn Generator được dùng cho nhiều mục đích. Điển hình là khi bạn làm việc với danh sách dữ liệu rất lớn mà muốn tiết kiệm bộ nhớ.

Iterator


Iterator là kiểu tổ chức dữ liệu trong Python cho phép bạn làm việc với một danh sách theo thứ tự tuần tự. Dưới đây là ví dụ đơn giản về Iterator:


Bạn tạo ra iterator thông qua iter()


>>> it = iter([1,2,3,4,5,6])

>>> it

<list_iterator object at 0x1062e3fd0>


Khi bạn tạo ra iterator và duyệt qua các phần tử, bạn dùng method next(). Mỗi khi method này được gọi, các phần từ lần lượt được duyệt qua theo thứ tự:


>>> next(it)

1

>>> next(it)

2

>>> it.__next__()

3


Khi bạn gọi next(it), bản chất Python sẽ gọi tới method __next__() của iterator.


Khi duyệt phần tử trong iterator, bạn không thể kiểm tra xem đã kết thúc việc duyệt hay chưa, giống như trong Java vốn có method là hasNext.

Với Python, khi đến phần tử cuối cùng, bạn nhận được exception sau:


>>> it.__next__()

6

>>> it.__next__()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

StopIteration


Do đó để biết được đã kết thúc việc duyệt, bạn có thể dùng try-except với StopIteration:


try:

  it.next()

except StopIteration:

  print(“it ends”)


Trong lập trình, đôi khi bạn tạo ra các object và tổ chức dữ liệu theo kiểu riêng của mình. Giả sử object của bạn có chứa mảng các phần tử. Để duyệt qua các phần tử này, bạn có thể biến object này thành iterator thông qua việc cài đặt hàm __iter__(). Hãy xem ví dụ sau:


Ta có object Category chứa rất nhiều Book.


class Category():

   def __init__(self):

       self.books = []


   def add_book(self, book):

       self.books.append(book)


if __name__ == "__main__":

   x = Category()

   x.add_book("Python fundamentals")

   x.add_book("Python web")

   x.add_book("Python API")

   for book in x:

       print(book)


Khi thử lần lượt duyệt qua các book trong một Category với For, bạn sẽ nhận lỗi sau:


Traceback (most recent call last):

  File "iterator/iterator-test.py", line 13, in <module>

    for book in x:

TypeError: 'Category' object is not iterable


Để duyệt danh sách các book trong Category, bạn cần biến nó thành 1 iterator thông qua việc add thêm method __iter__()


class Category():

   ...

   def __iter__(self):

       return iter(self.books)


Kết quả sau khi chạy lại:

Python fundamentals

Python web

Python API


Generator


Generator được dùng cho nhiều mục đích. Điển hình là khi bạn làm việc với danh sách dữ liệu rất lớn mà muốn tiết kiệm bộ nhớ. Hãy xem ví dụ sau: Lấy ra tuần tự các phần tử là bội số của 5 và nhỏ hơn số N cho trước, hãy xem đoạn code của generator-test.py như sau:


def get_number(N):

   x = 0

   while True:

       x += 5

       if x > N:

           return

       yield x


gen = get_number(10)

print(gen)


Kết quả:

<generator object get_number at 0x106fb7138>


y1 = next(gen)

print(y1)

y2 = next(gen)

print(y2)



Kết quả:

5

10


y3 = next(gen)

print(y3)


Kết quả nếu ta tiếp tục gọi next(), sẽ có exception vì các số bội số của 5 mà không lớn hơn 10 chỉ gồm 2 số ở trên.


Traceback (most recent call last):

  File "iterator/generator-test.py", line 16, in <module>

    y3 = next(gen)

StopIteration


Generator  được tạo ra khi trong hàm, chúng ta sử dụng từ khoá yield. Hàm này sẽ chỉ chạy tiếp khi phương thức next() được gọi. Do các phần tử được tính toán tại thời điểm gọi nên sẽ không tốn bộ nhớ cho việc lưu các phần tử chưa dùng tới. Tuy nhiên bạn cũng cần lưu ý để áp dụng Generator tuỳ vào từng bài toán cụ thể.


Lọc - tạo mới phần tử


Giả sử bạn muốn lọc phần tử từ mảng hoặc tạo ra mảng mới từ mảng đang có. Bạn có thể dùng lọc với mảng để trả về một mảng hoặc trả về một Generator. Với Generator, bạn sẽ tiết kiệm được bộ nhớ.


Giả sử bạn muốn tạo ra một mảng các số mới là bình phương của các số trong mảng đã có:


x = [1,2,3,4,5]

y = [x1 * x1 for x1 in x]

print(y)

[1, 4, 9, 16, 25]


Nếu muốn lấy kết quả là một generator bạn làm như sau:


x = [1,2,3,4,5,6,7,8,9,10]

y = (x1 * x1 for x1 in x)

print(y)

print(next(y))

print(next(y))

Kết quả:

<generator object <genexpr> at 0x1071a9138>

1

4


Giả sử bạn muốn lấy ra phần tử chẵn của mảng thành mảng mới, bạn có 2 cách sau:


x = [1,2,3,4,5]

y = [x1 for x1 in x if x1%2==0]

print(y) // [2, 4]


x = [1,2,3,4,5]

y1 = (x1 for x1 in x if x1%2==0)

print(y1)

// <generator object <genexpr> at 0x10bdc2138>


Mời các bạn đọc thêm bài viết: Python với hàm Main.


Bạn muốn học Python, cùng tham khảo khóa học này nhé Python cơ bản và ứng dụng thực tế.


- Tech Zone -

Thư giãn chút nào!!!

Bài viết liên quan