Làm thế nào để bạn tìm thấy mã byte trong python?

Python là một ngôn ngữ thông dịch. Phiên dịch có nghĩa là mã nguồn được dịch sang mã máy khi chương trình chạy. Nhưng đó là một sự đơn giản hóa tổng thể

Python về cơ bản có bốn bước để chạy mã nguồn của bạn. lexer, trình phân tích cú pháp, trình biên dịch và trình thông dịch. Lexer và trình phân tích cú pháp là hiển nhiên. Nó được sử dụng để tạo cây cú pháp trừu tượng. Trình biên dịch có thể gây sốc cho hầu hết mọi người

Giống như hầu hết các ngôn ngữ thông dịch khác, Python ban đầu biên dịch mã nguồn do chúng tôi viết sang định dạng trung gian

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
8

Mã byte là một tập lệnh cấp thấp được gọi là chứa tập lệnh có thể được giải thích bởi

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
9. Như bạn có thể thấy có cả trình biên dịch và trình thông dịch

Nhưng lý do Python được gọi là ngôn ngữ thông dịch là vì hầu hết công việc được thực hiện bởi trình thông dịch. Có thể đọc và hiểu bytecode sẽ giúp bạn tối ưu code cũng như hiểu Python hơn

Chúng tôi có một kịch bản đơn giản

$ ls __pycache__/
hello.cpython-38.pyc
0

def hello():
print(“Hello World!”)
hello()

Hãy thử chạy tập lệnh

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s

Khi bạn chạy tệp, thỉnh thoảng bạn sẽ thấy thư mục con

$ ls __pycache__/
hello.cpython-38.pyc
1. Nếu bạn nhìn vào bên trong nó, bạn sẽ thấy tên tệp mã nguồn của mình với phần mở rộng
$ ls __pycache__/
hello.cpython-38.pyc
2

$ ls __pycache__/
hello.cpython-38.pyc

Trong trường hợp bạn không nhìn thấy thư mục. Bạn có thể tự biên dịch tệp bằng CLI

________số 8_______

Nếu bạn chạy lại nó, bạn sẽ thấy thời gian thực thi sẽ được cải thiện một chút vì Python không phải phân tích lại mã nguồn của chúng tôi mỗi khi nó chạy

$ time python3 a.py
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s

Bạn thậm chí có thể chạy trực tiếp tệp

$ ls __pycache__/
hello.cpython-38.pyc
3

$ time  __pycache__/hello.cpython-38.pyc
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s

Tháo rời mã byte

Sử dụng

$ ls __pycache__/
hello.cpython-38.pyc
4 để xem nội dung của
$ ls __pycache__/
hello.cpython-38.pyc
5 sẽ cung cấp cho bạn các giá trị rác. Chúng tôi có thể thấy mã byte theo nghĩa đen của mã của chúng tôi bằng cách sử dụng
$ ls __pycache__/
hello.cpython-38.pyc
6 để ban đầu chuyển mã nguồn thành AST, sau đó là
$ ls __pycache__/
hello.cpython-38.pyc
7. Do đó, mã được biên dịch sau này có thể được chạy bởi
$ ls __pycache__/
hello.cpython-38.pyc
8 hoặc
$ ls __pycache__/
hello.cpython-38.pyc
9

>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!

Ở đây,

$ python3 -m compileall hello.py
0 ngụ ý rằng mã nguồn chứa nhiều câu lệnh Python

Bạn có thể xem mã byte cho các giá trị đã được biên dịch bằng cách sử dụng

$ ls __pycache__/
hello.cpython-38.pyc
7

>>> hello.__code__.co_code
b’t\x00d\x01\x83\x01\x01\x00d\x00S\x00'

Để xem

$ python3 -m compileall hello.py
2 theo nghĩa đen, chúng ta có thể sử dụng
$ ls __pycache__/
hello.cpython-38.pyc
7

>>> c.co_code
b'd\x00d\x01\x84\x00Z\x00d\x02S\x00'

Hãy cố gắng hiểu điều này. Chúng tôi biết rằng mỗi lệnh trong Python bao gồm hai byte ở định dạng sau

