Hàm sinh Python: Đây là gì?
By khoanc, at: 11:02 Ngày 03 tháng 11 năm 2023
Thời gian đọc ước tính: __READING_TIME__ minutes


1. Generator là gì?
Một generator trong Python là một loại iterable đặc biệt cho phép bạn lặp qua một chuỗi các phần tử tiềm năng rất lớn mà không cần lưu giữ toàn bộ chuỗi trong bộ nhớ. Không giống như danh sách hoặc bộ tuple, generator không lưu trữ tất cả các giá trị của chúng cùng một lúc. Thay vào đó, chúng tạo ra các giá trị khi bạn lặp qua chúng. Generator được định nghĩa bằng các hàm có từ khóa yield
.
2. Tại sao Generator lại quan trọng?
Generator rất cần thiết vì một số lý do:
-
Hiệu quả bộ nhớ: Generator hiệu quả về bộ nhớ vì chúng không tải toàn bộ chuỗi vào bộ nhớ. Điều này rất quan trọng khi làm việc với các tập dữ liệu lớn.
-
Đánh giá lười: Chúng hỗ trợ đánh giá lười, có nghĩa là các giá trị chỉ được tạo ra khi cần thiết, giảm thời gian tính toán và sử dụng tài nguyên.
-
Các chuỗi vô hạn: Generator có thể biểu diễn các chuỗi vô hạn, cho phép bạn làm việc với các luồng dữ liệu không có điểm cuối xác định.
-
Đơn giản hóa mã: Chúng làm cho mã ngắn gọn và dễ đọc hơn bằng cách tách logic tạo dữ liệu khỏi logic lặp.
Một trường hợp sử dụng quan trọng của generator là khi bạn đọc một tệp lớn từng dòng một.
Cách tiếp cận bình thường sẽ là
with open(filename) as file:
lines = [line.rstrip() for line in file]
điều này rất chậm và có thể gây ra sự cố bộ nhớ. Thay vào đó, chúng ta nên sử dụng phương pháp hiệu năng cao hơn bên dưới
def read_in_chunks(file_object, chunk_size=1024):
"""Hàm lười (generator) để đọc tệp từng phần.
Kích thước phần mặc định: 1k."""
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
with open('really_big_file.dat') as f:
for piece in read_in_chunks(f):
process_data(piece)
3. Cách sử dụng Generator
Để tạo và sử dụng một generator:
- Định nghĩa một hàm chứa từ khóa
yield
.
- Khi hàm được gọi, nó không thực thi ngay lập tức mà trả về một đối tượng generator.
- Các giá trị được tạo ra bằng từ khóa
yield
bên trong hàm, và trạng thái của hàm được giữ lại giữa các lần gọi.
def number_generator(n):
for i in range(n):
yield i
gen = number_generator(5)
for num in gen:
print(num) # Output: 0, 1, 2, 3, 4
4. Vấn đề với Generator
Mặc dù generator cung cấp hiệu quả bộ nhớ, nhưng chúng có thể gây ra các vấn đề về hiệu năng:
-
Truy cập chậm hơn: Truy cập các phần tử trong một generator có thể chậm hơn so với truy cập các phần tử trong một danh sách vì mỗi giá trị được tạo động.
-
Lặp lại một lần: Generator thường chỉ sử dụng một lần. Sau khi generator đã được dùng hết, bạn không thể tua lại nó, không giống như danh sách mà bạn có thể lặp lại nhiều lần.
-
Quản lý trạng thái: Quản lý trạng thái của một generator và hiểu khi nào nó được dùng hết có thể khó khăn.
-
Trường hợp sử dụng hạn chế: Generator phù hợp nhất với dữ liệu tuần tự, làm cho chúng ít phù hợp hơn với các tác vụ truy cập ngẫu nhiên hoặc thao tác dữ liệu phức tạp.
5. Các thư viện sử dụng Generator rộng rãi để tăng hiệu năng
-
Thư viện chuẩn Python: Thư viện chuẩn của Python bao gồm một số hàm và mô-đun tích hợp trả về generator, chẳng hạn như
range()
,zip()
vàenumerate()
.
-
Các thư viện của bên thứ ba: Các thư viện như
itertools
,asyncio
vàDask
sử dụng generator rộng rãi để cung cấp khả năng xử lý dữ liệu hiệu năng cao và khả năng lập trình không đồng bộ.
6. Sự khác biệt giữa Iterator và Generator
Generator là một loại iterator, nhưng có những điểm khác biệt chính:
-
Iterator: Iterator là một khái niệm tổng quát hơn và có thể là một đối tượng tuân theo giao thức iterator (với các phương thức
__iter__()
và__next__()
). Iterator có thể được tạo bằng các lớp và không nhất thiết phải liên quan đến đánh giá lười.
-
Generator: Generator là một loại iterator cụ thể được tạo bằng các hàm có từ khóa
yield
. Chúng được thiết kế rõ ràng để đánh giá lười và thường hiệu quả hơn về bộ nhớ.
Ví dụ về Iterator
class MyIterator:
def __init__(self, max_val):
self.max_val = max_val
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.max_val:
result = self.current
self.current += 1
return result
else:
raise StopIteration
my_iter = MyIterator(5)
for num in my_iter:
print(num) # Output: 0, 1, 2, 3, 4
Ví dụ về Generator
def number_generator(n):
for i in range(n):
yield i
gen = number_generator(5)
for num in gen:
print(num) # Output: 0, 1, 2, 3, 4
Tóm lại, generator là một dạng chuyên biệt của iterator, được thiết kế để hiệu quả về bộ nhớ và đánh giá lười, làm cho chúng lý tưởng cho dữ liệu tuần tự và các tập dữ liệu lớn.