Python – iterator và generator

Trong phần này, chúng ta cùng tìm hiểu iterator và generator. Iterator là 1 object cho phép developer duyệt qua tất cả các phần tử của collection (string, list, tuple, dictionary,…)

Trong Python, iterator là 1 object implement iterator protocol. Protocol iterator gồm có 2 phương thức: Phương thức __iter__ trả về iterator object và phương thức next trả về phần tử tiếp theo của sequence.

Iterator có nhiều ưu điểm
– Clean code
– Iterator có thể làm việc mới infinite sequence
– Iterator tiết kiệm resource

Python có nhiều built-in object implement iterator protocol. Ví dụ như: string, list, tuple, dictionary hoặc file.

Ví dụ 1: interator string

str = "vncoding.net"

for e in str:
   print(e, end=" ")

print()

it = iter(str)

print(next(it))
print(next(it))
print(next(it))

print(list(it))

Trong đoạn code sau, chúng ta show built-in iterator với string. Trong python, string là chuỗi kí tự bất biến. Function iter() trả về interator object. Chúng ta cũng có thể sử dụng inter() với list hoặc tuple.

Kết quả:
C:\Users\Dell>python test.py
v n c o d i n g . n e t
v
n
c
[‘o’, ‘d’, ‘i’, ‘n’, ‘g’, ‘.’, ‘n’, ‘e’, ‘t’]

Python reading lines

Sử dụng interator tiết kiệm resource nghĩa, chúng ta có thể lấy trong phần tử tiếp theo trong sequence mà không lưu toàn bộ dataset trong memory.

Ví dụ 2: Đọc dữ liệu từ file sử dụng loop

with open('data.txt', 'r') as f:

    while True:

        line = f.readline()
    
        if not line: 
            break
            
        else: 
            print(line.rstrip())

Kết quả:
C:\Users\Dell>python test.py
Today is 28/5/2022,
I don’t want to do anything because I’m tired

Đoạn code này đọc file và in nội dung file data.txt. Thay vì sử dụng loop, chúng ta sử dụng interator để đơn giản việc in dữ liệu ra màn hình console.

Ví dụ 3: Đọc file sử dụng interator

with open('data.txt', 'r') as f:

    for line in f:
        print(line.rstrip())

Hàm open() trả về object file, object file là 1 interator. Chúng ta có thể sử dụng nó trong vòng lặp for. Sử dụng interator giúp cho code đơn giản hơn

Kết quả:
C:\Users\Dell>python test.py
Today is 28/5/2022,
I don’t want to do anything because I’m tired

Python iterator protocol

Trong ví dụ dưới đây, chúng ta tạo object implement protocol interator

Ví dụ 4: Tạo object implement protocol interator

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 e in s:

   print(e)
   n += 1
   
   if n > 10:
      break

Trong code, chúng ta tạo chuỗi số 1, 4, 27, 256, … minh họa interator, chúng ta có thể làm việc làm việc với chuỗi vô hạn

def __iter__(self):
    return self

Câu lệnh for gọi function __iter__ dựa trên object container. Function __iter__ trả về interator object. Và interator object có thể sử dụng hàm __next__ để access vào các phần tử trong container.

 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 sequence

if n > 10:
    break

Chúng ta đang làm việc với chuỗi vô hạn, chúng ta phải thêm điều kiện để thoát khỏi vòng lặp.

Kết quả:
C:\Users\Dell>python test.py
1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611

StopIteration

Có thể interrupt vòng lặp theo cách khác. Trong định nghĩa class, chúng ta phải raise expcetion ‘StopIteration’. Trong ví dụ sau, chúng ta implement lại ví dụ ở trên.

Ví dụ 5: Interrupt loop

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 e in s:
   print(e)

Trong ví dụ này in ra 14 chuỗi số ra màn hình console.

if self.x > 14:
    raise StopIteration

Exception StopIteration giúp thoát khỏi vòng lặp for

Kết quả:
C:\Users\Dell>python test.py
1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611
8916100448256
302875106592253
11112006825558016

Python generators

Generator là 1 hàm đặc biệt được sử dụng để control interator của vòng lặp. Generator tương tự như hàm trả về 1 mảng. Generator có tham số, generator có thể được gọi để tạo ra chuỗi số. Nhưng generator không giống với function thông thường là: function thông thường trả về toàn bộ mảng, generator chỉ trả về 1 giá trị tại 1 thời điểm. Điều này giúp tiết kiệm bộ nhớ.

Generators trong Python:
– Được định nghĩa với keyword ‘def’
– Sử dụng keyword ‘yield’ để trả về giá trị
– Có thể sử dụng nhiều keyword ‘yield’
– Trả về iterator

Hãy cùng xem ví dụ sau
Ví dụ 6: Sử dụng generator

def gen():

   x, y = 1, 2
   yield x, y
   
   x += 1
   yield x, y

g = gen()

print(next(g))
print(next(g))

try:
   print(next(g))
   
except StopIteration:
   print("Iteration finished")