opcode oparg

$ python3 -m compileall hello.py
4 là lệnh một byte. Trong khi
$ python3 -m compileall hello.py
5 là đối số dành riêng cho hướng dẫn

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
0

Một điều sẽ ngay lập tức đến với bạn khi bạn thấy điều này là nó chẳng có ý nghĩa gì đối với con người chúng ta. Vì vậy, chúng tôi có một thứ gọi là

$ python3 -m compileall hello.py
6, tên thân thiện với con người của
$ python3 -m compileall hello.py
7. Để tìm tên, chúng ta có thể kiểm tra danh sách
$ python3 -m compileall hello.py
8 trong mô-đun
$ python3 -m compileall hello.py
9

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
1

Thay vì tổ chức opcode và oparg để xem hướng dẫn mã byte Chúng ta có thể phân tích trực tiếp đối tượng python thành dạng mã byte có thể đọc được bằng cách sử dụng mô-đun

$ time python3 a.py
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
0

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
2

Ở đây, số 2 ở bên trái biểu thị số dòng

đồng thuộc tính

Có rất nhiều thuộc tính quan trọng được mang bởi

$ time python3 a.py
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
1. Ví dụ

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
3
  • $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    2 là một tuple chứa các thuộc tính và phương thức toàn cục được sử dụng bên trong phạm vi. Hàm in là một hàm toàn cục đang được sử dụng trong hàm hiện tại nên chúng ta có thể thấy nó trong bộ dữ liệu
    $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    3
  • $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    4 là bộ chứa các tên biến cục bộ được sử dụng trong hàm. Không có biến nào được khai báo trong hàm nên bộ dữ liệu trống
  • $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    5 Nó sẽ trả về các chữ được sử dụng bởi bytecode. Trong hàm hello, giá trị theo nghĩa đen duy nhất chúng ta thấy là
    $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    6. Python thêm Không làm một trong các tham số trả về mặc định cho hàm. Điều này sẽ cho phép python xử lý trong trường hợp không tìm thấy câu lệnh return

Có co_attributes khác mà bạn có thể kiểm tra chúng bằng cách sử dụng

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
4

Để biết thông tin về co_attributes, bạn có thể xem chuỗi tài liệu này

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
5

Bây giờ hãy quay lại opcode. Đây là hướng dẫn mà máy ảo python sẽ thực thi chức năng xin chào của chúng tôi

  • $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    7yêu cầu python trả về phần tử vị trí thứ 0 trong
    $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    3và đẩy nó vào ngăn xếp đánh giá. Trong trường hợp này là chức năng in
  • $ time python3 a.py
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    9 sẽ lấy giá trị theo nghĩa đen ở chỉ mục 1 của
    $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    0 và đẩy nó lên đầu. Giá trị là
    $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    1
  • $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    2 yêu cầu python gọi hàm. Nó sẽ bật 1 đối số vị trí từ ngăn xếp. Bên dưới các đối số là đối tượng có thể gọi được. Tôi gọi đối tượng có thể gọi được bằng các đối số đã bật và đẩy giá trị trả về
  • $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    3 loại bỏ đỉnh ngăn xếp mà trong trường hợp hiện tại là chức năng của chúng tôi
  • $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    4 sẽ thêm phần tử vị trí o từ
    $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    5 là Không có vào giá trị cao nhất của ngăn xếp
  • $ time  __pycache__/hello.cpython-38.pyc
    Hello World!
    real 0m0.020s
    user 0m0.008s
    sys 0m0.012s
    6 sẽ trả về đỉnh ngăn xếp cho người gọi hàm

Như bạn có thể nhận thấy, các hướng dẫn mã byte của Python bao gồm thao tác đánh giá ngăn xếp của khung ngăn xếp cuộc gọi hiện tại. Chúng tôi sẽ nhanh chóng lướt qua máy ảo python để hiểu lý do

Cần phải hiểu rằng mã byte có thể không phổ biến giữa nhiều phiên bản Python. Tôi đang sử dụng Python3. 8 cho bài viết của tôi. Bạn có thể tìm hiểu về mã byte cụ thể của phiên bản tại đây

