06/04/2021, 14:46

Generator trong Python - Python căn bản

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 ...

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__()__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.

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__()__next__() được kế thừa tự động, vì vậy bạn có thể sử dụng hàm next() mà không cần dùng hàm iter() để 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:

ket qua generator JPG

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ả:

generator python JPG

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.

Tạ Quốc Bảo

23 chủ đề

7270 bài viết

Cùng chủ đề
0