Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Python

Cách Viết Generator Trong Python

Iterator đóng vai trò quan trọng trong Python. Chúng cho phép bạn tạo một đối tượng có thể lặp lại. Đây là cách tốt để lưu trữ dữ liệu có thể truy cập qua vòng lặp for in.

Vấn đề với iterator là cần rất nhiều công sức để xây dựng một cái. Chúng hữu ích, nhưng bạn phải viết rất nhiều mã để làm cho nó hoạt động. Generator giúp giải quyết vấn đề này.

Trong hướng dẫn này, chúng ta sẽ nói về generator Python là gì và tại sao bạn nên dùng chúng. Chúng ta cũng sẽ triển khai một generator trong Python để giúp bạn hiểu cách chúng hoạt động.

Không nói thêm nữa, hãy bắt đầu!

Generator Là Gì?

Generator là cách đơn giản để tạo iterator trong Python. Nó là một hàm trả về một đối tượng mà bạn có thể lặp lại.

Generator thường được gọi là "cú pháp đường" (syntactic sugar). Điều này là vì chúng không nhất thiết thêm chức năng mới vào Python. Chúng giúp mã của bạn hiệu quả hơn.

Giống như iterator, bạn chỉ có thể lặp qua generator một lần. Điều này là vì generator theo dõi số lần chúng đã được lặp qua, và không thể được đặt lại.

Hãy xem một iterator Python:

class Cookies:
    def __init__(self, value):
        self.value = value
    def __iter__(self):
        return self
    def __next__(self):
        return self.value

Mã này tạo một iterator gọi là Cookies. Khi bạn truyền dữ liệu qua iterator này, nó sẽ được biến thành đối tượng lặp được. Điều này có nghĩa là bạn có thể lặp qua dữ liệu bằng vòng lặp for in. Nếu bạn nhìn mã này và nghĩ nó rất dài, đó là vì nó thực sự dài.

Generator có thể giúp chúng ta rút gọn mã này. Hãy viết generator cho iterator trên:

def cookies(value):
    while True:
        yield value

Mã của chúng ta đã được giảm đáng kể về kích thước. Hãy thảo luận cách này hoạt động.

Generator là một hàm có từ khóa yield thay vì câu lệnh return. Câu lệnh yield trả về giá trị từ hàm.

Khác biệt giữa câu lệnh yield và return là: câu lệnh return dừng hàm chạy, trong khi yield tạm dừng hàm và tiếp tục lặp.

Hãy thử lặp qua hàm generator đơn giản của chúng ta:

for cookie in cookies("Raspberry"):
    print(cookie)

Mã của chúng ta trả về:

Raspberry

Raspberry

…

Mã này lặp lại cho đến khi chúng ta dừng chương trình. Điều này là vì chúng ta dùng vòng lặp while chạy mãi mãi. Điều này không hữu ích trong hầu hết các trường hợp. Hãy viết một generator dừng lại khi một hành động đã được thực hiện.

Cách Viết Generator Python

Chúng ta có danh sách bánh quy muốn in ra bảng điều khiển. Danh sách trông như sau:

["Raspberry", "Choc-Chip", "Cinnamon", "Oat"]

Để in chúng ra bảng điều khiển, chúng ta có thể tạo một generator đơn giản. Mở file Python mới và dán mã sau:

def print_cookies(cookies):
    length = len(cookies)
    for cookie in range(0, length):
        yield cookies[cookie]

Generator của chúng ta đi qua mỗi bánh quy trong danh sách chúng ta chỉ định và trả về từng bánh quy riêng lẻ. Generator này chưa hoạt động. Chúng ta phải dùng vòng lặp for để lặp qua nó:

cookie_list = ["Raspberry", "Choc-Chip", "Cinnamon", "Oat"]
for c in print_cookies(cookie_list):
    print(c)

Chúng ta đã định nghĩa mảng gọi là cookie_list lưu danh sách bốn bánh quy. Chúng ta thiết lập vòng lặp for dùng generator của chúng ta để lặp qua giá trị trong cookie_list.

Đối với mỗi lần lặp trong vòng lặp for, đối tượng được tạo ra được in ra bảng điều khiển:

Raspberry

Choc-Chip

Cinnamon

Oat

Chúng ta đã làm được! Generator này trả về giá trị cho đến khi mỗi giá trị trong biến cookie_list đã được lặp qua.

Generator có nhiều trường hợp sử dụng. Giả sử chúng ta có danh sách giá đơn hàng muốn tính tổng. Chúng ta có thể làm như sau:

def total_orders(orders):
    length = len(orders)
    total = 0
    for o in range(0, length):
        total += orders[o]
        yield total

