Yêu cầu. Tất cả các ví dụ đều tương thích với ít nhất Python v3. 6, ngoại trừ việc sử dụng
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
3 với thuộc tính >>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
4 đã được thêm vào trong Python v3. 9Cây cú pháp trừu tượng [AST] là gì?
Cây cú pháp trừu tượng [AST] là cấu trúc dữ liệu được sử dụng để suy luận về ngữ pháp của ngôn ngữ lập trình trong ngữ cảnh của các hướng dẫn được cung cấp trong mã nguồn
Chẳng hạn, trình biên dịch sử dụng AST khi chuyển đổi mã nguồn thành mã nhị phân
Đưa ra một số mã nguồn văn bản, trước tiên trình biên dịch mã hóa văn bản để xác định các từ khóa, biến, chữ, v.v. của ngôn ngữ lập trình. Mỗi mã thông báo đại diện cho một "nguyên tử" của một hướng dẫn
Sau đó, các mã thông báo được sắp xếp lại thành một AST, một cây trong đó các nút là “nguyên tử” của hướng dẫn và sắp xếp các mối quan hệ giữa các nguyên tử dựa trên ngữ pháp ngôn ngữ lập trình. Chẳng hạn, AST làm rõ sự hiện diện của lệnh gọi hàm, các đối số đầu vào có liên quan, các hướng dẫn soạn thảo hàm, v.v.
Sau đó, trình biên dịch có thể áp dụng nhiều tối ưu hóa cho AST và cuối cùng chuyển đổi nó thành mã nhị phân
Bất chấp vai trò của chúng đối với trình biên dịch, AST vẫn hữu ích cho nhiều trường hợp sử dụng hơn. Hãy thảo luận về điều này chi tiết hơn
Mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 Python và cách sử dụng nó
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
Mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 trong thư viện chuẩn Python có thể được sử dụng để tạo, truy cập và sửa đổi AST liên quan đến mã nguồn Python. Nó đã được giới thiệu vào năm 1990 và kể từ đó nó đã phát triển cùng với ngữ pháp PythonNgay cả khi nó là một phần của thư viện tiêu chuẩn trong một thời gian dài, việc sử dụng nó trực tiếp không phổ biến. Thay vào đó, bạn có thể đã sử dụng nó một cách gián tiếp vì các công cụ phổ biến sử dụng nó một cách bí mật
kiểm tra mã.
7 là một công cụ kiểm tra đột biến được sử dụng để thay đổi mã đang được kiểm tra để mở rộng bộ kiểm tra theo cách tự động. Trong thực tế, đột biến là một sửa đổi nhân tạo của AST được tạo từ mã đang được thử nghiệm. Để xem cách PyBites sử dụng>>> code = 'one_plus_two = 1+2 # type: int' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
7, hãy xem bài viết này>>> code = 'one_plus_two = 1+2 # type: int' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
mã số bảo hiểm.
9 là một bộ phân tích mã tĩnh nghiên cứu AST để xác định phần mã không được sử dụng>>> code = 'one_plus_two = 1+2 # type: int' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
lỗ hổng mã.
0 sử dụng biểu diễn AST để xác định các lỗ hổng bảo mậtModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]], type_comment='int']], type_ignores=[]]
tự động hoàn thành mã.
1 là một IDE và công cụ tự động hoàn thành trình soạn thảo văn bản dựa trên chức năng của mô-đunModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]], type_comment='int']], type_ignores=[]]
7 để đánh giá các biểu thức một cách an toànModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
định dạng mã.
3 vàModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]], type_comment='int']], type_ignores=[]]
4 là hai công cụ phổ biến để thực thi định dạng lại mã và cả hai đều sử dụng biểu diễn AST của mã nguồn để áp dụng các quy tắc định dạng của chúngModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]], type_comment='int']], type_ignores=[]]
Sử dụng mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 để điều tra các bài tập về PyBites Bite
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
Vẫn không thuyết phục về sự liên quan của một AST? . hãy xem xét một trường hợp sử dụng thực tế hơn và gần gũi hơn với Nền tảng PyBites
Nền tảng PyBites hiện đang cung cấp hơn 300 bài tập Bite và con số này không ngừng tăng lên. Do mục đích [bán] ẩn của nền tảng là đưa ra một loạt các thử thách bao gồm các mô-đun và chức năng Python khác nhau, việc xác định nội dung của các bài tập đã có sẽ bắt đầu trở nên khó khăn hơn và thay vào đó, nội dung nào còn lại để khám phá
Đây là nơi chúng ta có thể tận dụng mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7. Cụ thể, chúng tôi có thể xử lý mã nguồn của lời giải của các bài tập [do tác giả của các thử thách cung cấp] và khôi phục một số thống kê về nội dung của chúng. Chẳng hạn, các mô-đun phổ biến và các hàm dựng sẵn được sử dụngĐây là một số kết quả. Để theo dõi, hãy xem sổ ghi chép Jupyter này
Mức độ phổ biến của nội trang
Biểu đồ ở trên hiển thị các lệnh gọi nội trang Python được sắp xếp theo mức độ phổ biến của chúng. Nói cách khác, sử dụng mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7, người ta có thể phát hiện khi một lệnh gọi hàm được thực hiện và liệu nó có liên quan đến mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
9 hay không. Ba màu được sử dụng để phân biệt trực quan giữa các loại ngoại lệ, việc tạo các loại cơ sở [Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]],
type_comment='int']],
type_ignores=[]]
9, >>> code = 'one_plus_two = 1+2 # type: ignore'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
0, >>> code = 'one_plus_two = 1+2 # type: ignore'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
1, >>> code = 'one_plus_two = 1+2 # type: ignore'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
2 và >>> code = 'one_plus_two = 1+2 # type: ignore'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
3] hoặc các chức năng khác. Biểu đồ là số đếm tần số chuẩn hóa, i. e. , tần suất xuất hiện của từng phần tử được cộng dồn trong tất cả các bài tập và chia cho tổng tất cả các phần tử xuất hiện trong tất cả các bài tậpMột vài quan sát
Phân phối có đuôi nặng, với
4 đại diện cho 13. 4% của tất cả các cuộc gọi dựng sẵn, trong khi>>> code = 'one_plus_two = 1+2 # type: ignore' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
5 chỉ được sử dụng một lần>>> code = 'one_plus_two = 1+2 # type: ignore' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
Tất cả năm loại cơ sở đều được sử dụng, nhưng
6 chỉ được sử dụng trong 1 thử thách>>> code = 'one_plus_two = 1+2 # type: ignore' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
Chỉ có 5 trường hợp ngoại lệ tiêu chuẩn được sử dụng, với
7 là trường hợp phổ biến nhất>>> code = 'one_plus_two = 1+2 # type: ignore' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
Hầu hết các hàm dựng sẵn đều đã được sử dụng trong các bài tập, nhưng khi xem xét các lời gọi lập trình hàm, bạn có thể nhận thấy rằng _____17_______8 xuất hiện trong khi _____17_______9 thì không [thực ra thông lệ phổ biến là thích dùng hơn]
phổ biến mô-đun
Biểu đồ trên hiển thị xếp hạng cho các mô-đun. Để đơn giản, chúng tôi giới hạn chỉ báo cáo về các mô-đun gốc. Nếu các mô-đun con được sử dụng, tần số của chúng được cộng dồn vào tần số của các mô-đun gốc tương ứng
Như trước đây, biểu đồ có nhiều đuôi, một minh chứng rằng các bài tập PyBites Bite cố gắng “bao quát một chút mọi thứ”
Chúng ta có thể quan sát sự hiện diện của các mô-đun không chuẩn, chẳng hạn như
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
0 và Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
1, cũng như các mô-đun đặc biệt hơn như Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
2 và Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
3 được tạo ra cho mục đích của chính những thách thức đóNgười ta có thể dễ dàng mở rộng phân tích để hiểu các chức năng được sử dụng trong từng mô-đun/mô-đun con, cũng như đi sâu vào phân tích cụ thể hơn. Điều liên quan cần làm nổi bật là các kết quả được báo cáo ở đây được tạo với khoảng 50 dòng mã Python và sử dụng mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7. Xử lý hơn 300 tệp mã nguồn bằng các công cụ như Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
5, Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
6 hoặc bất kỳ công cụ nào khác sẽ khó hơn đáng kểHy vọng rằng các ví dụ này đã cho bạn ý tưởng sơ bộ về những gì bạn có thể đạt được với AST. Bước tiếp theo là hiểu cách tạo cấu trúc dữ liệu như vậy và điều tra thành phần của chúng
Phân tích hướng dẫn bài tập bằng cách sử dụng mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
Để bắt đầu làm quen với mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7, hãy xem điều gì sẽ xảy ra khi chúng ta phân tích một lệnh đơn lẻ. Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
9>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> ast.dump[tree, indent=4]
Điều này sẽ xuất ra
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
Lúc đầu có thể không rõ ràng, nhưng đầu ra được tạo bởi
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
3 thực sự là một cái câyCác từ bắt đầu bằng chữ in hoa là các nút của cây
Các thuộc tính của các nút là các cạnh của cây hoặc siêu dữ liệu
Hãy làm lại đầu ra thành sơ đồ với các quy ước sau
Một hình chữ nhật cho mỗi nút, đánh dấu đậm loại nút liên quan
Thuộc tính nút thu thập siêu dữ liệu được báo cáo bằng màu xanh lam
Các thuộc tính nút khác được chú thích với loại của chúng
Các nút được kết nối dựa trên thuộc tính của chúng
Với hình dung này trong tay, chúng ta có thể quan sát một vài điều
Gốc của cây là một nút
>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
1. Trên thực tế, ngay cả khi ví dụ của chúng tôi là một chương trình dòng đơn, nó vẫn là một mô-đun Python thực sự. Nút chứa hai thuộc tính >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
2 và >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
3. Hãy tạm gác >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
3 sang một bên và tập trung vào >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
2Vì một mô-đun Python chứa một loạt các hướng dẫn, nên thuộc tính
>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
6 là một danh sách các nút, một nút cho mỗi lệnh trong chương trình. Ví dụ của chúng tôi bao gồm một hoạt động chuyển nhượng duy nhất, do đó >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
6 chỉ chứa một nút >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
8Một thao tác gán có một bên phải chỉ định thao tác sẽ thực hiện và một bên trái chỉ định đích của thao tác. Hai bên được liên kết với các thuộc tính
>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
9 và class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
0 của nút >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
8Xem xét phía bên tay phải, thuộc tính
>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
9 là nút class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
3, do lệnh là phép toán nhị phân giữa hai toán hạng, được chỉ định đầy đủ với ba thuộc tính
4 là nútclass BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
5 do chúng tôi đang thực hiện phép cộngclass BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
6 vàclass BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
7 là các toán hạng cộng và bao gồm các nútclass BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
8, mỗi nút giữ giá trị thô trong thuộc tínhclass BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
9class BinOpVisitor[ast.NodeVisitor]: def visit_BinOp[self, node]: print[f"found BinOp at line: {node.lineno}"] self.generic_visit[node]
Xét về phía bên trái, vì Python hỗ trợ nhiều phép gán và giải nén bộ dữ liệu, thuộc tính
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
0 là một danh sách thu thập các đích khác nhau của thao tác. Trong trường hợp của chúng tôi, phép gán dành cho một biến duy nhất, do đó, một nút >>> vis = BinOpVisitor[]
>>> vis.visit[tree]
1 duy nhất được sử dụng. Đổi lại, nút >>> vis = BinOpVisitor[]
>>> vis.visit[tree]
1 có 2 thuộc tính
3 lưu tên của biến được sử dụng trong chương trình [>>> vis = BinOpVisitor[] >>> vis.visit[tree]
4]>>> vis = BinOpVisitor[] >>> vis.visit[tree]
5 chỉ định cách sử dụng tham chiếu biến trong chương trình. Đây chỉ có thể là một trong các loại>>> vis = BinOpVisitor[] >>> vis.visit[tree]
6,>>> vis = BinOpVisitor[] >>> vis.visit[tree]
7 hoặc>>> vis = BinOpVisitor[] >>> vis.visit[tree]
8, nhưng chúng luôn là các nút trống>>> vis = BinOpVisitor[] >>> vis.visit[tree]
Thuộc tính >>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
1 và loại bình luận
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
Thuộc tính
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
1 trong phần lớn các trường hợp sẽ là một danh sách trống. Đây là lý do tại sao trong bản phác thảo được tô màu xanh lam. Để hiểu tại sao lại như vậy và mục đích thực sự của thuộc tính là gì, chúng ta cần thực hiện một sự lạc đềTrăn 3. 0 đã giới thiệu các chú thích và vài năm sau, chúng đã được mở rộng thành các gợi ý loại. Nếu bạn không quen thuộc với những khái niệm đó, hãy xem hướng dẫn Real Python này và
Những thay đổi đó không được chuyển trở lại Python 2, mà thay vào đó sử dụng các nhận xét loại dưới dạng chú thích. Để biết thêm thông tin, hãy xem hoặc hướng dẫn Real Python này
Thuộc tính
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
1 đề cập đến một nhận xét loại đặc biệt import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
2 được sử dụng để chỉ ra trình kiểm tra loại [chẳng hạn như mypy] để loại bỏ lỗi nếu tìm thấy lỗi. Vì những lý do cũ, mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 vẫn đang báo cáo về những nhận xét đó, nhưng chỉ khi được yêu cầu làm như vậyHãy xem một ví dụ
>>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
Điều này sẽ xuất ra
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]],
type_comment='int']],
type_ignores=[]]
Lưu ý rằng sự khác biệt duy nhất đối với phân tích chi tiết về AST đã thảo luận trước đó là sự hiện diện của thuộc tính
import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
4. Thuộc tính phản ánh siêu dữ liệu được cung cấp bởi nhận xét loại import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
5 và được thêm vào nút cây AST >>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
8 vì chúng tôi đã chỉ định import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
7 khi kích hoạt phân tích cú phápTuy nhiên,
import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
2 được đối xử khác biệt. Những nhận xét loại đó được lưu trữ vào thuộc tính >>> code = 'one_plus_two = 1+2 # type: int'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
1 dưới dạng đối tượng Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
00 thay vì được thu thập dưới dạng siêu dữ liệu trong các nút bên trong của cây>>> code = 'one_plus_two = 1+2 # type: ignore'
>>> tree = ast.parse[code, type_comments=True]
>>> print[ast.dump[tree, indent=4]]
Điều này sẽ xuất ra
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[
TypeIgnore[lineno=1, tag='']]]
API mô-đun Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
Mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 chủ yếu là một tập hợp lớn các lớp, mỗi lớp cho mỗi khía cạnh khác nhau của ngữ pháp Python. Nhìn chung, có khoảng 100 lớp, từ nghĩa đen, đến cấu trúc phức tạp hơn, chẳng hạn như hiểu danh sáchModule[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
03 là lớp cơ sở cho tất cả các lớp khác trong mô-đun và nó định nghĩa các thuộc tính cơ sở sau cho tất cả các nút AST
04,Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
05,Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
06 vàModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
07 được sử dụng để theo dõi vị trí chính xác của hướng dẫn liên quan trong mã nguồnModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
08 chứa danh sách các tên thuộc tính [bạn có thể nghĩ rằng đó là danh sách các tên “con”]Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Khi xử lý một AST, phần khó nhất là hiểu ngữ nghĩa của các nút và thuộc tính. Thực tế có rất nhiều biến thể và ốp góc nên rất dễ bị nhầm lẫn
Một cách hay để bắt đầu làm quen với AST là sử dụng bảng điều khiển tương tác chẳng hạn như
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
09 tương tự như những gì chúng ta đã làm trong các ví dụ trước. Nếu bạn đã quen với IDE, thì cả PyCharm và Visual Studio Code đều cung cấp plugin để trực quan hóa AST [lưu ý rằng PyCharm sử dụng phiên bản AST của chính nó được gọi là Giao diện cấu trúc chương trình – PSI]Bất kể lựa chọn ưa thích của bạn là gì, tài liệu là nguồn tài nguyên cơ bản cần có trong tầm tay. Tuy nhiên, một vài nhận xét
Vì ngôn ngữ Python đang trong quá trình phát triển không ngừng, hãy đảm bảo sử dụng phiên bản mới nhất của tài liệu Python
Tài liệu chính thức cũng gợi ý tham khảo Green Tree Snake, tài liệu này thực sự làm rất tốt việc bổ sung cho tài liệu chính thức về những phần mà nếu không thì có vẻ “khô khan” về chi tiết
Bên cạnh các lớp, mô-đun
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
7 định nghĩa cách thực hiện việc thăm cây và cách thực hiện các phép biến đổiTham quan một AST
Bạn có thể truy cập AST theo hai cách. sử dụng các chức năng của trình trợ giúp hoặc thông qua một lớp
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
11Hãy bắt đầu xem xét các chức năng của trình trợ giúp
12 truy cập nút đã chỉ định và đệ quy tất cả các phần tử con của nó, nhưng theo thứ tự không được chỉ địnhModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
13 vàModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
14 tương tự nhưModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
15 vàModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
16 của cấu trúc dữ liệuModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
17, nhưng chỉ áp dụng cho một nút cụ thể và chúng không mang tính đệ quyModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Dưới đây là một số ví dụ
>>> import ast
>>> code = "one_plus_two = 1+2"
>>> tree = ast.parse[code]
>>> for node in ast.walk[tree]:
print[node.__class__.__name__]
Module
Assign
Name
BinOp
Store
Constant
Add
Constant
>>> for name, value in ast.iter_fields[tree]:
print[name, value]
body []
type_ignores []
>>> for node in ast.iter_child_nodes[tree]:
print[node.__class__.__name__]
Assign
Thay vào đó, khi sử dụng
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
11, người ta có thể đăng ký các cuộc gọi lại cụ thể để kích hoạt khi truy cập các loại nút cụ thểclass BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
trong ví dụ này
Chúng ta định nghĩa một lớp
19 mở rộng lớpModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
11Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Chúng tôi đăng ký một cuộc gọi lại để được kích hoạt khi
21 nút được truy cập. Tên gọi lại luôn làModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
22 trong đóModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
23 là một trong những tên loại nút được xác định trước [trong trường hợp của chúng tôi là ____49_______3]Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Khi cuộc gọi lại được gọi, nó sẽ nhận được tham chiếu của nút được phân tích. Trong ví dụ này, chúng tôi sử dụng thông tin nút để in số dòng của lệnh mà nó liên quan đến
Cuối cùng, chúng tôi gọi
25 để tuyên truyền lượt truy cập vào nút con của nút đầu vàoModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Loại ma thuật đen nào xảy ra đằng sau hiện trường để kích hoạt các cuộc gọi lại? . Hàm
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
11 cũng định nghĩa hàm Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
27 luôn được gọi trước. nếu loại nút đầu vào khớp với một trong các cuộc gọi lại, thì cuộc gọi lại đó được gọi, nếu không thì Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
28 được gọi để thăm các nút con. Trong ví dụ của chúng tôi, chúng tôi không ghi đè lên Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
27, do đó chúng tôi có thể kích hoạt một lượt truy cập vào cây chỉ bằng cách gọi phương thức>>> vis = BinOpVisitor[]
>>> vis.visit[tree]
Đây là ví dụ hoàn chỉnh
import ast
class BinOpVisitor[ast.NodeVisitor]:
def visit_BinOp[self, node]:
print[f"found BinOp at line: {node.lineno}"]
self.generic_visit[node]
code = """
left_op = 1
right_op = 2
sum_two_things = left_op + right_op
other_sum = sum_two_things - 1
print[sum_two_things]
print[other_sum]
"""
tree = ast.parse[code]
print["=== full AST ==="]
print[ast.dump[tree, indent=4]]
print[]
print["=== visit ==="]
vis = BinOpVisitor[]
vis.visit[tree]
Chạy chương trình ta thu được kết quả như sau
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
0Sửa đổi một AST
Một
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
30 có thể được sử dụng làm lớp cơ sở cho một máy biến áp, tương tự như logic được sử dụng cho lớp khách truy cập. Lần này, thay vì chỉ truy cập các nút, các cuộc gọi lại được sử dụng để sửa đổi, thay thế, thêm các nút mớiĐây là một ví dụ
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
1trong ví dụ này
Chúng tôi đã đăng ký gọi lại để được kích hoạt khi bàn giao một nút
31Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Cuộc gọi lại tạo một số ngẫu nhiên trong khoảng [-10, 10], tạo một nút
32 mới với giá trị được tạo và báo cáo một thông báo trên đầu ra tiêu chuẩnModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Cuối cùng, nó trả về tham chiếu của nút mới
Tham chiếu được trả về bởi các cuộc gọi lại đại diện cho nút sẽ sử dụng trong AST. Trong ví dụ này, chúng tôi đang thay thế nút ban đầu. Thay vào đó, khi trả về
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
33, nút đã truy cập sẽ bị xóa khỏi câyĐể kích hoạt chuyển đổi, chúng tôi sử dụng thao tác tương tự được sử dụng cho lượt truy cập. Lần này, lượt truy cập trả về tham chiếu của cây đã sửa đổi
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
2Đây là ví dụ đầy đủ
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
3Mã nguồn trong
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
34 giống với mã được sử dụng để thực hiện một lượt truy cập đơn giản. Tương tự như vậy, quá trình tạo cây liên quanSau đó, chúng tôi sửa một hạt giống cho trình tạo số ngẫu nhiên thông qua
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
35, để có đầu ra nhất quán khi chạy chương trình nhiều lầnChúng tôi tạo một đối tượng
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
36 và chúng tôi truy cập nó để thu được Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
37, đây là phiên bản đã biến đổi của cây ban đầuĐể xác minh các phép biến đổi, chúng tôi có thể in AST và “chạy nó” bằng cách chuyển đổi thành mã thực thi. Để làm như vậy, chúng ta sử dụng hàm trợ giúp
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
38Chúng tôi bắt đầu in nội dung của cây bằng cách sử dụng
3 như đã thấy trong các ví dụ trước>>> code = 'one_plus_two = 1+2 # type: int' >>> tree = ast.parse[code, type_comments=True] >>> print[ast.dump[tree, indent=4]]
Sau đó, chúng tôi áp dụng
40 cho cây. Mỗi nút trong AST thực sự được dự kiến sẽ cóModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
04 được lấp đầy, nhưng thay vì lấp đầy nó khi thực hiện các phép biến đổi, hàm trợ giúpModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
40 cho phép trì hoãn việc sửa lỗi này cho đến khi quá trình biên dịch được yêu cầuModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Cuối cùng, hàm dựng sẵn
43 được sử dụng để chuyển đổi AST thành một đối tượng mã, đến lượt nó được thực thi bằng cách gọi hàm dựng sẵnModule[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
44Module[ body=[ Assign[ targets=[ Name[id='one_plus_two', ctx=Store[]]], value=BinOp[ left=Constant[value=1], op=Add[], right=Constant[value=2]]]], type_ignores=[]]
Đây là đầu ra liên quan đến
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
45Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
4Đầu ra cho
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
46Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
5Đầu ra bây giờ là "ngẫu nhiên", như mong đợi của phép biến đổi. Tuy nhiên, phép biến đổi đã ghi đè lên cây ban đầu, vì
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
37 và Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
48 là cùng một đối tượngModule[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
6Tuy nhiên, để tránh điều này, người ta chỉ cần sử dụng lệnh để sao chép toàn bộ cây trước khi kích hoạt chuyển đổi hoặc ghi đè phương thức
Module[
body=[
Assign[
targets=[
Name[id='one_plus_two', ctx=Store[]]],
value=BinOp[
left=Constant[value=1],
op=Add[],
right=Constant[value=2]]]],
type_ignores=[]]
27 và xác định logic đặc biệt cho trường hợp sử dụng hiện tại