Hướng dẫn how do i use bytecode in python? - làm cách nào để sử dụng bytecode trong python?

Tìm hiểu về mã Python byte

Mã nguồn của ngôn ngữ lập trình có thể được thực thi bằng trình thông dịch hoặc trình biên dịch. Trong một ngôn ngữ được biên dịch, một trình biên dịch sẽ dịch mã nguồn trực tiếp thành mã máy nhị phân. Mã máy này dành riêng cho máy đích đó vì mỗi máy có thể có một hệ điều hành và phần cứng khác nhau. Sau khi biên dịch, máy đích sẽ trực tiếp chạy mã máy.

Trong một ngôn ngữ được giải thích, mã nguồn không được chạy trực tiếp bởi máy đích. Có một chương trình khác được gọi là trình thông dịch đọc và thực thi mã nguồn trực tiếp. Trình thông dịch, cụ thể cho máy đích, dịch từng câu lệnh của mã nguồn thành mã máy và chạy nó.

Python thường được gọi là ngôn ngữ được giải thích, tuy nhiên, nó kết hợp biên dịch và diễn giải. Khi chúng tôi thực thi mã nguồn [một tệp có tiện ích mở rộng

python -m compileall file_1.py ... file_n.py
17], trước tiên, Python biên dịch nó thành mã byte. Bytecode là một biểu diễn độc lập với nền tảng thấp của mã nguồn của bạn, tuy nhiên, đó không phải là mã máy nhị phân và không thể được chạy trực tiếp bởi máy đích. Trên thực tế, nó là một tập hợp các hướng dẫn cho một máy ảo được gọi là máy ảo Python [PVM].

Sau khi biên dịch, mã byte được gửi để thực hiện cho PVM. PVM là một trình thông dịch chạy mã byte và là một phần của hệ thống Python. Mã byte là độc lập với nền tảng, nhưng PVM là cụ thể cho máy đích. Việc triển khai mặc định của ngôn ngữ lập trình Python là CPython được viết bằng ngôn ngữ lập trình C. Cpython biên dịch mã nguồn Python vào mã byte và mã byte này sau đó được thực thi bởi máy ảo CPython.

Tạo tập tin bytecode

Trong Python, mã byte được lưu trữ trong tệp

python -m compileall file_1.py ... file_n.py
18. Trong Python 3, các tệp byte được lưu trữ trong một thư mục có tên
python -m compileall file_1.py ... file_n.py
19. Thư mục này được tự động tạo khi bạn cố gắng nhập một tệp khác mà bạn đã tạo:

import file_name

Tuy nhiên, nó sẽ không được tạo nếu chúng tôi không nhập tệp khác trong mã nguồn. Trong trường hợp đó, chúng ta vẫn có thể tạo ra nó theo cách thủ công. Để biên dịch các tệp riêng lẻ

python -m compileall file_1.py ... file_n.py
20 thành
python -m compileall file_1.py ... file_n.py
21 từ dòng lệnh, chúng ta có thể viết:

python -m compileall file_1.py ... file_n.py

Tất cả các tệp

python -m compileall file_1.py ... file_n.py
22 được tạo sẽ được lưu trữ trong
python -m compileall file_1.py ... file_n.py
23Folder. Nếu bạn không cung cấp tên tệp sau
python -m compileall file_1.py ... file_n.py
24, nó sẽ biên dịch tất cả các tệp mã nguồn Python trong thư mục hiện tại.

Chúng ta cũng có thể sử dụng hàm

python -m compileall file_1.py ... file_n.py
25 để biên dịch một chuỗi chứa mã nguồn Python. Cú pháp của chức năng này là:

python -m compileall file_1.py ... file_n.py
26

Chúng tôi chỉ tập trung vào ba đối số đầu tiên được yêu cầu [những đối số khác là tùy chọn].

python -m compileall file_1.py ... file_n.py
27 là mã nguồn để biên dịch có thể là một chuỗi, đối tượng byte hoặc đối tượng AST.
python -m compileall file_1.py ... file_n.py
28 là tên của tệp mà mã nguồn đến từ. Nếu mã nguồn không đến từ một tệp, bạn có thể viết bất cứ thứ gì bạn thích hoặc để lại một chuỗi trống.
python -m compileall file_1.py ... file_n.py
29 có thể là:

python -m compileall file_1.py ... file_n.py
30: Chấp nhận mã nguồn Python dưới mọi hình thức [bất kỳ số lượng câu lệnh hoặc khối]. Nó biên dịch chúng thành một mã byte cuối cùng trả về
python -m compileall file_1.py ... file_n.py
31

python -m compileall file_1.py ... file_n.py
32: Chấp nhận một biểu thức duy nhất và biên dịch nó thành mã byte cuối cùng trả về giá trị của biểu thức đó

python -m compileall file_1.py ... file_n.py
33: Chỉ chấp nhận một câu lệnh duy nhất [hoặc nhiều câu lệnh được phân tách bằng
python -m compileall file_1.py ... file_n.py
34]. Nếu câu lệnh cuối cùng là một biểu thức, thì mã byte kết quả sẽ in, giá trị của biểu thức đó thành đầu ra tiêu chuẩn.

Ví dụ: để biên dịch một số câu lệnh Python chúng ta có thể viết:

s='''
a=5
a+=1
print[a]
'''
compile[s, "", "exec"]

hoặc viết tương đương:

compile["a=5 \na+=1 \nprint[a]", "", "exec"]

Để đánh giá một biểu thức chúng ta có thể viết:

compile["a+7", "", "eval"]

Chế độ này gây ra lỗi nếu bạn không có biểu thức:

# This does not work:
compile["a=a+1", "", "eval"]

Ở đây

python -m compileall file_1.py ... file_n.py
36 không phải là một biểu thức và không trả lại bất cứ điều gì, vì vậy chúng tôi không thể sử dụng chế độ
python -m compileall file_1.py ... file_n.py
37. Tuy nhiên, chúng ta có thể sử dụng chế độ
python -m compileall file_1.py ... file_n.py
38 để biên dịch nó:

compile["a=a+1", "", "single"]

Nhưng những gì được trả lại bởi

python -m compileall file_1.py ... file_n.py
39? Khi bạn chạy chức năng
python -m compileall file_1.py ... file_n.py
39, Python trả về:

Vì vậy, những gì hàm

python -m compileall file_1.py ... file_n.py
39 đang trả về là một đối tượng mã [địa chỉ sau
python -m compileall file_1.py ... file_n.py
42 có thể khác nhau trên máy của bạn].

Đối tượng mã

Hàm

python -m compileall file_1.py ... file_n.py
25 trả về một đối tượng mã Python. Tất cả mọi thứ trong Python là một đối tượng. Ví dụ: chúng tôi xác định một biến số nguyên, giá trị của nó được lưu trữ trong đối tượng
python -m compileall file_1.py ... file_n.py
44 và bạn có thể dễ dàng kiểm tra loại của nó bằng hàm
python -m compileall file_1.py ... file_n.py
45:

a = 5
type[a] # Output is: int

Theo một cách tương tự, mã byte được tạo bởi hàm biên dịch được lưu trữ trong đối tượng

python -m compileall file_1.py ... file_n.py
46.

c = compile["a=a+1", "", "single"]
type[c] # Output is: code

Đối tượng mã không chỉ chứa mã byte mà còn một số thông tin khác cần thiết để cpython chạy mã byte [chúng sẽ được thảo luận sau]. Một đối tượng mã có thể được thực thi hoặc đánh giá bằng cách chuyển nó cho hàm

python -m compileall file_1.py ... file_n.py
47 hoặc
python -m compileall file_1.py ... file_n.py
48. Vì vậy, chúng tôi có thể viết:

python -m compileall file_1.py ... file_n.py
0

Khi bạn xác định một hàm trong Python, nó sẽ tạo một đối tượng mã cho nó và bạn có thể truy cập nó bằng thuộc tính

python -m compileall file_1.py ... file_n.py
49. Ví dụ: chúng ta có thể viết:

python -m compileall file_1.py ... file_n.py
1

Và đầu ra sẽ là:

python -m compileall file_1.py ... file_n.py
2

Giống như bất kỳ đối tượng nào khác, đối tượng mã có một số thuộc tính và để nhận mã byte được lưu trữ trong một đối tượng mã, bạn có thể sử dụng thuộc tính

python -m compileall file_1.py ... file_n.py
50 của nó:

python -m compileall file_1.py ... file_n.py
3

Đầu ra là:

python -m compileall file_1.py ... file_n.py
4

Kết quả là một byte theo nghĩa đen được đặt trước với

python -m compileall file_1.py ... file_n.py
51, nó là một chuỗi byte bất biến và có một loại
python -m compileall file_1.py ... file_n.py
52. Mỗi byte có thể có giá trị thập phân từ 0 đến 255. Vì vậy, một byte theo nghĩa đen là một chuỗi số nguyên bất động trong khoảng từ 0 đến 255. Mỗi byte có thể được hiển thị bởi một ký tự ASCII có mã ký tự giống như giá trị byte hoặc nó có thể Thể hiện bởi một
python -m compileall file_1.py ... file_n.py
53 hàng đầu theo sau là hai ký tự. Escape
python -m compileall file_1.py ... file_n.py
53 hàng đầu có nghĩa là hai ký tự tiếp theo được hiểu là các chữ số hex cho mã ký tự. Ví dụ:

python -m compileall file_1.py ... file_n.py
5

gives:

python -m compileall file_1.py ... file_n.py
6

Vì phần tử đầu tiên có giá trị thập phân là 101 và có thể được hiển thị với ký tự

python -m compileall file_1.py ... file_n.py
55 có mã ký tự ASCII là 101. hoặc::

python -m compileall file_1.py ... file_n.py
7

gives:

python -m compileall file_1.py ... file_n.py
8

Vì phần tử thứ 4 có giá trị thập phân là 131. Giá trị thập lục phân là 131 là 83. Vì vậy, byte này có thể được hiển thị với một ký tự có mã ký tự là

python -m compileall file_1.py ... file_n.py
56.

Các chuỗi byte này có thể được giải thích bằng Cpython, nhưng chúng không thân thiện với con người. Vì vậy, chúng ta cần hiểu làm thế nào các byte này được ánh xạ tới các hướng dẫn thực tế sẽ được thực thi bởi CPython. Trong phần tiếp theo, chúng tôi sẽ tháo rời mã byte thành một số hướng dẫn thân thiện với con người để xem mã byte được thực thi bởi CPython như thế nào.

Chi tiết mã byte

Trước khi đi sâu vào chi tiết, điều quan trọng cần lưu ý là việc triển khai chi tiết mã byte thường thay đổi giữa các phiên bản của Python. Vì vậy, những gì bạn thấy trong bài viết này có thể không hợp lệ cho tất cả các phiên bản của Python. Trên thực tế, nó bao gồm các thay đổi xảy ra trong phiên bản 3.6 và một số chi tiết có thể không hợp lệ cho các phiên bản cũ hơn. Mã trong bài viết này đã được thử nghiệm với Python 3.7.

Mã byte có thể được coi là một loạt các hướng dẫn hoặc một chương trình cấp thấp cho trình thông dịch Python. Sau phiên bản 3.6, Python sử dụng 2 byte cho mỗi lệnh. Một byte là cho mã của hướng dẫn đó được gọi là opcode và một byte được dành riêng cho itargumentwhich được gọi là oparg. Mỗi opcode có một tên thân thiện với con người được gọi là opname. Các hướng dẫn bytecode có định dạng chung như thế này:

python -m compileall file_1.py ... file_n.py
9

Chúng tôi đã có mã hóa trong mã byte của chúng tôi và chúng tôi chỉ cần ánh xạ chúng đến tên opname tương ứng của chúng. Có một mô -đun gọi là

python -m compileall file_1.py ... file_n.py
57 có thể giúp với điều đó. Trong mô -đun này, có một danh sách gọi là
python -m compileall file_1.py ... file_n.py
58 lưu trữ tất cả các tên opnames. Phần tử thứ i của danh sách này cung cấp tên opname cho một lệnh có mã opcode bằng i.

Một số hướng dẫn không cần một đối số, vì vậy họ bỏ qua byte sau opcode. Các opcodes có giá trị dưới một số nhất định bỏ qua đối số của họ. Giá trị này được lưu trữ trong

python -m compileall file_1.py ... file_n.py
59 và hiện bằng 90. Vì vậy, các opcodes> = ________ 159 có một đối số và các opcodes

Bài Viết Liên Quan

Chủ Đề