Máy ảo Python

Cpython sử dụng máy ảo dựa trên ngăn xếp. Nó đơn giản và mạnh mẽ. Không có đăng ký. Thay vào đó, chúng ta có thể thêm

$ time  __pycache__/hello.cpython-38.pyc
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
7 một mục vào
$ time  __pycache__/hello.cpython-38.pyc
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
8 của ngăn xếp hoặc
$ time  __pycache__/hello.cpython-38.pyc
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
9 mục đó từ
$ time  __pycache__/hello.cpython-38.pyc
Hello World!
real 0m0.020s
user 0m0.008s
sys 0m0.012s
8. Chúng tôi có
>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
1 là ngăn xếp chính cho chương trình Python. Nó có một mục tên là
>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
2 cho mỗi lệnh gọi hàm đang hoạt động. Đáy ngăn xếp là điểm vào của chương trình

Mỗi lần gọi hàm sẽ thêm một khung mới vào ngăn xếp và mỗi khi hàm gọi trả về, nó sẽ bật lên khung. Trong Python, chúng ta có thể dễ dàng truy cập các khung này bằng mô-đun

>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
3

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
6

Thanh tra trả lại một

>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
4 được gọi là
>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
5. Nó bao gồm
>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
6

ghi chú. Chúng tôi đang thấy

>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
7 vì chúng tôi đang sử dụng thiết bị đầu cuối

Khung hiện tại sẽ luôn ở trên cùng để bạn có thể truy cập khung hiện tại bằng cách sử dụng

>>> c = compile('def hello():\n\tprint("Hello World!")\nhello()', '', "exec")
>>> exec(c)
Hello World!
8

$ time python3 a.py 
Hello World!
real 0m0.038s
user 0m0.025s
sys 0m0.010s
7

Bạn có thể nhận thêm thông tin về khung bằng các thuộc tính đối tượng khung. để biết thêm thông tin tham khảo tại đây

Phần kết luận

Để bài viết ngắn gọn, chúng tôi xin kết thúc tại đây. Còn rất nhiều thứ còn lại nhưng với chừng này bạn có thể bắt đầu khám phá mã byte python

Học cách đọc và viết mã byte Python sẽ giúp bạn hiểu Python hơn và giúp bạn tối ưu hóa mã của mình

Ngoài ra, đây là bước khởi đầu để hiểu cách thức hoạt động của máy ảo. Điều này là dành cho hôm nay. Tôi đã thêm các liên kết hữu ích để học thêm

Bạn sẽ thấy mã byte của chương trình Python như thế nào?

Bạn có thể sử dụng danh sách này. opname để tra cứu tên của các lệnh mã byte từ các giá trị byte thập phân của chúng nếu bạn muốn thử phân tách một hàm theo cách thủ công.

Mã byte Python là gì?

Bytecode là ngôn ngữ trung gian cho máy ảo Python được sử dụng để tối ưu hóa hiệu suất . Thay vì thực thi trực tiếp mã nguồn mà con người có thể đọc được, các mã số nhỏ gọn, hằng số và tham chiếu được sử dụng để biểu thị kết quả phân tích cú pháp trình biên dịch và phân tích ngữ nghĩa.

Mã byte là gì?

Trong hầu hết các hệ thống máy tính, byte là đơn vị dữ liệu dài tám chữ số nhị phân . Một byte là đơn vị mà hầu hết các máy tính sử dụng để biểu thị một ký tự như chữ cái, số hoặc ký hiệu chính tả. Mỗi byte có thể chứa một chuỗi bit cần được sử dụng trong một đơn vị lớn hơn cho mục đích ứng dụng.

Ví dụ mã byte là gì?

Các loại mã byte khác nhau sử dụng cú pháp khác nhau, có thể được đọc và thực thi bởi máy ảo tương ứng. Một ví dụ phổ biến là Java bytecode, được biên dịch từ mã nguồn Java và có thể chạy trên Máy ảo Java (JVM)