Hãy xem xét năng suất trong Python một cách chi tiết. Mục tiêu của chúng ta là hiểu tại sao và khi nào nên sử dụng câu lệnh yield thay vì câu lệnh return trong Python
Trình tạo trong Python
Bất kỳ hàm nào mang lại giá trị đều được gọi là trình tạo trong Python. Và một hàm trả về giá trị đương nhiên chỉ là một hàm
Do đó, câu hỏi “lợi nhuận so với lợi nhuận trong Python” có thể được diễn đạt lại thành “hàm so với trình tạo trong Python”
Đây là một minh họa tuyệt vời về hàm so với trình tạo trong Python
Một hàm chạy từ đầu đến cuối và có thể trả về một giá trị. Ngược lại, một trình tạo sẽ mang lại các giá trị và tạm dừng thực thi theo định kỳ
Hàm trình tạo trả về một đối tượng trình tạo, còn được gọi là trình lặp. Trình vòng lặp tạo ra một giá trị tại một thời điểm. Nó không lưu trữ bất kỳ giá trị. Điều này làm cho bộ nhớ máy phát hiệu quả
Ví dụ: bạn có thể sử dụng trình tạo để lặp qua các giá trị mà không cần lưu trữ bất kỳ giá trị nào trong bộ nhớ. Sau này, bạn sẽ tìm hiểu tại sao và khi nào điều này hữu ích
Cách thay thế 'return' bằng 'yield' trong Python
Thay thế các câu lệnh return bằng các câu lệnh năng suất trong Python có nghĩa là bạn biến một hàm thành một trình tạo
Thí dụ
Hãy tạo một hàm square[] bình phương một danh sách đầu vào gồm các số. Đây là một hàm thông thường trả về toàn bộ danh sách là kết quả
def square[numbers]: result = [] for n in numbers: result.append[n ** 2] return result numbers = [1, 2, 3, 4, 5] squared_numbers = square[numbers] print[squared_numbers]
đầu ra
[1, 4, 9, 16, 25]
Sau đó, hãy chuyển đổi chức năng này thành một trình tạo. Thay vì lưu trữ các số bình phương vào một danh sách, bạn có thể sinh ra từng giá trị một mà không cần lưu trữ chúng
def square[numbers]: for n in numbers: yield n ** 2 numbers = [1, 2, 3, 4, 5] squared_numbers = square[numbers] print[squared_numbers]
đầu ra
Bây giờ bạn không còn nhận được danh sách các số bình phương. Điều này là do kết quả squared_numbers là một đối tượng trình tạo
Nhưng làm thế nào bạn có thể truy cập các giá trị sau đó?
Tiếp theo chúng ta hãy nói về hàm next[] mà bạn yêu cầu trình tạo tạo giá trị
Gọi Hàm next[] để Mang lại Giá trị từ Trình tạo
Đối tượng trình tạo không chứa số trong bộ nhớ. Thay vào đó, nó tính toán và cho kết quả từng cái một. Nó chỉ thực hiện điều này khi bạn yêu cầu giá trị tiếp theo bằng hàm next[]
Hãy yêu cầu trình tạo tính toán số bình phương đầu tiên
print[next[squared_numbers]]
đầu ra
1
Hãy làm cho nó tính các số còn lại bằng cách gọi next[] bốn lần nữa
print[next[squared_numbers]] print[next[squared_numbers]] print[next[squared_numbers]] print[next[squared_numbers]]
đầu ra
4 9 16 25
Bây giờ trình tạo đã bình phương tất cả các số. Nếu bạn gọi next[] một lần nữa
print[next[squared_numbers]]
Lần này, một lỗi xảy ra
Traceback [most recent call last]: File "", line 13, in StopIteration
Lỗi này cho bạn biết không còn số nào được bình phương. Nói cách khác, trình tạo đã cạn kiệt
Bây giờ bạn đã hiểu cách một trình tạo hoạt động và cách làm cho nó tính toán các giá trị
Quên việc gọi next[] với Generators
Việc sử dụng hàm next[] cho thấy cách trình tạo hoạt động
Trên thực tế, bạn không cần gọi hàm next[]
Thay vào đó, bạn có thể sử dụng vòng lặp for có cùng cú pháp mà bạn sẽ sử dụng trong danh sách
Vòng lặp for thực sự gọi hàm next[] ẩn
Chẳng hạn, hãy lặp lại ví dụ về trình tạo bằng cách sử dụng vòng lặp for
[1, 4, 9, 16, 25]0
Kết quả
[1, 4, 9, 16, 25]1
Điều này chứng tỏ các máy phát điện tổng hợp có. Ngay cả khi bạn không lưu trữ các giá trị, bạn vẫn có thể sử dụng cùng một cú pháp vòng lặp for mà bạn sẽ sử dụng trên bất kỳ lần lặp nào khác
Mang lại một dòng giá trị vô tận
Như bạn đã biết, đối tượng trình tạo tạo ra một giá trị tại một thời điểm. Nó không lưu trữ bất kỳ giá trị nào trong số đó
Điều này cho phép tạo ra một dòng giá trị vô hạn. Bạn có thể lặp qua luồng vô hạn với cùng cú pháp mà bạn sẽ lặp một danh sách. Điều này là do cú pháp linh hoạt của trình tạo
Thí dụ
Hãy tạo một trình tạo vô hạn tạo ra tất cả các số lên đến vô cùng sau điểm bắt đầu
[1, 4, 9, 16, 25]2
Trình tạo này tạo ra các giá trị từ bắt đầu đến vô cùng
Hãy lặp lại các giá trị này [Cảnh báo. Một vòng lặp vô tận]
[1, 4, 9, 16, 25]3
Kết quả là bạn thấy một vòng lặp vô hạn in ra các giá trị vô thời hạn
[1, 4, 9, 16, 25]4
Nhưng các vòng lặp vô hạn là xấu, phải không?
Vâng, họ là. Nhưng vấn đề là để chứng minh về mặt cú pháp nó trông như thế nào nếu bạn có thể lặp qua một tập hợp giá trị vô hạn
Nhìn vào đoạn mã—bạn có thể viết
def square[numbers]: for n in numbers: yield n ** 2 numbers = [1, 2, 3, 4, 5] squared_numbers = square[numbers] print[squared_numbers]0 theo nghĩa đen và nó hoạt động. Tất cả là nhờ các trình tạo và thực tế là chúng không lưu trữ giá trị
Năng suất so với. Return—So sánh thời gian chạy
Hãy thực hiện so sánh thời gian chạy giữa năng suất và lợi nhuận trong Python
Trong ví dụ này, có một danh sách gồm mười số và hai hàm
- Hàm data_list[] chọn ngẫu nhiên một số từ danh sách
def square[numbers]: for n in numbers: yield n ** 2 numbers = [1, 2, 3, 4, 5] squared_numbers = square[numbers] print[squared_numbers]
1 lần - Trình tạo data_generator[] có chức năng này cũng chọn ngẫu nhiên một số từ danh sách
def square[numbers]: for n in numbers: yield n ** 2 numbers = [1, 2, 3, 4, 5] squared_numbers = square[numbers] print[squared_numbers]
1 lần
Mã này so sánh thời gian chạy của việc sử dụng các hàm này để tạo danh sách 1 triệu số được chọn ngẫu nhiên
[1, 4, 9, 16, 25]5
Kết quả
[1, 4, 9, 16, 25]6
Điều này cho thấy cách một trình tạo nhanh hơn để tạo. Điều này là do khi bạn tạo danh sách, tất cả các số phải được lưu trong bộ nhớ. Nhưng khi bạn sử dụng trình tạo, các số không được lưu trữ ở bất kỳ đâu, do đó, việc tạo nhanh như chớp
Khi sử dụng Yield trong Python
Hãy tự hỏi bản thân, “Tôi có cần nhiều mục cùng một lúc không?”
Nếu câu trả lời là “Không”, hãy sử dụng máy phát điện
Quay lại ví dụ về bình phương số. Hàm này lấy một danh sách các số, bình phương chúng và trả về danh sách
[1, 4, 9, 16, 25]7
Vì bạn chỉ muốn in danh sách các số bình phương, bạn có thể sử dụng trình tạo. Điều này là do các số không phụ thuộc vào nhau—Bạn có thể bình phương bất kỳ số nào mà không cần biết số tiếp theo. Do đó, không cần lưu trữ tất cả các số bình phương ở bất cứ đâu
[1, 4, 9, 16, 25]8
Một ví dụ khác, có lẽ thực tế hơn, hãy nghĩ về việc lặp qua một tệp có một tỷ chuỗi [e. g. mật khẩu]
Không có cách nào bạn có thể lưu trữ một tỷ chuỗi vào một danh sách. Trong trường hợp này, bạn có thể sử dụng trình tạo để lặp qua từng chuỗi một mà không cần lưu trữ chúng
Phần tốt nhất là về mặt cú pháp, có vẻ như bạn thực sự lưu trữ các giá trị vào một danh sách và đọc chúng từ đó
[1, 4, 9, 16, 25]9
Phần kết luận
Sự khác biệt giữa trả về và năng suất trong Python là trả về được sử dụng với các hàm thông thường và năng suất với trình tạo
Câu lệnh return trả về một giá trị từ hàm cho người gọi nó. Sau đó, phạm vi chức năng được thoát và mọi thứ bên trong chức năng đã biến mất
Câu lệnh năng suất trong Python biến một hàm thành trình tạo
Trình tạo là một hàm tiết kiệm bộ nhớ được gọi nhiều lần để truy xuất từng giá trị một
Câu lệnh năng suất tạm dừng trình tạo thực thi và trả về một giá trị duy nhất. Khi trình tạo được gọi lại, nó tiếp tục thực hiện từ nơi nó tạm dừng. Quá trình này tiếp tục cho đến khi không còn giá trị nào
Trình tạo không lưu trữ bất kỳ giá trị nào trong bộ nhớ. Thay vào đó, nó biết giá trị hiện tại và cách lấy giá trị tiếp theo. Điều này làm cho bộ nhớ của trình tạo hiệu quả để tạo
Lợi ích cú pháp của các trình tạo là việc lặp qua một trình tạo trông giống như lặp qua một danh sách
Sử dụng trình tạo rất tốt khi bạn lặp qua một nhóm phần tử và không cần lưu trữ chúng ở bất kỳ đâu