Generator này sẽ giữ tổng chạy của giá trị tất cả đơn hàng trong danh sách. Sau khi mỗi mục đã được lặp qua, generator sẽ yield giá trị tổng hiện tại của các đơn hàng. Hãy viết vòng lặp for dùng generator của chúng ta:

order_list = [2.30, 2.50, 1.95, 6.00, 7.50, 2.15]
for order in total_orders(order_list):
    print(order)

Mã của chúng ta trả về:

2.3

4.8

6.75

12.75

20.25

22.4

Generator của chúng ta đã tính giá trị tích lũy của tất cả đơn hàng trong biến order_list. Mỗi lần lặp qua danh sách trả về giá trị tích lũy mới.

Cách Viết Generator Expression (Biểu Thức Generator)

Generator đã dễ viết hơn iterator. Cuộc tìm kiếm mã sạch hơn không cần dừng lại, nhờ generator expression.

Generator expression tương tự list comprehension. Generator expression tạo ra một mục tại một thời điểm, như generator. Điều này khác với list comprehension tạo ra toàn bộ danh sách cùng một lúc.

Hãy viết generator cho ví dụ bánh quy:

cookie_list = ["Raspberry", "Choc-Chip", "Cinnamon", "Oat"]
cookie_generator = (cookie for cookie in cookie_list)

Chúng ta đã định nghĩa danh sách bánh quy trong biến cookie_list. Chúng ta tạo một generator expression. Expression này dùng cú pháp list comprehension nhưng có một khác biệt lớn: list comprehension được định nghĩa trong ngoặc vuông, trong khi generator được định nghĩa trong ngoặc tròn.

Hãy tạo vòng lặp for lặp qua expression:

for cookie in cookie_generator:
    print(cookie)

Mã của chúng ta trả về:

Raspberry

Choc-Chip

Cinnamon

Oat

Phản hồi của generator này giống với ví dụ đầu tiên. Cú pháp của chúng ta rõ ràng hơn đáng kể.

Lưu ý quan trọng: khi lặp qua generator được khai báo bằng generator expression, bạn không gọi nó như hàm. Generator cookie_generator không nhận giá trị đầu vào: nó đã chứa mã cần để lặp qua danh sách cookie_list.

Bạn chỉ nên dùng cú pháp generator expression khi cần viết generator thực hiện chức năng đơn giản. In danh sách giá trị là ví dụ tốt; nhân các giá trị trong danh sách với số cụ thể là ví dụ tốt khác.

Điều này là vì dù cú pháp generator expression rõ ràng, nó chủ yếu được thiết kế cho biểu thức một dòng. Nếu generator của bạn dùng hơn một dòng mã, hãy viết nó như hàm generator dùng cú pháp chúng ta thảo luận trước đó trong bài viết.

Tại Sao Generator Được Sử Dụng?

Lý do chính generator được dùng là chúng ngắn gọn hơn iterator.

Generator giống như một hàm và không cần câu lệnh __init__, __iter__, hay __next__ để hoạt động. Điều này khác với iterator yêu cầu cả ba câu lệnh đó. Hành vi này có nghĩa là bạn có thể triển khai generator trong ít dòng mã hơn đáng kể so với viết iterator.

Thêm vào đó, generator hiệu quả về bộ nhớ. Generator chỉ tạo ra một mục tại một thời điểm. Chúng không tạo toàn bộ dãy trong bộ nhớ trước khi trả về kết quả. Điều này làm cho generator rất thực tế nếu bạn cần lặp qua danh sách dữ liệu lớn.

Cách này hoạt động là iterator dùng phương pháp đánh giá lười (lazy evaluation). Chúng chỉ tạo phần tử tiếp theo của đối tượng lặp được khi mục đó được yêu cầu.

Kết Luận (Và Thử Thách)

Generator cho phép bạn tạo iterator Pythonic hơn.

Generator giúp dễ dàng viết đối tượng dùng giao thức iterator mà không phải viết câu lệnh __init__, __iter__, hay __next__. Generator được định nghĩa như hàm. Chúng dùng câu lệnh yield để dừng generator và truyền giá trị về chương trình chính trước khi trở về hoạt động thường xuyên.

Nếu bạn tìm kiếm thử thách, đây là vài ý tưởng:

  • Viết generator đảo ngược chuỗi.
  • Viết generator chỉ trả về giá trị chứa từ cụ thể từ danh sách giá trị.
  • Viết generator nhân mọi số trong danh sách gấp đôi.

Để đọc thêm, xem hướng dẫn của chúng tôi về cách viết và dùng iterator Python. Bây giờ bạn đã sẵn sàng bắt đầu làm việc với generator trong Python như chuyên gia!