Chương trình tạo ra một generation đơn giản

def gen():

   x, y = 1, 2
   yield x, y
   
   x += 1
   yield x, y

Generator được định nghĩa với keyword def, giống như function thông thường. Chúng ta sử dụng 2 keyword ‘yielf’ trong khối lệnh của generator. Keyword ‘yield’ thoát khỏi generator và trả về giá trị. Lần tiếp theo function next() của interator được gọi, logic source code tiếp tục execute từ dòng lệnh sau keyword ‘yield’ thứ nhất. Chú ý: các biến local x, y được lưu trong tất cả các lần lặp. Khi không còn keyword ‘yield’, exception ‘StopIteration’ được raise.

Kết quả:
C:\Users\Dell>python test.py
(1, 2)
(2, 2)
Iteration finished

Ví dụ sau đây, chúng ta tính số Fibonacci. Số đầu tiên của chuỗi Fibonacci là 0, số thứ 2 là 1, và mỗi số tiếp theo bằng tổng của 2 số liền trước nó.

Ví dụ 7: Viết generator trả về chuỗi Fibonacci

import time

def fib():
    
   a, b = 0, 1

   while True:
      yield b      
      a, b = b, a + b


g = fib()

try:
   for e in g:
      print(e)
      
      time.sleep(1)
            
except KeyboardInterrupt:
   print("Calculation stopped")

Script in liên tục các số trong chuỗi Fibonacci cho đến khi ấn Ctrl+C để kết thúc vòng lặp

Kết quả:
C:\Users\Dell>python test.py
1
1
2
3
5
8
13
21
Calculation stopped

Biểu thức generator

Biểu thức generator tương tự với list comprehension. Chỉ khác là biểu thức generator trả về generator, không trả về list.

Ví dụ 8: Minh họa biểu thức generator

n = (e for e in range(50000000) if not e % 3)

i = 0

for e in n:
    print(e)
    
    i += 1
    
    if i > 100:
        raise StopIteration

Ví dụ này in ra các giá trị chia hết cho 3

n = (e for e in range(50000000) if not e % 3)

Biểu thức generator được tạo với dấu ngoặc (). Tạo 1 list comprehension trong ví dụ này sẽ không hiệu quả vì sẽ chiếm 1 lượng lớn memory không cần thiết. Thay vì dùng list comprehension, chúng ta tạo 1 biểu thức generator để tạo ra các giá trị theo yêu cầu.

i = 0

for e in n:
    print(e)
    
    i += 1
    
    if i > 100:
        raise StopIteration

Trong vòng for, chúng ta in ra 100 giá trị

Ví dụ 9: sử dụng generator viết 1 utility giống như command grep trong Linux.
data.txt

Vietnam, officially the Socialist Republic of Vietnam,[n 3] is a country in Southeast Asia, at the eastern edge of mainland Southeast Asia, with an area of 311,699 square kilometres (120,348 sq mi) and population of 96 million, making it the world's fifteenth-most populous country. 
Vietnam borders China to the north, Laos and Cambodia to the west, and shares maritime borders with Thailand through the Gulf of Thailand, and the Philippines, Indonesia, and Malaysia through the South China Sea. 
Its capital is Hanoi and largest city Ho Chi Minh City.

Đây là file sample để test

test.py

import sys

def grep(pattern, lines):
    return ((line, lines.index(line)+1) for line in lines if pattern in line)

file_name = sys.argv[2]
pattern = sys.argv[1]

with open(file_name, 'r') as f:
    lines = f.readlines()
    
    for line, n in grep(pattern, lines):
        print(n, line.rstrip())

Đoạn code này đọc nội dung file và in số dòng và nội dung dòng có chứa từ khóa cần tìm kiếm.

def grep(pattern, lines):
    return ((line, lines.index(line)+1) for line in lines if pattern in line)

Sử dụng biểu thức generator duyệt qua list các dòng và chọn ra các dòng và tính toán số dòng chứa từ khóa cần tìm kiếm.

with open(file_name, 'r') as f:
    lines = f.readlines()
    
    for line, n in grep(pattern, lines):
        print(n, line.rstrip())

Open và đọc nội dung file, gọi hàm grep() trả về generator chứa nội dung (số dòng + nội dung dòng) chứa từ khóa tìm kiếm

Kết quả:
C:\Users\Dell>python test.py Vietnam data.txt
1 Vietnam, officially the Socialist Republic of Vietnam,[n 3] is a country in Southeast Asia, at the eastern edge of mainland Southeast Asia, with an area of 311,699 square kilometres (120,348 sq mi) and population of 96 million, making it the world’s fifteenth-most populous country.
2 Vietnam borders China to the north, Laos and Cambodia to the west, and shares maritime borders with Thailand through the Gulf of Thailand, and the Philippines, Indonesia, and Malaysia through the South China Sea.

Note:
– python: process Python
– test.py: script chứa code ở trên
– Vietnam: từ khóa cần tìm kiếm
– data.txt: file dữ liệu.

Be the first to comment

Leave a Reply