Các từ “yield” và “generator” là những khái niệm chính trong Python và cả hai đều cần nỗ lực nhiều hơn để hiểu ý nghĩa của chúng. Trong bài viết này, tôi sẽ cho bạn biết về mục đích của chúng và tại sao chúng lại hữu ích
Khi chúng ta làm việc với các hàm [còn được gọi là “chương trình con”, quá trình thực thi bắt đầu từ dòng đầu tiên và tiếp tục cho đến khi tìm thấy kết quả trả về, ngoại lệ hoặc kết thúc của hàm. Các chương trình con này chỉ trả lại một giá trị trong khi chúng trả về quyền kiểm soát thực thi. Chúng ta phải làm gì nếu muốn trả về một loạt giá trị thay vì chỉ một giá trị?
Trong Python, trình tạo là các hàm có thể tạo ra một loạt giá trị và chúng có thể bị tạm dừng để sau này lấy lại quyền kiểm soát thực thi
Tại sao chúng ta nên sử dụng máy phát điện?
Hãy xem xét một ví dụ. Giả sử chúng ta muốn giải bài toán sau
Cho một số N, trả về tất cả các số nguyên tố nhỏ hơn N
Sau đây sẽ là một nỗ lực ban đầu đơn giản
Nếu N=100, kết quả của việc thực hiện là
Nếu N là một số lớn, giải pháp này không chỉ mất quá nhiều thời gian để trả về kết quả mà còn có thể chiếm hết bộ nhớ khả dụng. Rõ ràng, sử dụng một danh sách để lấy toàn bộ các số nguyên tố không phải là giải pháp phù hợp. Điều gì sẽ xảy ra nếu thay vì sử dụng một hàm chỉ trả về một danh sách, chúng ta lại tạo một hàm trả về số nguyên tố tiếp theo? . Tức là máy phát điện
Máy phát điện và năng suấtTrình tạo là một hàm trả về một giá trị thông qua từ khóa yield thay vì return. Để lấy giá trị tiếp theo của trình tạo, chúng ta sử dụng chức năng tương tự như đối với trình vòng lặp. tiếp theo[]
Mỗi khi chúng ta gọi next[] trên một trình tạo, trình tạo phải chuyển một giá trị và điều khiển thông qua năng suất. Sao có thể như thế được? . Khi chúng ta gọi next[], trình tạo sẽ tiếp tục thực thi theo trạng thái đã lưu. Nếu chúng ta không gọi next[], trạng thái đã lưu sẽ bị loại bỏ
Hãy xem hàm get_first_primes[n] từ ví dụ sẽ trông như thế nào nếu chúng ta biến nó thành một trình tạo
Nếu trình tạo kết thúc định nghĩa của nó hoặc trả về được thực thi, nó sẽ tăng ngoại lệ StopIteration, điều đó có nghĩa là trình tạo đã cạn kiệt. Khi trình tạo đã hết, khi chúng ta gọi next[], nó sẽ báo lỗi vì chúng ta chỉ có thể lặp qua trình tạo một lần
Chúng tôi sẽ nhận được kết quả sau
Nếu chúng ta muốn một trình tạo không bao giờ hết [nghĩa là không bao giờ hết], chúng ta nên sửa đổi thời gian trong hàm get_first_primes để trình tạo của chúng ta là vô hạn
Sơ đồ sau đây cho thấy luồng thực thi cho N = 5
Thưởng. Coroutines thông qua Trình tạo nâng cao
Một coroutine là một chức năng, ngoài khả năng tạm dừng thực thi và sau đó được tiếp tục, còn có thể trao đổi dữ liệu theo cả hai cách. Trong trường hợp này, trình tạo không chỉ trả về các giá trị cho trình gọi của nó mà trình gọi còn gửi các giá trị đến trình tạo thông qua send[]
Để chứng minh nó hoạt động như thế nào, chúng ta sẽ quay lại ví dụ về số nguyên tố. Giả sử rằng, thay vì hiển thị danh sách các số nguyên tố nhỏ hơn N, chúng tôi muốn hiển thị số nguyên tố sau cho từng phần tử trong danh sách, sử dụng lại trình tạo số nguyên tố vô hạn
Do đó, chúng ta có thể thay đổi giá trị thành một giá trị khác mỗi khi trình tạo sử dụng câu lệnh năng suất
Chức năng cơ bản gọi nó sẽ là
Và chúng ta sẽ nhận được kết quả như sau
Điều đó là có thể bởi vì send sẽ gửi một giá trị mới đến trình tạo trong khi trả về giá trị thu được từ trình tạo đó
Được rồi, chúng ta đã đi đến cuối. Đây là phần giới thiệu về máy phát điện. Dưới đây là một số liên kết tham khảo trong trường hợp bạn muốn khám phá chủ đề chi tiết hơn
Ở đây chúng ta sẽ tìm hiểu sâu hơn về các trình tạo Python, bao gồm các biểu thức trình tạo và hàm trình tạo
Biểu thức trình tạo
Sự khác biệt giữa mức độ hiểu danh sách và biểu thức trình tạo đôi khi gây nhầm lẫn;
Danh sách hiểu sử dụng dấu ngoặc vuông, trong khi biểu thức trình tạo sử dụng dấu ngoặc đơn
Đây là một cách hiểu danh sách đại diện
Trong 1]
[n ** 2 for n in range[12]]
Ra[1]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
Trong khi đây là biểu thức trình tạo đại diện
Trong 2]
[n ** 2 for n in range[12]]
Ra[2]
Lưu ý rằng việc in biểu thức trình tạo không in nội dung;
Trong 3]
G = [n ** 2 for n in range[12]] list[G]
Ra[3]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
Danh sách là một tập hợp các giá trị, trong khi trình tạo là một công thức để tạo ra các giá trị
Khi bạn tạo một danh sách, bạn thực sự đang xây dựng một tập hợp các giá trị và có một số chi phí bộ nhớ liên quan đến điều đó. Khi bạn tạo một trình tạo, bạn không xây dựng một tập hợp các giá trị mà là một công thức để tạo ra các giá trị đó. Cả hai đều hiển thị cùng một giao diện trình lặp, như chúng ta có thể thấy ở đây
Trong [4]
L = [n ** 2 for n in range[12]] for val in L: print[val, end=' ']
________số 8_______
Trong [5]
G = [n ** 2 for n in range[12]] for val in G: print[val, end=' ']
________số 8_______
Sự khác biệt là một biểu thức trình tạo không thực sự tính toán các giá trị cho đến khi chúng cần thiết. Điều này không chỉ dẫn đến hiệu quả bộ nhớ mà còn cả hiệu quả tính toán. Điều này cũng có nghĩa là mặc dù kích thước của danh sách bị giới hạn bởi bộ nhớ khả dụng, nhưng kích thước của biểu thức trình tạo là không giới hạn
Có thể tạo một ví dụ về biểu thức trình tạo vô hạn bằng cách sử dụng trình vòng lặp
9 được xác định trongG = [n ** 2 for n in range[12]] list[G]0
Trong [6]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]0
Ra[6]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]1
Trong [7]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]2
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]3
Bộ vòng lặp
9 sẽ tiếp tục vui vẻ đếm mãi cho đến khi bạn yêu cầu nó dừng lại;Trong [8]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]4
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]5
Bạn có thể thấy những gì chúng tôi đang nhận được ở đây. nếu chúng ta mở rộng danh sách các thừa số một cách thích hợp, thì cái mà chúng ta sẽ bắt đầu là một trình tạo số nguyên tố, sử dụng thuật toán Sàng của Eratosthenes. Chúng ta sẽ khám phá điều này trong giây lát
Một danh sách có thể được lặp đi lặp lại nhiều lần;
Đây là một trong những vấn đề tiềm năng của các biểu thức trình tạo. Với một danh sách, chúng ta có thể làm điều này một cách đơn giản
Trong [9]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]6
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]7
Mặt khác, một biểu thức trình tạo được sử dụng hết sau một lần lặp
Trong [10]
G = [n ** 2 for n in range[12]] list[G]
Ra[10]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
Trong [11]
[n ** 2 for n in range[12]]0
Ra[11]
[n ** 2 for n in range[12]]1
Điều này có thể rất hữu ích vì nó có nghĩa là có thể dừng và bắt đầu lặp lại
Trong [12]
[n ** 2 for n in range[12]]2
[n ** 2 for n in range[12]]3
Một nơi tôi thấy điều này hữu ích là khi làm việc với các tập hợp tệp dữ liệu trên đĩa;
Chức năng máy phát điện. Sử dụng G = [n ** 2 for n in range[12]]
list[G]
2
Chúng ta đã thấy trong phần trước rằng khả năng hiểu danh sách được sử dụng tốt nhất để tạo các danh sách tương đối đơn giản, trong khi sử dụng vòng lặp
G = [n ** 2 for n in range[12]] list[G]3 bình thường có thể tốt hơn trong các tình huống phức tạp hơn. Điều này cũng đúng với các biểu thức trình tạo. chúng ta có thể tạo các trình tạo phức tạp hơn bằng cách sử dụng các hàm tạo, sử dụng câu lệnh
G = [n ** 2 for n in range[12]] list[G]2
Ở đây chúng ta có hai cách để xây dựng cùng một danh sách
Trong [13]
[n ** 2 for n in range[12]]4
[n ** 2 for n in range[12]]5
Tương tự như vậy, ở đây chúng ta có hai cách để xây dựng các máy phát điện tương đương
Trong [14]
[n ** 2 for n in range[12]]6
[n ** 2 for n in range[12]]7
Hàm tạo là một hàm, thay vì sử dụng
G = [n ** 2 for n in range[12]] list[G]5 để trả về một giá trị một lần, sử dụng
G = [n ** 2 for n in range[12]] list[G]2 để tạo ra một chuỗi giá trị [có thể là vô hạn]. Giống như trong các biểu thức trình tạo, trạng thái của trình tạo được giữ nguyên giữa các lần lặp lại một phần, nhưng nếu chúng ta muốn có một bản sao mới của trình tạo, chúng ta chỉ cần gọi lại hàm
Thí dụ. Trình tạo số nguyên tố
Ở đây tôi sẽ đưa ra ví dụ yêu thích của tôi về hàm tạo. một chức năng để tạo ra một loạt các số nguyên tố không giới hạn. Một thuật toán cổ điển cho việc này là Sàng Eratosthenes, hoạt động giống như thế này
Trong [15]
[n ** 2 for n in range[12]]8
[n ** 2 for n in range[12]]9
Trong [16]
01Trong [17]
23Trong [18]
45Nếu chúng ta lặp lại quy trình này đủ số lần trên một danh sách đủ lớn, chúng ta có thể tạo bao nhiêu số nguyên tố tùy thích
Hãy đóng gói logic này trong một hàm tạo
Trong 19]
67Thats tất cả để có nó. Mặc dù đây chắc chắn không phải là cách triển khai hiệu quả nhất về mặt tính toán của Sàng Eratosthenes, nhưng nó minh họa cú pháp hàm trình tạo có thể thuận tiện như thế nào để xây dựng các chuỗi phức tạp hơn