Là một nhà phát triển cốt lõi của Python đã khiến tôi muốn hiểu cách thức hoạt động của ngôn ngữ này. Tôi nhận ra rằng sẽ luôn có những góc khuất mà tôi không biết mọi chi tiết phức tạp, nhưng để có thể trợ giúp về các vấn đề và thiết kế chung của Python, tôi cảm thấy mình nên thử và hiểu ngữ nghĩa cốt lõi của nó cũng như cách mọi thứ hoạt động bên trong.
Nhưng cho đến gần đây tôi không hiểu cách thức hoạt động của
for x in iterator:
yield x
3/for x in iterator:
yield x
4 trong Python 3. 5. Tôi biết rằng for x in iterator:
yield x
5 trong Python 3. 3 kết hợp với for x in iterator:
yield x
6 trong Python 3. 4 đã dẫn đến cú pháp mới này. Nhưng việc không thực hiện nhiều công việc liên quan đến mạng -- thứ mà for x in iterator:
yield x
6 không giới hạn mà còn tập trung vào -- đã khiến tôi không thực sự chú ý nhiều đến tất cả những thứ for x in iterator:
yield x
3/for x in iterator:
yield x
4 này. ý tôi là tôi biết điều đóyield from iterator
là [về cơ bản] tương đương với
for x in iterator:
yield x
Và tôi biết rằng
for x in iterator:
yield x
6 là một khung vòng lặp sự kiện cho phép lập trình không đồng bộ và tôi biết những từ đó [về cơ bản] có nghĩa là gì. Nhưng chưa bao giờ đi sâu vào cú pháp for x in iterator:
yield x
3/for x in iterator:
yield x
4 để hiểu tất cả những thứ này kết hợp với nhau như thế nào, tôi cảm thấy mình không hiểu lập trình bất đồng bộ trong Python, điều này làm tôi khó chịu. Vì vậy, tôi quyết định dành thời gian và thử tìm hiểu xem tất cả những thứ đó hoạt động như thế nào. Và vì tôi đã nghe từ nhiều người rằng họ cũng không hiểu cách thức hoạt động của thế giới lập trình không đồng bộ mới này, nên tôi quyết định viết bài luận này [vâng, bài đăng này đã mất rất nhiều thời gian và dài đến nỗi vợ tôi Bây giờ vì tôi muốn hiểu đúng về cách thức hoạt động của cú pháp, bài tiểu luận này có một số chi tiết kỹ thuật cấp thấp về cách CPython thực hiện mọi việc. Hoàn toàn ổn nếu nó chi tiết hơn bạn muốn hoặc bạn không hiểu hết về nó vì tôi không giải thích mọi sắc thái của nội bộ CPython để giữ cho điều này không biến thành một cuốn sách [e. g. , nếu bạn không biết rằng các đối tượng mã có cờ, mặc kệ đối tượng mã là gì, cũng không sao và bạn không cần quan tâm để lấy thứ gì từ tiểu luận này]. Tôi đã cố gắng cung cấp một bản tóm tắt dễ tiếp cận hơn ở cuối mỗi phần để bạn có thể lướt qua các chi tiết nếu chúng trở nên nhiều hơn những gì bạn muốn giải quyết.
Một bài học lịch sử về coroutines trong PythonTheo Wikipedia, "Coroutines là các thành phần chương trình máy tính tổng quát hóa các chương trình con cho đa nhiệm không ưu tiên, bằng cách cho phép nhiều điểm vào để tạm dừng và tiếp tục thực thi tại các vị trí nhất định". Đó là một cách nói khá kỹ thuật, "các coroutines là các chức năng mà bạn có thể tạm dừng thực thi". Và nếu bạn đang nói với chính mình, "nghe giống như máy phát điện", bạn sẽ đúng
Quay lại Python 2. 2, các trình tạo được giới thiệu lần đầu tiên bởi PEP 255 [chúng còn được gọi là trình lặp trình tạo vì trình tạo triển khai giao thức trình lặp]. Lấy cảm hứng chủ yếu từ ngôn ngữ lập trình Biểu tượng, các trình tạo cho phép tạo một trình vòng lặp không lãng phí bộ nhớ khi tính toán giá trị tiếp theo trong phép lặp theo cách đơn giản [bạn luôn có thể tạo một lớp triển khai
for x in iterator:
yield x
23 và for x in iterator:
yield x
24 mà . Ví dụ: nếu bạn muốn tạo phiên bản for x in iterator:
yield x
25 của riêng mình, bạn có thể thực hiện điều đó một cách háo hức bằng cách tạo một danh sách các số nguyênfor x in iterator:
yield x
2Tuy nhiên, vấn đề với điều này là nếu bạn muốn một dãy lớn như các số nguyên từ 0 đến 1.000.000, bạn phải tạo một danh sách đủ dài để chứa 1.000.000 số nguyên. Nhưng khi các trình tạo được thêm vào ngôn ngữ, bạn có thể đột nhiên tạo một trình vòng lặp mà không cần phải tạo toàn bộ chuỗi mà không tốn nhiều công sức. Thay vào đó, tất cả những gì bạn phải làm là có đủ bộ nhớ cho một số nguyên tại một thời điểm
for x in iterator:
yield x
6Có một chức năng tạm dừng những gì nó đang làm bất cứ khi nào nó chạm vào biểu thức
for x in iterator:
yield x
26 - mặc dù đó là một câu lệnh cho đến Python 2. 5 -- và sau đó có thể tiếp tục sau đó rất hữu ích về mặt sử dụng ít bộ nhớ hơn, cho phép ý tưởng về các chuỗi vô hạn, v.v.Nhưng như bạn có thể nhận thấy, tất cả các trình tạo đều liên quan đến trình vòng lặp. Bây giờ có một cách tốt hơn để tạo các trình vòng lặp rõ ràng là tuyệt vời [và điều này được thể hiện khi bạn xác định một phương thức
for x in iterator:
yield x
23 trên một đối tượng làm trình tạo], nhưng mọi người biết rằng nếu chúng ta lấy phần "tạm dừng" của trình tạo và thêm vào phần "gửi . Và tính năng chính xác của việc gửi nội dung vào trình tạo bị tạm dừng đã được thêm vào Python 2. 5 cảm ơn PEP 342. Trong số những thứ khác, PEP 342 đã giới thiệu phương pháp for x in iterator:
yield x
28 trên máy phát điện. Điều này cho phép một người không chỉ tạm dừng trình tạo mà còn gửi giá trị trở lại trình tạo nơi nó tạm dừng. Tiếp tục lấy ví dụ về for x in iterator:
yield x
25 của chúng ta, bạn có thể làm cho chuỗi nhảy tiến hoặc lùi một lượng nào đófor x in iterator:
yield x
1Máy phát điện không bị lẫn lộn nữa cho đến Python 3. 3 khi PEP 380 được thêm vào
for x in iterator:
yield x
5. Nói một cách chính xác, tính năng này trao quyền cho bạn tái cấu trúc các trình tạo một cách rõ ràng bằng cách giúp dễ dàng tạo ra mọi giá trị từ một trình vòng lặp [điều mà một trình tạo thuận tiện xảy ra]for x in iterator:
yield x
3Nhờ làm cho việc tái cấu trúc trở nên dễ dàng hơn,
for x in iterator:
yield x
5 cũng cho phép bạn xâu chuỗi các trình tạo lại với nhau để các giá trị nổi lên và xuống trong ngăn xếp cuộc gọi mà không cần mã phải làm bất kỳ điều gì đặc biệtfor x in iterator:
yield x
5Bản tóm tắt
Trình tạo trong Python 2. 2 tạm dừng thực thi mã. Sau khi khả năng gửi giá trị trở lại trình tạo bị tạm dừng đã được giới thiệu trong Python 2. 5, khái niệm về coroutines trong Python đã trở nên khả thi. Và việc bổ sung
for x in iterator:
yield x
5 trong Python 3. 3 giúp tái cấu trúc các trình tạo cũng như xâu chuỗi chúng lại với nhau dễ dàng hơnVòng lặp sự kiện là gì?Điều quan trọng là phải hiểu vòng lặp sự kiện là gì và cách chúng có thể lập trình không đồng bộ nếu bạn quan tâm đến
for x in iterator:
yield x
3/for x in iterator:
yield x
4. Nếu bạn đã từng lập trình GUI trước đây -- bao gồm cả công việc giao diện người dùng web -- thì bạn đã từng làm việc với một vòng lặp sự kiện. Nhưng vì khái niệm lập trình không đồng bộ như một cấu trúc ngôn ngữ là mới trong Python, sẽ không sao nếu bạn không biết vòng lặp sự kiện là gìQuay trở lại Wikipedia, vòng lặp sự kiện "là một cấu trúc lập trình chờ đợi và gửi các sự kiện hoặc thông báo trong một chương trình". Về cơ bản, một vòng lặp sự kiện cho phép bạn thực hiện, "khi A xảy ra, hãy làm B". Có lẽ ví dụ đơn giản nhất để giải thích điều này là vòng lặp sự kiện JavaScript có trong mọi trình duyệt. Bất cứ khi nào bạn nhấp vào thứ gì đó ["khi A xảy ra"], lần nhấp đó sẽ được đưa vào vòng lặp sự kiện JavaScript để kiểm tra xem có bất kỳ cuộc gọi lại
for x in iterator:
yield x
65 nào được đăng ký để xử lý lần nhấp đó ["do B"]. Nếu bất kỳ cuộc gọi lại nào đã được đăng ký thì cuộc gọi lại được gọi với các chi tiết về lần nhấp. Vòng lặp sự kiện được coi là một vòng lặp vì nó liên tục thu thập các sự kiện và lặp lại chúng để tìm xem phải làm gì với sự kiệnTrong trường hợp của Python,
for x in iterator:
yield x
6 đã được thêm vào thư viện chuẩn để cung cấp vòng lặp sự kiện. Có một trọng tâm về kết nối mạng trong for x in iterator:
yield x
6, trong trường hợp của vòng lặp sự kiện là biến "khi A xảy ra" thành khi I/O từ ổ cắm sẵn sàng để đọc và/hoặc ghi [thông qua mô-đun for x in iterator:
yield x
68]. Khác với GUI và I/O, các vòng lặp sự kiện cũng thường được sử dụng để thực thi mã trong một luồng hoặc quy trình con khác và để vòng lặp sự kiện đóng vai trò là bộ lập lịch [i. e. , đa nhiệm hợp tác]. Nếu bạn tình cờ hiểu GIL của Python, các vòng lặp sự kiện sẽ hữu ích trong trường hợp việc phát hành GIL là có thể và hữu íchBản tóm tắt
Các vòng lặp sự kiện cung cấp một vòng lặp cho phép bạn nói, "khi A xảy ra thì hãy làm B". Về cơ bản, một vòng lặp sự kiện sẽ đề phòng khi có điều gì đó xảy ra và khi điều gì đó mà vòng lặp sự kiện quan tâm xảy ra thì nó sẽ gọi bất kỳ mã nào quan tâm đến điều gì đã xảy ra. Python đã đạt được một vòng lặp sự kiện trong thư viện chuẩn ở dạng
for x in iterator:
yield x
6 trong Python 3. 4Cách thức hoạt động của for x in iterator:
yield x
3 và for x in iterator:
yield x
4Nó giống như trong Python 3. 4
Giữa các trình tạo được tìm thấy trong Python 3. 3 và một vòng lặp sự kiện ở dạng
for x in iterator:
yield x
6, Python 3. 4 đã đủ để hỗ trợ lập trình không đồng bộ ở dạng lập trình đồng thời. Lập trình không đồng bộ về cơ bản là lập trình mà thứ tự thực hiện không được biết trước [do đó không đồng bộ thay vì đồng bộ]. Lập trình đồng thời là viết mã để thực thi độc lập với các phần khác, ngay cả khi tất cả thực thi trong một luồng [đồng thời không phải là song song]. Ví dụ, sau đây là Python 3. 4 để đếm ngược mỗi giây trong hai lệnh gọi hàm đồng thời, không đồng bộfor x in iterator:
yield x
7Trong Trăn 3. 4, trình trang trí
for x in iterator:
yield x
13 được sử dụng để gắn nhãn cho một chức năng hoạt động như một quy trình đăng quang được sử dụng với for x in iterator:
yield x
6 và vòng lặp sự kiện của nó. Điều này đã mang lại cho Python định nghĩa cụ thể đầu tiên về coroutine. một đối tượng đã triển khai các phương thức được thêm vào trình tạo trong PEP 342 và được đại diện bởi lớp cơ sở trừu tượng for x in iterator:
yield x
15. Điều này có nghĩa là đột nhiên tất cả các trình tạo triển khai giao diện coroutine ngay cả khi chúng không được sử dụng theo kiểu đó. Để khắc phục điều này, for x in iterator:
yield x
6 yêu cầu tất cả các máy phát điện được sử dụng như một coroutine phải được trang trí bằng for x in iterator:
yield x
13Với định nghĩa cụ thể về quy trình đăng ký này [phù hợp với API mà trình tạo đã cung cấp], sau đó bạn đã sử dụng
for x in iterator:
yield x
5 trên bất kỳ đối tượng for x in iterator:
yield x
19 nào để chuyển nó xuống vòng lặp sự kiện, tạm dừng thực thi quy trình đăng ký trong khi bạn chờ đợi điều gì đó xảy ra [là một tương lai . Khi đối tượng tương lai đạt đến vòng lặp sự kiện, nó được theo dõi ở đó cho đến khi đối tượng tương lai hoàn thành bất cứ điều gì nó cần làm. Khi tương lai đã hoàn thành công việc của nó, vòng lặp sự kiện nhận thấy và coroutine bị tạm dừng chờ kết quả của tương lai bắt đầu lại với kết quả của nó được gửi trở lại coroutine bằng phương thức for x in iterator:
yield x
28 của nóLấy ví dụ của chúng tôi ở trên. Vòng lặp sự kiện bắt đầu từng lệnh gọi quy trình đăng ký
for x in iterator:
yield x
32, thực thi cho đến khi nó chạm vào for x in iterator:
yield x
5 và hàm for x in iterator:
yield x
34 ở một trong số chúng. Điều đó trả về một đối tượng for x in iterator:
yield x
19 được truyền xuống vòng lặp sự kiện và tạm dừng thực thi coroutine. Ở đó, vòng lặp sự kiện theo dõi đối tượng trong tương lai cho đến khi một giây kết thúc [cũng như kiểm tra những thứ khác mà nó đang xem, chẳng hạn như coroutine khác]. Sau khi hết một giây, vòng lặp sự kiện lấy quy trình đăng ký for x in iterator:
yield x
32 bị tạm dừng đã cung cấp cho vòng lặp sự kiện đối tượng tương lai, gửi kết quả của đối tượng tương lai trở lại quy trình đăng ký đã cung cấp cho nó đối tượng tương lai ngay từ đầu và quy trình đăng ký bắt đầu . Điều này tiếp tục cho đến khi tất cả các quy trình đăng ký for x in iterator:
yield x
32 chạy xong và vòng lặp sự kiện không còn gì để xem. Tôi sẽ thực sự cho bạn thấy một ví dụ hoàn chỉnh về cách hoạt động chính xác của tất cả các công cụ trong vòng lặp sự kiện/quy trình đăng ký này sau, nhưng trước tiên tôi muốn giải thích cách thức hoạt động của for x in iterator:
yield x
3 và for x in iterator:
yield x
4Đi từ for x in iterator:
yield x
5 đến for x in iterator:
yield x
4 trong Python 3. 5
for x in iterator:
yield x
for x in iterator:
yield x
Trong Trăn 3. 4, một hàm được gắn cờ là một coroutine cho mục đích lập trình không đồng bộ trông giống như
for x in iterator:
yield x
7Trong Trăn 3. 5, trình trang trí
for x in iterator:
yield x
52 đã được thêm vào để gắn cờ trình tạo là một coroutine giống như for x in iterator:
yield x
13. Bạn cũng có thể sử dụng for x in iterator:
yield x
54 để xác định về mặt cú pháp một hàm là một coroutine, mặc dù nó không thể chứa bất kỳ dạng biểu thức for x in iterator:
yield x
26 nào; for x in iterator:
yield x
4Tuy nhiên, một điều quan trọng mà
for x in iterator:
yield x
3 và for x in iterator:
yield x
52 làm là thắt chặt định nghĩa về coroutine là gì. Nó đưa các coroutine từ chỗ chỉ đơn giản là một giao diện thành một loại thực tế, làm cho sự khác biệt giữa bất kỳ trình tạo nào và trình tạo có nghĩa là một coroutine nghiêm ngặt hơn nhiều [và chức năng for x in iterator:
yield x
70 thậm chí còn chặt chẽ hơn bằng cách nói rằng phải sử dụng for x in iterator:
yield x
3]Bạn cũng sẽ nhận thấy rằng ngoài
for x in iterator:
yield x
3, Python 3. 5 ví dụ giới thiệu biểu thức for x in iterator:
yield x
4 [chỉ hợp lệ trong một for x in iterator:
yield x
54]. Trong khi for x in iterator:
yield x
4 hoạt động giống như for x in iterator:
yield x
5, các đối tượng được chấp nhận bởi một biểu thức for x in iterator:
yield x
4 là khác nhau. Các coroutines chắc chắn được cho phép trong một biểu thức for x in iterator:
yield x
4 vì khái niệm về các coroutines là nền tảng trong tất cả những điều này. Nhưng khi bạn gọi for x in iterator:
yield x
4 trên một đối tượng, về mặt kỹ thuật, nó cần phải là một đối tượng có thể chờ đợi. một đối tượng định nghĩa một phương thức for x in iterator:
yield x
70 trả về một trình vòng lặp không phải là một coroutine. Bản thân các coroutine cũng được coi là các đối tượng có thể chờ đợi [do đó tại sao for x in iterator:
yield x
15 kế thừa từ for x in iterator:
yield x
72]. Định nghĩa này tuân theo truyền thống của Python về việc biến hầu hết các cấu trúc cú pháp thành một lệnh gọi phương thức bên dưới mui xe, giống như for x in iterator:
yield x
73 là for x in iterator:
yield x
74 hoặc for x in iterator:
yield x
75 bên dưới tất cảLàm thế nào để sự khác biệt giữa
for x in iterator:
yield x
5 và for x in iterator:
yield x
4 diễn ra ở mức độ thấp [i. e. , một máy phát điện với for x in iterator:
yield x
52 so với. một với for x in iterator:
yield x
54]? . 5 để có được các chi tiết thực chất. Mã byte cho for x in iterator:
yield x
40 làfor x in iterator:
yield x
0Mã byte cho
for x in iterator:
yield x
41 làfor x in iterator:
yield x
1Bỏ qua sự khác biệt về số dòng do
for x in iterator:
yield x
40 có trình trang trí for x in iterator:
yield x
13, sự khác biệt duy nhất có thể nhìn thấy giữa chúng là opcode for x in iterator:
yield x
44 so với opcode for x in iterator:
yield x
45. Cả hai chức năng đều được gắn cờ chính xác là coroutines, vì vậy không có sự khác biệt nào ở đó. Trong trường hợp của for x in iterator:
yield x
44, nó chỉ cần kiểm tra xem đối số của nó là một trình tạo hay coroutine, nếu không, nó gọi for x in iterator:
yield x
47 trên đối số của nó [việc chấp nhận một đối tượng coroutine bởi opcode cho for x in iterator:
yield x
5 chỉ được phép khi opcode được sử dụng từ bên trong chính coroutine Nhưng
for x in iterator:
yield x
45 làm điều gì đó khác biệt. Mặc dù mã byte sẽ chấp nhận một coroutine giống như for x in iterator:
yield x
44, nhưng nó sẽ không chấp nhận một trình tạo nếu chưa được gắn cờ là một coroutine. Tuy nhiên, ngoài các coroutine, mã byte sẽ chấp nhận một đối tượng có thể chờ đợi như đã thảo luận trước đó. Điều này làm cho các biểu thức for x in iterator:
yield x
5 và biểu thức for x in iterator:
yield x
4 đều chấp nhận các coroutine trong khi khác nhau về việc chúng có chấp nhận các trình tạo đơn giản hoặc các đối tượng có thể chờ đợi tương ứng hay khôngBạn có thể thắc mắc tại sao sự khác biệt giữa những gì một coroutine dựa trên
for x in iterator:
yield x
3 và một coroutine dựa trên trình tạo sẽ chấp nhận trong các biểu thức tạm dừng tương ứng của chúng? . Vì các trình tạo vốn đã triển khai API cho các coroutine nên sẽ rất dễ vô tình sử dụng một trình tạo trong khi bạn thực sự dự kiến sẽ sử dụng một coroutine. Và vì không phải tất cả các trình tạo đều được viết để sử dụng trong luồng điều khiển dựa trên coroutine, nên bạn cần tránh vô tình sử dụng trình tạo không chính xác. Nhưng vì Python không được biên dịch tĩnh, ngôn ngữ tốt nhất có thể cung cấp là kiểm tra thời gian chạy khi sử dụng quy trình đăng ký do trình tạo xác định. Điều này có nghĩa là khi sử dụng for x in iterator:
yield x
52, trình biên dịch của Python không thể biết liệu một trình tạo sẽ được sử dụng như một quy trình đăng quang hay chỉ là một trình tạo đơn giản [hãy nhớ rằng chỉ vì cú pháp nói for x in iterator:
yield x
52 không có nghĩa là ai đó chưa hoàn thành trước đó Một điểm rất quan trọng mà tôi muốn làm rõ về sự khác biệt giữa một coroutine dựa trên trình tạo và một
for x in iterator:
yield x
3 là chỉ những coroutine dựa trên trình tạo mới thực sự có thể tạm dừng thực thi và buộc một thứ gì đó được gửi xuống vòng lặp sự kiện. Bạn thường không thấy chi tiết rất quan trọng này vì bạn thường gọi các hàm dành riêng cho vòng lặp sự kiện như hàm for x in iterator:
yield x
34 vì các vòng lặp sự kiện triển khai API của riêng chúng và đây là loại hàm phải lo lắng về chi tiết nhỏ này. Đối với đại đa số chúng ta, chúng ta sẽ làm việc với các vòng lặp sự kiện hơn là viết chúng và do đó chỉ viết for x in iterator:
yield x
3 coroutines và không bao giờ cần thực sự quan tâm đến điều này. Nhưng nếu bạn giống tôi và đang thắc mắc tại sao bạn không thể viết một cái gì đó như for x in iterator:
yield x
34 chỉ bằng cách sử dụng for x in iterator:
yield x
3 coroutines, thì đây có thể là "aha. " khoảng khăcBản tóm tắt
Hãy tóm tắt tất cả những điều này thành các thuật ngữ đơn giản hơn. Xác định một phương thức với
for x in iterator:
yield x
54 làm cho nó trở thành một coroutine. Một cách khác để tạo một coroutine là gắn cờ một trình tạo bằng for x in iterator:
yield x
52 -- về mặt kỹ thuật, cờ là cờ for x in iterator:
yield x
00 trên một đối tượng mã -- hoặc một lớp con của for x in iterator:
yield x
15. Bạn chỉ có thể tạm dừng chuỗi cuộc gọi coroutine với một coroutine dựa trên trình tạoMột đối tượng có thể chờ đợi là một coroutine hoặc một đối tượng xác định
for x in iterator:
yield x
70 -- về mặt kỹ thuật là for x in iterator:
yield x
72 -- trả về một iterator không phải là một coroutine. Biểu thức for x in iterator:
yield x
4 về cơ bản là for x in iterator:
yield x
5 nhưng với các hạn chế chỉ hoạt động với các đối tượng có thể chờ đợi [trình tạo đơn giản sẽ không hoạt động với biểu thức for x in iterator:
yield x
4]. Hàm for x in iterator:
yield x
3 là một coroutine có các câu lệnh for x in iterator:
yield x
56 -- bao gồm cả _______025 ẩn ở cuối mỗi hàm trong Python -- và/hoặc các biểu thức for x in iterator:
yield x
4 [không được phép sử dụng các biểu thức for x in iterator:
yield x
26]. Các hạn chế đối với các hàm for x in iterator:
yield x
3 là để đảm bảo rằng bạn không vô tình trộn và khớp các coroutine dựa trên trình tạo với các trình tạo khác vì mục đích sử dụng dự kiến của hai loại trình tạo này khá khác nhauHãy nghĩ về for x in iterator:
yield x
3/for x in iterator:
yield x
4 như một API cho lập trình không đồng bộMột điều quan trọng mà tôi muốn chỉ ra thực sự là điều mà tôi đã không thực sự nghĩ sâu về nó cho đến khi tôi xem bài phát biểu quan trọng về Python Brasil 2015 của David Beazley. Trong buổi nói chuyện đó, David đã chỉ ra rằng
for x in iterator:
yield x
3/for x in iterator:
yield x
4 thực sự là một API dành cho lập trình không đồng bộ [anh ấy đã nhắc lại với tôi trên Twitter]. Ý của David khi nói điều này là mọi người không nên nghĩ rằng for x in iterator:
yield x
3/for x in iterator:
yield x
4 đồng nghĩa với for x in iterator:
yield x
6, mà thay vào đó hãy nghĩ rằng for x in iterator:
yield x
6 là một khuôn khổ có thể sử dụng API for x in iterator:
yield x
3/for x in iterator:
yield x
4 cho lập trình không đồng bộDavid thực sự tin rằng ý tưởng về việc
for x in iterator:
yield x
3/for x in iterator:
yield x
4 là một API lập trình không đồng bộ mà anh ấy đã tạo dự án for x in iterator:
yield x
41 để triển khai vòng lặp sự kiện của riêng mình. Điều này đã giúp tôi hiểu rõ rằng for x in iterator:
yield x
3/for x in iterator:
yield x
4 cho phép Python cung cấp các khối xây dựng cho lập trình không đồng bộ, nhưng không ràng buộc bạn vào một vòng lặp sự kiện cụ thể hoặc các chi tiết cấp thấp khác [không giống như các ngôn ngữ lập trình khác tích hợp vòng lặp sự kiện vào . Điều này cho phép các dự án như for x in iterator:
yield x
41 không chỉ hoạt động khác ở cấp độ thấp hơn [e. g. , for x in iterator:
yield x
6 sử dụng các đối tượng trong tương lai làm API để giao tiếp với vòng lặp sự kiện của nó trong khi for x in iterator:
yield x
41 sử dụng các bộ dữ liệu], nhưng cũng có các đặc điểm hiệu suất và tiêu điểm khác nhau [e. g. , for x in iterator:
yield x
6 có toàn bộ khuôn khổ để triển khai các lớp vận chuyển và giao thức giúp nó có thể mở rộng trong khi for x in iterator:
yield x
41 đơn giản hơn và mong người dùng lo lắng về loại điều đó nhưng cũng cho phép nó chạy nhanh hơn]Dựa trên lịch sử [ngắn] của lập trình không đồng bộ trong Python, có thể hiểu rằng mọi người có thể nghĩ rằng
for x in iterator:
yield x
3/for x in iterator:
yield x
4 == for x in iterator:
yield x
6. Ý tôi là for x in iterator:
yield x
6 là thứ đã giúp lập trình không đồng bộ trong Python 3. 4 và là một yếu tố thúc đẩy để thêm for x in iterator:
yield x
3/for x in iterator:
yield x
4 vào Python 3. 5. Nhưng thiết kế của for x in iterator:
yield x
3/for x in iterator:
yield x
4 đủ linh hoạt để không yêu cầu for x in iterator:
yield x
6 hoặc bóp méo bất kỳ quyết định thiết kế quan trọng nào chỉ dành cho khuôn khổ đó. Nói cách khác, for x in iterator:
yield x
3/for x in iterator:
yield x
4 tiếp tục truyền thống của Python là thiết kế mọi thứ trở nên linh hoạt nhất có thể trong khi vẫn thực dụng để sử dụng [và triển khai]Một ví dụTại thời điểm này, đầu của bạn có thể tràn ngập các thuật ngữ và khái niệm mới, khiến bạn hơi khó nắm bắt đầy đủ cách thức hoạt động của tất cả những điều này để cung cấp cho bạn lập trình không đồng bộ. Để giúp làm cho mọi thứ trở nên cụ thể hơn, đây là một ví dụ lập trình không đồng bộ hoàn chỉnh [nếu giả định], từ đầu đến cuối từ vòng lặp sự kiện và các chức năng được liên kết với mã người dùng. Ví dụ này có các coroutines đại diện cho các lần phóng tên lửa riêng lẻ nhưng dường như đang đếm ngược đồng thời. Đây là lập trình không đồng bộ thông qua đồng thời;
for x in iterator:
yield x
2Như tôi đã nói, nó đã được tạo ra, nhưng nếu bạn chạy cái này trong Python 3. 5, bạn sẽ nhận thấy rằng cả ba coroutine đều chạy độc lập trong một luồng và tổng thời gian chạy là khoảng 5 giây. Bạn có thể coi
for x in iterator:
yield x
60, for x in iterator:
yield x
61 và for x in iterator:
yield x
62 là những gì mà nhà cung cấp vòng lặp sự kiện như for x in iterator:
yield x
6 và for x in iterator:
yield x
41 sẽ cung cấp cho bạn. Đối với người dùng bình thường, chỉ có mã trong for x in iterator:
yield x
32 và for x in iterator:
yield x
66 là quan trọng. Như bạn có thể thấy, không có phép thuật nào đối với for x in iterator:
yield x
3, for x in iterator:
yield x
4 hoặc toàn bộ thỏa thuận lập trình không đồng bộ này; Hy vọng và ước mơ của tôi cho tương laiBây giờ tôi đã hiểu cách lập trình không đồng bộ này hoạt động trong Python, tôi muốn sử dụng nó mọi lúc. Đó là một khái niệm tuyệt vời, tốt hơn rất nhiều so với thứ mà bạn đã sử dụng chủ đề trước đây. Vấn đề là Python 3. 5 quá mới nên
for x in iterator:
yield x
3/for x in iterator:
yield x
4 cũng rất mới. Điều đó có nghĩa là không có nhiều thư viện hỗ trợ lập trình bất đồng bộ như thế này. Chẳng hạn, để thực hiện các yêu cầu HTTP, bạn phải tự tạo yêu cầu HTTP bằng tay [yuck], hãy sử dụng một dự án như khung for x in iterator:
yield x
71 để thêm HTTP vào đầu vòng lặp sự kiện khác [trong trường hợp này là for x in iterator:
yield x
6] hoặc hy vọng có nhiều dự án hơn Cá nhân tôi hy vọng các dự án như
for x in iterator:
yield x
73 sẽ thành công để chúng tôi có sự tách biệt rõ ràng giữa việc lấy dữ liệu nhị phân từ I/O và cách chúng tôi diễn giải dữ liệu nhị phân đó. Loại trừu tượng này rất quan trọng vì hầu hết các thư viện I/O trong Python được liên kết khá chặt chẽ với cách chúng thực hiện I/O và cách chúng xử lý dữ liệu đến từ I/O. Đây là sự cố với gói for x in iterator:
yield x
76 trong thư viện chuẩn của Python vì gói này không có trình phân tích cú pháp HTTP mà có đối tượng kết nối thực hiện tất cả I/O cho bạn. Và nếu bạn đang hy vọng for x in iterator:
yield x
77 sẽ hỗ trợ lập trình không đồng bộ, thì hy vọng của bạn đã tiêu tan vì I/O đồng bộ mà for x in iterator:
yield x
77 sử dụng đã được đưa vào thiết kế của nó. Sự thay đổi về khả năng thực hiện lập trình không đồng bộ này mang lại cho cộng đồng Python cơ hội khắc phục sự cố mà cộng đồng này gặp phải khi không có sự trừu tượng hóa ở các lớp khác nhau của ngăn xếp mạng. Và chúng tôi có lợi thế là không khó để làm cho mã không đồng bộ chạy như thể nó đồng bộ, vì vậy các công cụ lấp đầy khoảng trống cho lập trình không đồng bộ có thể hoạt động ở cả hai thế giớiTôi cũng hy vọng rằng Python sẽ nhận được một số hình thức hỗ trợ trong
for x in iterator:
yield x
3 coroutines cho for x in iterator:
yield x
26. Có thể điều này sẽ yêu cầu một từ khóa khác [có thể giống như for x in iterator:
yield x
81?], nhưng thực tế là bạn thực sự không thể triển khai một hệ thống vòng lặp sự kiện chỉ với for x in iterator:
yield x
3 coroutine làm phiền tôi. May mắn thay, hóa ra tôi không phải là người duy nhất nghĩ điều này, và vì tác giả của PEP 492 cũng đồng ý với tôi, nên tôi nghĩ có cơ hội loại bỏ điều nàySự kết luậnVề cơ bản,
for x in iterator:
yield x
3 và for x in iterator:
yield x
4 là các trình tạo ưa thích mà chúng tôi gọi là coroutines và có một số hỗ trợ bổ sung cho những thứ được gọi là đối tượng có thể chờ đợi và biến các trình tạo đơn giản thành coroutines. Tất cả những điều này kết hợp với nhau để hỗ trợ đồng thời để chúng tôi hỗ trợ tốt hơn cho lập trình không đồng bộ trong Python. Nó tuyệt vời và dễ sử dụng hơn nhiều so với các cách tiếp cận có thể so sánh được như luồng -- Tôi đã viết một ví dụ từ đầu đến cuối về lập trình không đồng bộ dưới 100 dòng mã Python đã nhận xét -- trong khi vẫn khá linh hoạt và nhanh chóng [Câu hỏi thường gặp về đồ cổ nói rằng nó . ]. Tôi rất vui vì điều này đã xuất hiện trong Python 3 và tôi mong cộng đồng đón nhận nó và giúp tăng cường sự hỗ trợ của nó trong các thư viện và khung để tất cả chúng ta đều có thể hưởng lợi từ việc lập trình không đồng bộ trong Python