Hướng dẫn does python use a virtual machine - python có sử dụng máy ảo không
Trượt 1 Show
Chương trình chuyên nghiệp theo định hướng công việc đáng tin cậy nhất Chuyên gia được chứng nhận DevOps (DCP) Thực hiện bước đầu tiên của bạn vào thế giới của DevOps với khóa học này, điều này sẽ giúp bạn tìm hiểu về các phương pháp và công cụ được sử dụng để phát triển, triển khai và vận hành phần mềm chất lượng cao. Trượt 2 DevOps to DevSecops - Tìm hiểu sự tiến hóa DevSecops Chứng nhận Chuyên nghiệp (DSOCP) Học cách tự động hóa bảo mật vào môi trường DevOps có nhịp độ nhanh bằng các công cụ và tập lệnh nguồn mở khác nhau. Trượt 2 DevOps to DevSecops - Tìm hiểu sự tiến hóa DevSecops Chứng nhận Chuyên nghiệp (DSOCP) Học cách tự động hóa bảo mật vào môi trường DevOps có nhịp độ nhanh bằng các công cụ và tập lệnh nguồn mở khác nhau. Trượt 2 DevOps to DevSecops - Tìm hiểu sự tiến hóa DevSecops Chứng nhận Chuyên nghiệp (DSOCP) Trượt 2 DevOps to DevSecops - Tìm hiểu sự tiến hóa DevSecops Chứng nhận Chuyên nghiệp (DSOCP) Học cách tự động hóa bảo mật vào môi trường DevOps có nhịp độ nhanh bằng các công cụ và tập lệnh nguồn mở khác nhau. Được chứng nhận về kỹ năng công nghệ mới để cai trị ngành công nghiệp Kỹ thuật độ tin cậy trang web (SRE) Chứng nhận Chuyên nghiệp Một phương pháp đo lường và đạt được độ tin cậy thông qua công việc kỹ thuật và hoạt động - được phát triển bởi Google để quản lý dịch vụ. Thạc sĩ về Kỹ thuật DevOps (MDE) Được ghi danh cho khóa học nâng cao nhất và duy nhất trên thế giới có thể khiến bạn trở thành một chuyên gia và kiến trúc sư thành thạo trong các nguyên tắc của DevOps, DevSecops và trang web Kỹ thuật độ tin cậy (SRE) cùng nhau. Có được chuyên môn và chứng nhận bản thân
Bên trong máy ảo Python Mua trên LeanpubMục lục
1. Giới thiệuIntroduction2. Quan điểm từ 30.000ft 3. Biên soạn mã nguồn Python 3.1 Từ nguồn này sang cây khác
3.5 Từ AST đến mã đối tượng 4. Đối tượng Python Công việc này là phiên bản mở rộng của các ghi chú cá nhân được thực hiện trong khi điều tra hoạt động bên trong của phiên dịch viên Python. Có một số lượng đáng kể sự khôn ngoan trong các video có sẵn trong các video PYCON, bài giảng của trường và viết blog. Công việc này sẽ không đầy đủ mà không cần thừa nhận những nguồn tuyệt vời này. Khi kết thúc bài viết này, một người đọc nên hiểu các quy trình và cấu trúc dữ liệu rất quan trọng đối với việc thực hiện chương trình Python. Chúng tôi bắt đầu tiếp theo với một cái nhìn tổng quan về việc thực thi tập lệnh được truyền dưới dạng đối số dòng lệnh cho trình thông dịch. Người đọc có thể cài đặt 07 thực thi từ nguồn bằng cách làm theo các hướng dẫn tại hướng dẫn của nhà phát triển Python. 2. Quan điểm từ 30.000ftThe View From 30,000ftChương này là một tổng quan cấp cao về các quy trình liên quan đến việc thực hiện chương trình Python. Bất kể sự phức tạp của chương trình Python, các kỹ thuật được mô tả ở đây là như nhau. Trong các chương tiếp theo, chúng tôi phóng to để cung cấp chi tiết về các phần khác nhau của câu đố. Giải thích tuyệt vời về quá trình này được cung cấp bởi Yaniv Aknin trong loạt bài nội bộ Python của ông cung cấp một số cơ sở và động lực cho cuộc thảo luận này. Một phương pháp thực thi tập lệnh Python là truyền nó như một đối số cho trình thông dịch Python như vậy 11. Có nhiều cách khác để tương tác với trình thông dịch - chúng tôi có thể bắt đầu trình thông dịch tương tác, thực thi một chuỗi dưới dạng mã, v.v. Tuy nhiên, các phương pháp này không được chúng tôi quan tâm. Hình 2.1 là luồng hoạt động liên quan đến việc thực hiện một mô-đun được chuyển cho trình thông dịch tại dòng lệnh. Hình 2.1: Dòng chảy trong quá trình thực thi mã nguồn Python thực thi là một chương trình 08 như bất kỳ chương trình 08 nào khác như hạt nhân Linux hoặc chương trình 14 đơn giản trong 08 rất nhiều quá trình tương tự xảy ra khi chúng tôi chạy Trình thông dịch Python có thể thực thi. Điểm nhập cảnh thực thi là phương thức 16 trong 17. Phương pháp 16 này xử lý khởi tạo cơ bản, chẳng hạn như phân bổ bộ nhớ, cài đặt địa phương, v.v. Sau đó, nó gọi hàm 19 trong 20 chịu trách nhiệm cho các khởi tạo cụ thể của Python. Chúng bao gồm phân tích các đối số dòng lệnh và cài đặt cờ chương trình, các biến môi trường đọc, móc chạy, thực hiện ngẫu nhiên băm, v.v. Sau, 19 gọi hàm 22 trong 23; 22 chịu trách nhiệm khởi tạo trình thông dịch và tất cả các đối tượng và cấu trúc dữ liệu được liên kết theo yêu cầu của thời gian chạy Python. Sau khi 22 hoàn thành thành công, giờ đây chúng tôi có quyền truy cập vào tất cả các đối tượng Python. Cấu trúc dữ liệu trạng thái thông dịch và trình thông dịch là hai ví dụ về cấu trúc dữ liệu được khởi tạo bằng cuộc gọi 22. Một cái nhìn về các định nghĩa cấu trúc dữ liệu cho những điều này cung cấp một số bối cảnh vào các chức năng của chúng. Trình thông dịch và trạng thái luồng là các cấu trúc 08 với các con trỏ đến các trường chứa thông tin cần thiết để thực hiện một chương trình. Liệt kê 2.1 là trạng thái thông dịch 28 (chỉ giả sử rằng 28 là 08 SIGN cho một định nghĩa loại mặc dù điều này không hoàn toàn đúng).Liệt kê 2.1: Cấu trúc dữ liệu trạng thái thông dịch
Bất cứ ai đã sử dụng ngôn ngữ lập trình Python đủ lâu đều có thể nhận ra một vài lĩnh vực được đề cập trong cấu trúc này (Sysdict, Constructions, Codec)*.
Một chương trình Python phải thực thi trong một luồng. Cấu trúc trạng thái luồng chứa tất cả các thông tin cần thiết bởi một luồng để chạy một số mã. Liệt kê 2.2 là một đoạn của cấu trúc dữ liệu luồng. Liệt kê 2.2: Một mặt cắt của cấu trúc dữ liệu trạng thái luồng
Thông tin chi tiết về trình thông dịch và cấu trúc dữ liệu trạng thái luồng sẽ theo sau trong các chương tiếp theo. Quá trình khởi tạo cũng thiết lập các cơ chế nhập khẩu cũng như stdio thô sơ. Sau khi khởi tạo, hàm 19 gọi hàm 41 cũng trong mô -đun 42. Một loạt các cuộc gọi chức năng sau: 43 được thực hiện cho hàm 44. Cuộc gọi chức năng 45 tạo ra không gian tên 46 trong đó nội dung tệp sẽ được thực thi. Nó cũng kiểm tra sự hiện diện của phiên bản 47 của mô -đun - tệp 47 chứa phiên bản biên dịch của mô -đun thực thi. Nếu phiên bản 47 tồn tại, nó sẽ cố gắng đọc và thực hiện nó. Mặt khác, trình thông dịch gọi hàm 50 theo sau là một cuộc gọi đến hàm 44 và sau đó là hàm 52. Hàm 52 đọc nội dung mô -đun và xây dựng một cây phân tích từ nó. Hàm 54 sau đó được gọi với cây phân tích như một đối số và tạo ra một cây cú pháp trừu tượng (AST) từ cây phân tích. AST được tạo ra sau đó được truyền đến hàm 55. Hàm này gọi hàm 56 tạo ra các đối tượng mã từ AST. Xin lưu ý rằng mã byte được tạo trong cuộc gọi đến 56 được chuyển qua trình tối ưu hóa 58 đơn giản để thực hiện tối ưu hóa treo thấp của mã byte được tạo trước khi tạo đối tượng mã. Với các đối tượng mã được tạo, đã đến lúc thực hiện các hướng dẫn được gói gọn bởi các đối tượng mã. Hàm 55 gọi 60 từ 61 với đối tượng mã làm đối số. Điều này dẫn đến một loạt các cuộc gọi chức năng khác: 62. Đối tượng mã là một đối số cho hầu hết các chức năng này. 63 là vòng thực hiện thực tế xử lý thực thi các đối tượng mã. Hàm này được gọi với một đối tượng khung như một đối số. Đối tượng khung này cung cấp bối cảnh để thực thi đối tượng mã. Vòng thực thi đọc và thực hiện các hướng dẫn từ một mảng các hướng dẫn, thêm hoặc xóa các đối tượng khỏi ngăn xếp giá trị trong quy trình (giá trị này ở đâu?), Cho đến khi không còn hướng dẫn nào để thực hiện hoặc một cái gì đó đặc biệt phá vỡ vòng lặp này xảy ra. Python cung cấp một tập hợp các chức năng mà người ta có thể sử dụng để khám phá các đối tượng mã thực tế. Ví dụ, một chương trình đơn giản có thể được biên dịch thành một đối tượng mã và được tháo rời để có được các opcodes được thực thi bởi máy ảo Python, như trong Liệt kê 2.3. Liệt kê 2.3: Tháo chức năng Python
Tệp 64 chứa một danh sách các hướng dẫn mã Byte của máy ảo Python. Các opcodes khá thẳng về mặt khái niệm. Lấy ví dụ của chúng tôi từ Liệt kê 2.3 với bốn hướng dẫn - Opcode Load_Fast tải giá trị của đối số của nó ( 65 trong trường hợp này) vào ngăn xếp đánh giá (giá trị). Máy ảo Python là một máy ảo dựa trên ngăn xếp, vì vậy các giá trị cho các hoạt động và kết quả từ các hoạt động trực tiếp trên một ngăn xếp. Opcode 66 sau đó bật hai mục từ ngăn xếp giá trị, thực hiện phép nhân nhị phân trên cả hai giá trị và đặt kết quả trở lại vào ngăn xếp giá trị. Opcode 67 bật một giá trị từ ngăn xếp, đặt đối tượng giá trị trả về cho giá trị này và thoát ra khỏi vòng thông dịch. Từ việc tháo gỡ trong Danh sách 2.3, một điều khá rõ ràng là lời giải thích khá đơn giản này về hoạt động của vòng lặp phiên dịch để loại bỏ rất nhiều chi tiết. Một vài trong số những câu hỏi nổi bật này có thể bao gồm. Sau khi thực hiện mô-đun, chức năng 19 tiếp tục với quy trình dọn dẹp. Giống như 22 thực hiện khởi tạo trong quá trình khởi động phiên dịch, 70 được gọi để thực hiện một số công việc dọn dẹp; Quá trình dọn dẹp này liên quan đến việc chờ đợi các luồng thoát ra, gọi bất kỳ móc thoát nào, giải phóng bất kỳ bộ nhớ nào được phân bổ bởi trình thông dịch vẫn đang được sử dụng, v.v., mở đường cho thông dịch viên thoát ra. Trên đây là một tổng quan cấp cao về các quy trình liên quan đến việc thực hiện mô-đun Python. Rất nhiều chi tiết bị bỏ lại ở giai đoạn này, nhưng tất cả sẽ được tiết lộ trong các chương tiếp theo. Chúng tôi tiếp tục trong chương tiếp theo với một mô tả về quy trình biên dịch. 3. Biên soạn mã nguồn PythonCompiling Python Source CodeMặc dù hầu hết mọi người có thể không coi Python là ngôn ngữ được biên dịch, nhưng nó là một. Trong quá trình biên dịch, trình thông dịch tạo mã byte có thể thực thi từ mã nguồn Python. Tuy nhiên, quy trình biên dịch Python sườn là một quy trình tương đối đơn giản. Nó liên quan đến các bước sau đây theo thứ tự.
Mã nguồn phân tích mã nguồn vào cây phân tích và tạo AST từ một phân tích như vậy là một quá trình tiêu chuẩn và Python không đưa bất kỳ sắc thái phức tạp nào, vì vậy trọng tâm của chương này là chuyển đổi AST thành biểu đồ luồng điều khiển và phát xạ của Đối tượng mã từ biểu đồ luồng điều khiển. Đối với bất kỳ ai quan tâm đến Parse Tree và AST Generation, The Dragon Book cung cấp một tour du lịch chuyên sâu của cả hai chủ đề. 3.1 Từ nguồn này sang cây khácFrom Source To Parse TreeTrình phân tích cú pháp Python là trình phân tích cú pháp LL (1) dựa trên mô tả của các trình phân tích cú pháp như vậy trong cuốn sách rồng. Mô-đun 71 chứa đặc điểm ngữ pháp của biểu mẫu Backus-Naur (EBNF) mở rộng của ngôn ngữ Python. Danh sách 3.0 là một mặt cắt ngang của ngữ pháp này.Liệt kê 3.0: Một phần chéo của ngữ pháp Python BNF
Hàm 52 trong 73 là điểm nhập để phân tích bất kỳ mô-đun nào được truyền cho trình thông dịch tại dòng lệnh. Hàm này gọi hàm 74 chịu trách nhiệm tạo mã thông báo từ các mô -đun được cung cấp. 3.2 Mã thông báo PythonPython tokensMã nguồn Python bao gồm các mã thông báo. Ví dụ: 75 là mã thông báo từ khóa; 76 là mã thông báo số theo nghĩa đen. Mã thông báo, phân tách mã nguồn thành mã thông báo cấu thành, là nhiệm vụ đầu tiên trong quá trình phân tích cú pháp. Các mã thông báo từ bước này rơi vào các loại sau.
Một nhóm các mã thông báo được phân định bởi mã thông báo mới tạo nên một dòng logic; Do đó, chúng tôi có thể nói rằng một chương trình Python bao gồm một chuỗi các dòng logic. Mỗi dòng logic này bao gồm một số dòng vật lý được kết thúc bởi một chuỗi cuối dòng. Hầu hết các lần, các dòng logic đều bản đồ đến các dòng vật lý, vì vậy chúng tôi có một dòng logic được phân định bởi các ký tự cuối dòng. Những dòng logic này thường ánh xạ tới các câu lệnh Python. Báo cáo ghép có thể trải rộng nhiều dòng vật lý; ngoặc đơn, dấu ngoặc vuông hoặc niềng răng xoăn xung quanh một tuyên bố ngầm tham gia các dòng logic tạo nên tuyên bố như vậy. Mặt khác, ký tự dấu gạch chéo ngược là cần thiết để tham gia nhiều dòng logic một cách rõ ràng.NEWLINE token makes up a logical line; hence we could say that a Python program consists of a sequence of logical lines. Each of these logical lines consists of several physical lines that are each terminated by an end-of-line sequence. Most times, logical lines map to physical lines, so we have a logical line delimited by end-of-line characters. These logical lines usually map to Python statements. Compound statements may span multiple physical lines; parenthesis, square brackets or curly braces around a statement implicitly joins the logical lines that make up such statement. The backslash character, on the other hand, is needed to join multiple logical lines explicitly. Th thụt lề cũng đóng một vai trò trung tâm trong việc nhóm các tuyên bố Python. Do đó, một trong những dòng trong ngữ pháp Python là 88 vì vậy một nhiệm vụ quan trọng của tokenizer tạo ra các mã thông báo 89 và 90 đi vào cây phân tích. Tokenizer sử dụng một thuật toán tương tự như trong danh sách 3.1 để tạo các mã thông báo 91 và 92 này.Liệt kê 3.1: Thuật toán thụt python
Hàm 74 trong 94 quét tệp nguồn từ trái sang phải và từ trên xuống dưới mã thông báo nội dung tệp và sau đó xuất ra cấu trúc 95. Các ký tự không gian trắng khác ngoài Kẻ hủy diệt phục vụ để phân định mã thông báo nhưng không bắt buộc. Trong các trường hợp mơ hồ như trong 96, một mã thông báo bao gồm chuỗi dài nhất có thể tạo thành một mã thông báo hợp pháp từ trái sang phải; Trong ví dụ này, các mã thông báo là nghĩa đen 76, toán tử 77 và nghĩa đen 76. Cấu trúc tokenizer được tạo bởi hàm 74 được chuyển đến hàm 01 cố gắng xây dựng một cây phân tích theo ngữ pháp Python trong đó liệt kê 3.0 là một tập hợp con. Khi trình phân tích cú pháp gặp phải một mã thông báo vi phạm ngữ pháp Python, nó sẽ tăng ngoại lệ 02. Mô -đun 03 cung cấp quyền truy cập hạn chế vào cây phân tích của một khối mã Python và liệt kê 3.2 là một trình diễn cơ bản.Liệt kê 3.2: Sử dụng mô -đun phân tích cú pháp để lấy cây phân tích của mã Python
Cuộc gọi 04 trong Liệt kê 3.2 Trả về một biểu diễn trung gian của một cây phân tích 05 trong khi cuộc gọi đến 06 trả về cây phân tích được biểu thị bằng danh sách Python - mỗi danh sách đại diện cho một nút của cây Parse. Các mục đầu tiên trong mỗi danh sách, số nguyên, xác định quy tắc sản xuất trong ngữ pháp Python chịu trách nhiệm cho nút đó. Hình 3.0: Một cây phân tích để liệt kê 3.2 (một hàm trả về chuỗi ‘Xin chào thế giới) Hình 3.0 là một sơ đồ cây của cùng một cây phân tích từ Liệt kê 3.2 với một số mã thông báo bị tước đi, và người ta có thể dễ dàng thấy phần ngữ pháp dễ dàng hơn của từng giá trị số nguyên. Các quy tắc sản xuất này đều được chỉ định trong các tệp tiêu đề 07 (thiết bị đầu cuối) và 08 (thiết bị đầu cuối). 3.3 Từ cây phân tích đến cây cú pháp trừu tượngFrom Parse Tree To Abstract Syntax TreeCây Parse dày đặc với thông tin về cú pháp Python, và tất cả các thông tin đó như cách các dòng được phân định là không liên quan để tạo mã byte. Đây là nơi cây cú pháp trừu tượng (AST) xuất hiện. Cây cú pháp trừu tượng là một đại diện của mã độc lập với các kết quả của cú pháp Python. Ví dụ, một cây phân tích chứa các cấu trúc cú pháp như các nút đại tràng và newline, như trong Hình 3.0, nhưng AST không bao gồm cấu trúc cú pháp như trong danh sách 3.4. Việc chuyển đổi cây phân tích thành cây cú pháp trừu tượng là bước tiếp theo trong đường ống biên dịch. Liệt kê 3.4: Sử dụng mô -đun09 để thao tác với AST của mã nguồn Python
Python sử dụng ngôn ngữ định nghĩa cú pháp Tóm tắt Zephyr (ASDL) và các định nghĩa ASDL của các cấu trúc Python khác nhau có trong tệp 10 tệp. Liệt kê 3.5 là một đoạn của định nghĩa ASDL của câu lệnh Python.Liệt kê 3.5: Một đoạn định nghĩa ASDL của câu lệnh Python
Hàm 11 trong 12 gọi 13 cũng trong 12 hướng dẫn các nút cây phân tích khác nhau và tạo các nút AST phù hợp bằng cách sử dụng các hàm được xác định trong 12. Trái tim của hàm này là một câu lệnh chuyển đổi lớn gọi các hàm cụ thể của nút trên mỗi loại nút. Ví dụ: mã chịu trách nhiệm tạo nút 16 cho biểu thức 17 nằm trong danh sách 3.7.Liệt kê 3.7: Chức năng tạo nút AST cho biểu thức IF Liệt kê 3.8: Chức năng Python đơn giản
Lấy mã trong Liệt kê 3.8, ví dụ, việc chuyển đổi cây phân tích cú pháp của nó thành AST sẽ dẫn đến AST tương tự như Hình 3.1. Hình 3.1: AST để liệt kê 3.2 Mô -đun 09 đi kèm với trình thông dịch Python cung cấp cho chúng tôi khả năng thao tác với Python AST. Các công cụ như 19 có thể lấy biểu diễn 16 trong Python và xuất mã nguồn Python tương ứng. Với AST được tạo, bước tiếp theo là tạo bảng ký hiệu. 3.4 Xây dựng bảng biểu tượngBuilding The Symbol TableBảng ký hiệu, như tên cho thấy, là một tập hợp các biểu tượng và bối cảnh sử dụng của chúng trong một khối mã. Xây dựng bảng biểu tượng liên quan đến việc phân tích và gán phạm vi cho các tên trong một khối mã. Hàm 21 trong 22 đi bộ AST để tạo bảng ký hiệu. Đây là một quá trình hai bước được tóm tắt trong danh sách 3.12.Liệt kê 3.12: Tạo bảng ký hiệu từ AST 0 Đầu tiên, chúng tôi truy cập từng nút của 16 để xây dựng một bộ sưu tập các biểu tượng được sử dụng. Sau lần vượt qua đầu tiên, các mục bảng ký hiệu chứa tất cả các tên đã được sử dụng trong mô -đun, nhưng nó không có thông tin theo ngữ cảnh về các tên đó. Ví dụ, trình thông dịch không thể biết nếu một biến nhất định là biến toàn cầu, cục bộ hoặc miễn phí. Hàm 24 trong 25 xử lý giai đoạn thứ hai. Trong giai đoạn này, thuật toán gán phạm vi (cục bộ, toàn cầu hoặc miễn phí) cho các biểu tượng được thu thập từ đường chuyền đầu tiên. Các ý kiến trong 25 khá nhiều thông tin và được diễn giải bên dưới để cung cấp một số hiểu biết sâu sắc về giai đoạn thứ hai của quy trình xây dựng bảng biểu tượng.
Ví dụ: bảng ký hiệu cho một mô -đun có nội dung trong danh sách 3.16 sẽ chứa ba mục bảng biểu tượng. Liệt kê 3.16: một chức năng đơn giản1 Mục đầu tiên là của mô -đun kèm theo và nó sẽ có 28 được xác định với phạm vi cục bộ. Mục nhập bảng biểu tượng tiếp theo sẽ là chức năng 28 và điều này sẽ có các tên 30 và 31 được đánh dấu là cục bộ. Mục nhập bảng ký hiệu cuối cùng sẽ là chức năng 31 lồng nhau. Mục nhập này sẽ có biến số được đánh dấu là 33. Một điều cần lưu ý là mặc dù 28 có phạm vi cục bộ trong mục nhập bảng ký hiệu cho khối mô -đun, nhưng nó được xác định toàn cầu trong khối mã mô -đun vì trường 35 của bảng biểu tượng trỏ đến mục biểu tượng 36 các mô -đun kèm theo. 3.5 Từ AST đến mã đối tượngFrom AST To Code ObjectsSau khi tạo bảng ký hiệu, bước tiếp theo là tạo các đối tượng mã. Các chức năng cho bước này nằm trong mô -đun 22. Đầu tiên, họ chuyển đổi 16 thành các khối cơ bản của các hướng dẫn mã byte python. Các khối cơ bản là các khối mã có một mục nhập duy nhất nhưng có thể có nhiều lối ra. Thuật toán ở đây sử dụng một mẫu tương tự như được sử dụng để tạo bảng ký hiệu. Các hàm có tên 39, trong đó 40 là loại nút, truy cập đệ quy từng nút của AST phát ra các hướng dẫn mã byte trong quá trình. Chúng tôi thấy một số ví dụ về các chức năng này trong các phần tiếp theo. Các khối của mã byte ở đây hoàn toàn biểu thị một biểu đồ, biểu đồ luồng điều khiển. Biểu đồ này hiển thị các đường dẫn thực thi mã tiềm năng. Trong bước thứ hai, thuật toán làm phẳng biểu đồ luồng điều khiển bằng cách sử dụng độ sâu tìm kiếm đầu tiên theo thứ tự. Sau đó, các lần bù nhảy được tính toán và sử dụng làm đối số hướng dẫn cho các hướng dẫn bytecode 41. Các hướng dẫn bytecode sau đó được sử dụng để tạo một đối tượng mã. Các khối cơ bảnKhối cơ bản là trung tâm để tạo các đối tượng mã. Một khối cơ bản là một chuỗi các hướng dẫn có một điểm nhập nhưng nhiều điểm thoát. Liệt kê 3.19 là định nghĩa của cấu trúc dữ liệu 42.Liệt kê 3.19: Dữ liệu 43 2 Các trường thú vị ở đây là 44 là danh sách được liên kết của tất cả các khối cơ bản được phân bổ trong quá trình biên dịch, 45 là một loạt các hướng dẫn trong khối cơ bản và 46 là luồng cơ bản tiếp theo đạt được bằng cách thực hiện luồng điều khiển thông thường. Mỗi hướng dẫn có một cấu trúc được hiển thị trong danh sách 3.20 giữ một lệnh bytecode. Các hướng dẫn bytecode này nằm trong tệp tiêu đề 47.Liệt kê 3.20: Dữ liệu 48 3 Để minh họa cách trình thông dịch tạo các khối cơ bản này, chúng tôi sử dụng hàm trong Liệt kê 3.11. Việc biên dịch AST của nó được hiển thị trong Hình 3.2 thành CFG dẫn đến biểu đồ tương tự như trong Hình 3.4 - điều này chỉ hiển thị các khối với các hướng dẫn. Việc kiểm tra biểu đồ này cung cấp một số trực giác đằng sau các khối cơ bản. Một số khối cơ bản có một điểm nhập duy nhất, nhưng một số khác có nhiều lối ra. Các khối này được mô tả chi tiết hơn tiếp theo. Hình 3.4: Biểu đồ luồng điều khiển cho hàm FizzBuzz trong Liệt kê 3.11.Liệt kê 3.21: Biên soạn câu lệnh 17 4 Phần thân của chức năng trong danh sách 3.13 là một câu lệnh 17 như có thể nhìn thấy trong Hình 3.2. Đoạn trích trong danh sách 3.21 là hàm biên dịch câu lệnh 17 16 vào các khối cơ bản. Khi hàm này biên dịch nút câu lệnh 17 của chúng tôi, câu lệnh 54 trên dòng 20 được thực thi. Đầu tiên, nó tạo ra một khối cơ bản mới cho một nút 54 nếu như vậy tồn tại. Sau đó, nó truy cập vào mệnh đề bảo vệ của nút câu lệnh 17. Những gì chúng ta có trong chức năng trong danh sách 3.11 rất thú vị vì mệnh đề Guard là một biểu thức boolean có thể kích hoạt một bước nhảy trong khi thực hiện. Liệt kê 3.22 là hàm biên dịch biểu thức boolean.Liệt kê 3.22: Biên soạn một tuyên bố Boolean 5 Mã lên đến vòng lặp ở dòng 20 là thẳng về phía trước. Trong vòng lặp, trình biên dịch truy cập từng biểu thức và sau mỗi lần truy cập, nó thêm một 41. Điều này là do đánh giá ngắn mạch được sử dụng bởi Python. Điều đó có nghĩa là khi một hoạt động boolean như 58 đánh giá thành 59, trình thông dịch bỏ qua các biểu thức khác và thực hiện một bước nhảy để tiếp tục thực hiện. Trình biên dịch biết nơi cần nhảy đến nếu cần vì các hướng dẫn sau đi vào một khối cơ bản mới - việc sử dụng 60 thực thi điều này. Vì vậy, chúng tôi có hai khối bây giờ. Sau khi truy cập thử nghiệm, hàm 61 sẽ thêm lệnh nhảy cho câu lệnh 17, sau đó biên dịch phần thân của câu lệnh 17. Hãy nhớ lại rằng sau khi truy cập biểu thức Boolean, trình biên dịch đã tạo ra một khối cơ bản mới. Khối này chứa các bước nhảy và hướng dẫn cho phần thân của câu lệnh 17, một lợi nhuận đơn giản trong trường hợp này. Mục tiêu của bước nhảy này là khối tiếp theo sẽ nắm giữ nhánh 65 của câu lệnh IF. Bước tiếp theo là biên dịch thành phần 65 của câu lệnh IF nhưng trước đó, trình biên dịch gọi hàm 60 để kích hoạt khối 68. Cánh tay 69 chỉ là một tuyên bố 17 khác, do đó hàm 61 được gọi lại. Lần này trong suốt bài kiểm tra của 17 là một hoạt động so sánh. Đây là một so sánh duy nhất, do đó không có bước nhảy nào liên quan và không có khối mới, do đó, trình thông dịch phát ra mã byte để so sánh các giá trị và trả về để biên dịch phần thân của câu lệnh 17. Quá trình tương tự tiếp tục cho cánh tay 69 cuối cùng dẫn đến CFG trong Hình 3.3. Hình 3.3 cho thấy hàm 75 có thể thoát khỏi khối 1 theo hai cách. Đầu tiên là thông qua thực hiện nối tiếp tất cả các hướng dẫn trong Khối 1 sau đó tiếp tục trong khối 2. Cái còn lại thông qua lệnh 41 sau thao tác so sánh đầu tiên. Mục tiêu của bước nhảy này là khối 3, nhưng một đối tượng mã thực thi không biết gì về các khối cơ bản - đối tượng mã có một luồng byte được lập chỉ mục với độ lệch. Chúng tôi phải cung cấp các hướng dẫn nhảy với độ lệch vào luồng lệnh bytecode của các mục tiêu nhảy. Lắp ráp các khối cơ bảnHàm 77 trong 22 tuyến tính hóa CFG và tạo đối tượng mã từ CFG tuyến tính hóa. Nó làm như vậy bằng cách tính toán các phần bù hướng dẫn cho các mục tiêu nhảy và sử dụng các mục tiêu này làm đối số cho các hướng dẫn nhảy. Đầu tiên, chức năng lắp ráp, trong trường hợp này, thêm các hướng dẫn cho câu lệnh 79 Vì câu lệnh cuối cùng của hàm không phải là câu lệnh 80 - bây giờ bạn biết lý do tại sao bạn có thể xác định các phương thức mà không cần thêm câu lệnh 80. Tiếp theo, nó làm hỏng CFG bằng cách sử dụng một lần đi ngang trước theo thứ tự-Traversal sau đơn đặt hàng đến thăm trẻ em của một nút trước khi truy cập nút. Cấu trúc dữ liệu trình biên dịch giữ biểu đồ phẳng, 82, trong mảng 83 để xử lý thêm. Tiếp theo, nó tính toán các độ lệch hướng dẫn và sử dụng các mục tiêu này làm mục tiêu cho các hướng dẫn nhảy bytecode. Hàm 84 trong danh sách 3.24 xử lý việc này. Hàm 84 trong danh sách 3.24 tương đối đơn giản. Trong 86 ở dòng 10, nó tính toán độ lệch vào luồng lệnh cho mọi hướng dẫn (gần giống với một chỉ mục mảng). Trong 86 tiếp theo ở dòng 17, nó sử dụng các độ lệch được tính toán làm đối số cho các hướng dẫn nhảy phân biệt giữa các bước nhảy tuyệt đối và tương đối.Liệt kê 3.24: Tính toán mã bytepode 6 Với các hướng dẫn bù được tính toán và nhảy bù được lắp ráp, trình biên dịch phát ra các hướng dẫn có trong biểu đồ phẳng theo thứ tự sau ngược từ đường truyền. Thứ tự bài ngược là một phân loại tôpô của CFG. Điều này có nghĩa là cho mỗi cạnh từ đỉnh U đến đỉnh v, u đến trước v theo thứ tự sắp xếp. Điều này là hiển nhiên; Chúng tôi muốn một nút nhảy đến một nút khác để luôn luôn đến trước mục tiêu nhảy đó. Sau khi phát ra các hướng dẫn bytecode, trình biên dịch sẽ tạo các đối tượng mã cho mỗi khối mã bằng cách sử dụng mã byte được phát ra và thông tin có trong bảng ký hiệu. Đối tượng mã được tạo được trả về hàm gọi đánh dấu sự kết thúc của quá trình biên dịch. 4. Đối tượng PythonPython ObjectsTrong chương này, chúng tôi xem xét các đối tượng Python và việc triển khai của chúng trong máy ảo Cpython. Đây là trung tâm để hiểu máy ảo Python. Hầu hết các nguồn được tham chiếu trong chương này đều có sẵn trong các thư mục 88 và 89. Không có gì đáng ngạc nhiên, việc triển khai hệ thống đối tượng Python khá phức tạp, vì vậy chúng tôi cố gắng tránh bị sa lầy trong các chi tiết đẫm máu về việc triển khai 08. Để khởi động điều này, chúng tôi bắt đầu bằng cách nhìn vào cấu trúc 38 - công việc của hệ thống đối tượng Python. 4.1 PyobjectPyObjectMột kiểm tra chữ thảo của mã nguồn cpython cho thấy tính phổ biến của cấu trúc 38. Như chúng ta sẽ thấy sau này trong chuyên luận này, tất cả các đối tượng ngăn xếp giá trị được thông dịch sử dụng trong quá trình đánh giá là 38s. Vì muốn có một thuật ngữ tốt hơn, chúng tôi gọi đây là siêu lớp của tất cả các đối tượng Python. Các giá trị không bao giờ được khai báo là 38 nhưng một con trỏ đến bất kỳ đối tượng nào có thể được chuyển đến 38. Trong thuật ngữ Layman, bất kỳ đối tượng nào cũng có thể được coi là cấu trúc 38 vì phân đoạn ban đầu của tất cả các đối tượng là cấu trúc 38. Liệt kê 4.0 là một định nghĩa về cấu trúc 38. Cấu trúc này bao gồm một số trường phải được lấp đầy cho một giá trị được coi là một đối tượng.Liệt kê 4.0: Định nghĩa Pyobject 7 99 khi có mặt là macro 08 xác định các trường trỏ đến đối tượng được phân bổ trước đó và đối tượng tiếp theo, do đó tạo thành một danh sách liên kết gấp đôi của tất cả các đối tượng trực tiếp. Trường 01 là để quản lý bộ nhớ, trong khi 02 là một con trỏ đến đối tượng loại cho đối tượng đã cho. Loại này xác định những gì dữ liệu thể hiện, loại dữ liệu mà nó chứa và loại hoạt động mà đối tượng hỗ trợ. Lấy đoạn trích trong liệt kê 4.1 chẳng hạn, tên, 03, trỏ đến một đối tượng chuỗi và loại đối tượng là Hồi Str.Danh sách 4.1: Tuyên bố biến trong Python 8 Một câu hỏi hợp lệ từ đây là nếu trường Loại trỏ đến đối tượng 04 thì trường 02 của đối tượng 04 chỉ ra là gì? 07 cho một đối tượng loại đề cập đến cách tự đề cập đến chính nó do đó câu nói rằng loại 04 là 04. Các loại trong VM được triển khai bằng cấu trúc dữ liệu 10 được xác định trong mô -đun 11. Đây là một c 12 với các trường cho hầu hết các chức năng hoặc bộ sưu tập các chức năng được điền theo từng loại. Chúng tôi xem xét cấu trúc dữ liệu này tiếp theo. 4.2 Các loại mổ xẻDissecting TypesCấu trúc 10 được xác định trong 14 đóng vai trò là cấu trúc cơ sở của tất cả các loại Python. Cấu trúc dữ liệu xác định một số lượng lớn các trường chủ yếu là con trỏ đến các chức năng C thực hiện một số chức năng cho một loại nhất định. Liệt kê 4.2 là định nghĩa cấu trúc 10.all Python types. The data structure defines a large number of fields that are mostly pointers to C functions that implement some functionality for a given type. Listing 4.2 is the 10 structure definition.Liệt kê 4.2: Định nghĩa PytypeObject 9 Trường 16 là một phần mở rộng của trường 38 được thảo luận trong phần trước; Phần mở rộng này thêm một trường 18 cho các đối tượng có khái niệm về độ dài. Tài liệu API Python C chứa một mô tả về từng trường trong cấu trúc đối tượng này. Điều quan trọng cần lưu ý là các trường trong cấu trúc mỗi thực hiện một phần của hành vi loại. Hầu hết các trường này là một phần của những gì chúng ta có thể gọi là giao diện hoặc giao thức đối tượng; Các loại thực hiện các chức năng này nhưng theo một cách cụ thể. Ví dụ: trường 19 là tham chiếu đến hàm băm cho một loại đã cho - trường này có thể được để lại mà không có giá trị trong trường hợp các trường hợp của loại không thể băm; Bất cứ hàm nào nằm trong trường 19 đều được gọi khi phương thức 21 được gọi trên một thể hiện của loại đó. Đối tượng loại cũng có trường - 22 tham chiếu các phương thức duy nhất cho loại đó. Khe khe 23 đề cập đến một hàm tạo ra các thể hiện mới của loại và như vậy. Một số trong các trường này, chẳng hạn như 24, là tùy chọn - không phải mọi loại đều cần chạy chức năng khởi tạo, đặc biệt là khi loại là bất biến, chẳng hạn như bộ dữ liệu. Ngược lại, các lĩnh vực khác, chẳng hạn như 23, là bắt buộc. Ngoài ra, trong số các lĩnh vực này là các trường cho các giao thức Python khác, chẳng hạn như sau.
Tiếp theo, chúng tôi xem một vài đối tượng loại là nghiên cứu trường hợp về cách các trường đối tượng loại được điền. 4.3 Loại nghiên cứu trường hợp đối tượngType Object Case Studies Loại
1 stmt : simple_stmt | compound_stmt
2 simple_stmt : small_stmt ( ';' small_stmt ) * [ ';' ] NEWLINE
3 small_stmt : ( expr_stmt | del_stmt | pass_stmt | flow_stmt |
4 import_stmt | global_stmt | nonlocal_stmt | assert_stmt )
5 expr_stmt : testlist_star_expr ( augassign ( yield_expr | testlist ) |
6 ( '=' ( yield_expr | testlist_star_expr )) * )
7 testlist_star_expr : ( test | star_expr ) ( ',' ( test | star_expr )) * [ ',' ]
8 augassign : ( '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^='
9 | '<<=' | '>>=' | '**=' | '//=' )
10
11 del_stmt : 'del' exprlist
12 pass_stmt : 'pass'
13 flow_stmt : break_stmt | continue_stmt | return_stmt | raise_stmt |
14 yield_stmt
15 break_stmt : 'break'
16 continue_stmt : 'continue'
17 return_stmt : 'return' [ testlist ]
18 yield_stmt : yield_expr
19 raise_stmt : 'raise' [ test [ 'from' test ]]
20 import_stmt : import_name | import_from
21 import_name : 'import' dotted_as_names
22 import_from : ( 'from' (( '.' | '...' ) * dotted_name | ( '.' | '...' ) + )
23 'import' ( '*' | '(' import_as_names ')' | import_as_names ))
24 import_as_name : NAME [ 'as' NAME ]
25 dotted_as_name : dotted_name [ 'as' NAME ]
26 import_as_names : import_as_name ( ',' import_as_name ) * [ ',' ]
27 dotted_as_names : dotted_as_name ( ',' dotted_as_name ) *
28 dotted_name : NAME ( '.' NAME ) *
29 global_stmt : 'global' NAME ( ',' NAME ) *
30 nonlocal_stmt : 'nonlocal' NAME ( ',' NAME ) *
31 assert_stmt : 'assert' test [ ',' test ]
32
33 ...
|