Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Generator trong Python
Trong bài này chúng ta sẽ tìm hiểu về Generator trong Python, đây là cách giúp bạn tạo ra một đối tượng iterator cực kì dễ dàng. Bên cạn đó mình cũng phân tích giúp bạn hiểu được sự khác nhau giữa một hàm bình thường và một hàm generator.
1. Generators trong Python là gì?
Generator là cách tạo ra một mô hình lặp iterator trong Python, bằng cách sử dụng từ khóa yield để tạo ra những trình lặp một cách đơn giản và hiệu quả nhất.
Một hàm khi sử dụng yield thì bản thân nó đã tự kế thừa hai phương thức __iter__()
và __next__()
nên ta có thể sử dụng hàm next()
mà không cần phải sử dụng hàm iter()
để khởi tạo iterator.
2. Cách tạo Generators trong Python
Việc tạo một generator trong Python khá là đơn giản, nó giống như một hàm bình thường, nhưng thay vì sử dụng lệnh return để trả về thì ta sử dụng lệnh yield.
Bài viết này được đăng tại [free tuts .net]
Nếu một function (hàm) có chứa một hoặc nhiều lệnh yield thì nó là một generator. Một hàm có thể có một hoặc nhiều lệnh yield đặt tại nhiều vị trí khác nhau trong hàm.
Sự khác nhau giữa lệnh return và yield là trong khi return sẽ trả về một giá trị và kết thúc hàm, nhưng yield thì sẽ trả về nhưng vẫn giữ lại trạng thái của các biến, sau đó nếu được gọi tiếp thì nó sẽ tiếp tục xử lý ngay tại vị trí tạm dừng đó.
3. Sự khác nhau giữa hàm generator và hàm bình thường
Dưới đây là một vài điểm khác nhau giữa hàm generator và hàm bình thường.
- Generator chứa một hoặc nhiều lệnh yield.
- Khi được gọi, hàm generator sẽ trả về một đối tượng iterator nhưng nó không thực thi liền.
- Hai phương thức
__iter__()
và__next__()
được kế thừa tự động, vì vậy bạn có thể sử dụng hàmnext()
mà không cần dùng hàmiter()
để tạo iterator . - Trạng thái của các biến được lưu trữ lại giữa những lần gọi.
- Cuối cùng khi hàm kết thúc thì
StopIteration
sẽ bung ra cho lần gọi hàm tiếp theo, đây là lỗi cho thấy iterator đã duyệt đến phần tử cuối cùng.
Hãy xem ví dụ dưới đây để hiểu rõ hơn.
# HỌC PYTHON TAI FREETUTS.NET # AUTHOR: CƯỜNG NGUYỄN def generateNumber(): n = 1 print("Lần gọi thứ nhất trả về n = ", n) yield n n += 1 print("Lần gọi thứ hai trả về n = ", n) yield n n += 1 print("Lần gọi thứ ba trả về n = ", n) yield n # Chương trình chính num = generateNumber() # Kiểm tra xe num là gì? # => Nó là một generate object # <generator object generateNumber at 0x000001CD0A939510> print(num) # Gọi đến generate => trả về yield đầu tiên # Kết quả: 1 print(next(num)) # Gọi đến generate => trả về yield thứ hai # Kết quả: 2 print(next(num)) # Gọi đến generate => trả về yield thứ ba # Kết quả: 3 print(next(num))
Như bạn thấy, biến n
bên trong hàm đã được nhớ sau mỗi lần gọi.
4. Sử dụng vòng lặp trong Generator Python
Ở ví dụ trên mình chỉ giải thích cách hoạt động của generator chứ thực tế không ai làm như vậy.
Chúng ta thường sử dụng vòng lặp để tạo ra những generator.
Hãy xem ví dụ dưới đây, mình sẽ tạo ra một generator các số từ 1 đến 10.
# Tạo generator def generateNumber(): for i in range(1, 11): yield i # Lặp qua generator num = generateNumber() for n in num: print(n)
Kết quả như sau:
5. Sử dụng generator expression trong Python
Ngoài những cách trên thì bạn có thể tạo ra generator bằng biểu thức expression.
Cách hoạt động của nó giống như list comprehension, chỉ có điều một bên sử dụng dấu ngoặc vuông, một bên sử dụng dấu ngoặc nhọn.
Cách này chỉ phù hợp với những trường hợp có list data sẵn, và bạn muốn tạo ra một generator dựa trên list đó.
# Tạo list my_list = [1, 3, 6, 10] # Sử dụng expression để tạo generator generator = (x**2 for x in my_list) for item in generator: print(item)
Kết quả:
Ta có thể viết lại ví dụ trên bằng cách sử dụng vòng lặp như sau:
# Tạo list my_list = [1, 3, 6, 10] # Sử dụng vòng lặp def newList(my_list): for item in my_list: yield item generator = newList(my_list) for item in generator: print(item)
6. Tại sao nên dùng generator trong Python?
Lý do đơn giản và thuyết phục nhất là generator rất đơn giản và dễ thực hiện.
Generator triển khai dễ dàng và ngắn gọn hơn nhiều so với việc sử dụng trình lặp của iterator.
Ví dụ: Xây dựng một iterator tính lũy thừa của 2.
class LuyThua2(): def __init__(self, max = 0): # Thuộc tính lưu số lũy thừa hiện tại self.n = 0 # Thuộc tính lưu số lũy thừa tối đa self.max = max def __iter__(self): return self def __next__(self): if self.n > self.max: raise StopIteration else: result = 2 ** self.n self.n += 1 return result l = LuyThua2(5) print(next(l)) # n = 0 print(next(l)) # n = 1 print(next(l)) # n = 2 print(next(l)) # n = 3 print(next(l)) # n = 4 print(next(l)) # n = 5 print(next(l)) # Trả về lỗi StopIteration
Nhưng nếu viết bằng Generator thì chương trình rất ngắn gọn như sau:
def LuyThua2(max = 0): n = 0 while n <= max: yield 2 ** n n += 1 l = LuyThua2(5) print(next(l)) # n = 1 print(next(l)) # n = 2 print(next(l)) # n = 3 print(next(l)) # n = 4 print(next(l)) # n = 5 print(next(l)) # n = 6 print(next(l)) # Lỗi StopIteration
Ngoài ra, generator thân thiện hơn bởi nó chỉ tạo ra một mục tại một thời điểm gọi.
Trên là những chia sẻ cơ bản về cách sử dụng generator trong Python.