8. Câu lệnh ghép¶
Câu lệnh ghép chứa (nhóm) câu lệnh khác; chúng ảnh hưởng hoặc kiểm soát việc thực hiện các câu lệnh khác theo một cách nào đó. Nói chung, các câu lệnh ghép trải dài trên nhiều dòng, mặc dù trong các phiên bản đơn giản, toàn bộ câu lệnh ghép có thể được chứa trong một dòng.
Các câu lệnh if, while và for triển khai các cấu trúc luồng điều khiển truyền thống. try chỉ định các trình xử lý ngoại lệ và/hoặc mã dọn dẹp cho một nhóm câu lệnh, trong khi câu lệnh with cho phép thực thi mã khởi tạo và mã hoàn thiện xung quanh một khối mã. Các định nghĩa hàm và lớp cũng là các câu lệnh ghép về mặt cú pháp.
Một câu lệnh ghép bao gồm một hoặc nhiều 'mệnh đề'. Một mệnh đề bao gồm một tiêu đề và một 'bộ'. Các tiêu đề mệnh đề của một câu lệnh ghép cụ thể đều có cùng mức thụt lề. Mỗi tiêu đề mệnh đề bắt đầu bằng một từ khóa xác định duy nhất và kết thúc bằng dấu hai chấm. Một bộ là một nhóm các câu lệnh được điều khiển bởi một mệnh đề. Một bộ có thể là một hoặc nhiều câu lệnh đơn giản được phân tách bằng dấu chấm phẩy trên cùng dòng với tiêu đề, theo sau dấu hai chấm của tiêu đề hoặc có thể là một hoặc nhiều câu lệnh thụt lề trên các dòng tiếp theo. Chỉ dạng sau của một bộ mới có thể chứa các câu lệnh ghép lồng nhau; những điều sau đây là bất hợp pháp, chủ yếu là vì không rõ mệnh đề if sau sẽ thuộc về mệnh đề if nào:
if test1: if test2: print(x)
Cũng lưu ý rằng dấu chấm phẩy liên kết chặt chẽ hơn dấu hai chấm trong ngữ cảnh này, do đó, trong ví dụ sau, tất cả hoặc không có lệnh gọi print() nào được thực thi:
nếu x < y < z: print(x); in(y); in(z)
Tóm tắt:
compound_stmt:if_stmt|while_stmt|for_stmt|try_stmt|with_stmt|match_stmt|funcdef|classdef|async_with_stmt|async_for_stmt|async_funcdefsuite:stmt_listNEWLINE | NEWLINE INDENTstatement+ DEDENT statement:stmt_listNEWLINE |compound_stmtstmt_list:simple_stmt(";"simple_stmt)* [";"]
Lưu ý rằng các câu lệnh luôn kết thúc bằng NEWLINE và có thể theo sau là DEDENT. Cũng lưu ý rằng các mệnh đề tiếp tục tùy chọn luôn bắt đầu bằng một từ khóa không thể bắt đầu một câu lệnh, do đó không có sự mơ hồ (vấn đề 'else lủng lẳng' được giải quyết trong Python bằng cách yêu cầu các câu lệnh if lồng nhau phải thụt lề).
Định dạng của các quy tắc ngữ pháp trong các phần sau đây đặt mỗi mệnh đề trên một dòng riêng biệt để dễ hiểu.
8.1. Tuyên bố if¶
Câu lệnh if được sử dụng để thực thi có điều kiện:
if_stmt: "if"assignment_expression":"suite("elif"assignment_expression":"suite)* ["else" ":"suite]
Nó chọn chính xác một trong các bộ bằng cách đánh giá từng biểu thức một cho đến khi tìm thấy một biểu thức đúng (xem phần Các phép toán Boolean để biết định nghĩa đúng và sai); thì bộ đó sẽ được thực thi (và không có phần nào khác của câu lệnh if được thực thi hoặc đánh giá). Nếu tất cả các biểu thức đều sai, thì bộ mệnh đề else, nếu có, sẽ được thực thi.
8.2. Tuyên bố while¶
Câu lệnh while được sử dụng để thực thi lặp lại miễn là biểu thức đúng:
while_stmt: "while"assignment_expression":"suite["else" ":"suite]
Việc này liên tục kiểm tra biểu thức và nếu nó đúng thì sẽ thực thi bộ đầu tiên; nếu biểu thức sai (có thể là lần đầu tiên nó được kiểm tra) thì bộ mệnh đề else, nếu có, sẽ được thực thi và vòng lặp kết thúc.
Câu lệnh break được thực thi trong bộ đầu tiên sẽ chấm dứt vòng lặp mà không thực thi bộ mệnh đề else. Câu lệnh continue được thực thi trong bộ phần mềm đầu tiên sẽ bỏ qua phần còn lại của bộ phần mềm và quay lại kiểm tra biểu thức.
8.3. Tuyên bố for¶
Câu lệnh for được sử dụng để lặp lại các phần tử của một chuỗi (chẳng hạn như chuỗi, bộ dữ liệu hoặc danh sách) hoặc đối tượng có thể lặp khác:
for_stmt: "for"target_list"in"starred_expression_list":"suite["else" ":"suite]
Biểu thức starred_expression_list được đánh giá một lần; nó sẽ mang lại một đối tượng iterable. Một iterator được tạo cho lần lặp đó. Sau đó, mục đầu tiên do trình vòng lặp cung cấp sẽ được gán cho danh sách mục tiêu bằng cách sử dụng các quy tắc tiêu chuẩn cho các phép gán (xem Báo cáo bài tập) và bộ phần mềm sẽ được thực thi. Điều này lặp lại cho từng mục được cung cấp bởi iterator. Khi vòng lặp hết, bộ trong mệnh đề else, nếu có, sẽ được thực thi và vòng lặp kết thúc.
Câu lệnh break được thực thi trong bộ đầu tiên sẽ chấm dứt vòng lặp mà không thực thi bộ mệnh đề else. Câu lệnh continue được thực thi trong bộ đầu tiên sẽ bỏ qua phần còn lại của bộ và tiếp tục với mục tiếp theo hoặc với mệnh đề else nếu không có mục tiếp theo.
Vòng lặp for thực hiện gán các biến trong danh sách đích. Điều này sẽ ghi đè tất cả các phép gán trước đó cho các biến đó, bao gồm cả các biến được thực hiện trong bộ vòng lặp for:
cho tôi trong phạm vi (10):
in(i)
i = 5 # this sẽ không ảnh hưởng đến vòng lặp for
# because tôi sẽ bị ghi đè bằng cái tiếp theo
# index trong phạm vi
Các tên trong danh sách đích sẽ không bị xóa khi vòng lặp kết thúc, nhưng nếu chuỗi trống, chúng sẽ không được vòng lặp gán cho chúng. Gợi ý: kiểu tích hợp range() biểu thị chuỗi số học bất biến của các số nguyên. Ví dụ: lặp lại range(3) liên tiếp sẽ mang lại 0, 1 và sau đó là 2.
Thay đổi trong phiên bản 3.11: Các phần tử được gắn dấu sao hiện được cho phép trong danh sách biểu thức.
8.4. Tuyên bố try¶
Câu lệnh try chỉ định các trình xử lý ngoại lệ và/hoặc mã dọn dẹp cho một nhóm câu lệnh:
try_stmt:try1_stmt|try2_stmt|try3_stmttry1_stmt: "try" ":"suite("except" [expression["as"identifier]] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try2_stmt: "try" ":"suite("except" "*"expression["as"identifier] ":"suite)+ ["else" ":"suite] ["finally" ":"suite] try3_stmt: "try" ":"suite"finally" ":"suite
Thông tin bổ sung về các ngoại lệ có thể được tìm thấy trong phần Ngoại lệ và thông tin về cách sử dụng câu lệnh raise để tạo ra các ngoại lệ có thể được tìm thấy trong phần Tuyên bố raise.
Thay đổi trong phiên bản 3.14: Hỗ trợ tùy chọn bỏ dấu ngoặc đơn nhóm khi sử dụng nhiều loại ngoại lệ. Xem PEP 758.
8.4.1. mệnh đề except¶
(Các) mệnh đề except chỉ định một hoặc nhiều trình xử lý ngoại lệ. Khi không có ngoại lệ nào xảy ra trong mệnh đề try thì không có trình xử lý ngoại lệ nào được thực thi. Khi một ngoại lệ xảy ra trong bộ try, việc tìm kiếm trình xử lý ngoại lệ sẽ được bắt đầu. Tìm kiếm này lần lượt kiểm tra các mệnh đề except cho đến khi tìm thấy một mệnh đề phù hợp với ngoại lệ. Mệnh đề except không biểu thức, nếu có, phải ở cuối cùng; nó khớp với bất kỳ ngoại lệ nào.
Đối với mệnh đề except có một biểu thức, biểu thức đó phải đánh giá một loại ngoại lệ hoặc một bộ các loại ngoại lệ. Có thể bỏ dấu ngoặc đơn nếu cung cấp nhiều loại ngoại lệ và mệnh đề as không được sử dụng. Ngoại lệ được nêu ra khớp với một mệnh đề except có biểu thức đánh giá theo lớp hoặc non-virtual base class của đối tượng ngoại lệ hoặc với một bộ dữ liệu có chứa một lớp như vậy.
Nếu không có mệnh đề except nào khớp với ngoại lệ đó thì việc tìm kiếm trình xử lý ngoại lệ sẽ tiếp tục trong mã xung quanh và trên ngăn xếp lệnh gọi. [1]
Nếu việc đánh giá một biểu thức trong tiêu đề của mệnh đề except đưa ra một ngoại lệ, thì quá trình tìm kiếm ban đầu cho một trình xử lý sẽ bị hủy và việc tìm kiếm sẽ bắt đầu tìm kiếm ngoại lệ mới trong mã xung quanh và trên ngăn xếp cuộc gọi (nó được xử lý như thể toàn bộ câu lệnh try đã đưa ra ngoại lệ).
Khi tìm thấy mệnh đề except phù hợp, ngoại lệ sẽ được gán cho mục tiêu được chỉ định sau từ khóa as trong mệnh đề except đó, nếu có, và bộ mệnh đề except sẽ được thực thi. Tất cả các mệnh đề except phải có một khối thực thi. Khi kết thúc khối này, việc thực thi sẽ tiếp tục bình thường sau toàn bộ câu lệnh try. (Điều này có nghĩa là nếu tồn tại hai trình xử lý lồng nhau cho cùng một ngoại lệ và ngoại lệ đó xảy ra trong mệnh đề try của trình xử lý bên trong thì trình xử lý bên ngoài sẽ không xử lý ngoại lệ đó.)
Khi một ngoại lệ được gán bằng as target, nó sẽ bị xóa ở cuối mệnh đề except. Điều này giống như
ngoại trừ E là N:
foo
đã được dịch sang
ngoại trừ E là N:
thử:
foo
cuối cùng:
del N
Điều này có nghĩa là ngoại lệ phải được gán cho một tên khác để có thể đề cập đến nó sau mệnh đề except. Các ngoại lệ được xóa vì với truy nguyên được gắn vào chúng, chúng tạo thành một chu trình tham chiếu với khung ngăn xếp, giữ cho tất cả các cục bộ trong khung đó tồn tại cho đến khi quá trình thu gom rác tiếp theo diễn ra.
Trước khi bộ mệnh đề except được thực thi, ngoại lệ được lưu trữ trong mô-đun sys, nơi nó có thể được truy cập từ bên trong nội dung của mệnh đề except bằng cách gọi sys.exception(). Khi rời khỏi trình xử lý ngoại lệ, ngoại lệ được lưu trữ trong mô-đun sys được đặt lại về giá trị trước đó:
>>> in(sys.Exception())
không có
>>> thử:
... nâng cao TypeError
... ngoại trừ:
... print(repr(sys.Exception()))
... thử:
... tăng ValueError
... ngoại trừ:
... print(repr(sys.Exception()))
... print(repr(sys.Exception()))
...
LoạiError()
Giá trịError()
LoạiError()
>>> in(sys.Exception())
không có
8.4.2. mệnh đề except*¶
(Các) mệnh đề except* chỉ định một hoặc nhiều trình xử lý cho các nhóm ngoại lệ (phiên bản BaseExceptionGroup). Câu lệnh try có thể có mệnh đề except hoặc except*, nhưng không được có cả hai. Loại ngoại lệ để khớp là bắt buộc trong trường hợp except*, vì vậy except*: là lỗi cú pháp. Loại này được hiểu như trong trường hợp except, nhưng việc so khớp được thực hiện trên các ngoại lệ có trong nhóm đang được xử lý. Một TypeError được tạo ra nếu loại phù hợp là lớp con của BaseExceptionGroup, vì điều đó sẽ có ngữ nghĩa không rõ ràng.
Khi một nhóm ngoại lệ được tạo ra trong khối thử, mỗi mệnh đề except* sẽ chia (xem split()) nó thành các nhóm con gồm các ngoại lệ khớp và không khớp. Nếu nhóm con phù hợp không trống, nó sẽ trở thành ngoại lệ được xử lý (giá trị được trả về từ sys.exception()) và được gán cho mục tiêu của mệnh đề except* (nếu có). Sau đó, nội dung của mệnh đề except* sẽ được thực thi. Nếu nhóm con không khớp không trống, nó sẽ được except* tiếp theo xử lý theo cách tương tự. Điều này tiếp tục cho đến khi tất cả các ngoại lệ trong nhóm được khớp hoặc mệnh đề except* cuối cùng đã chạy.
Sau khi tất cả các mệnh đề except* được thực thi, nhóm ngoại lệ chưa được xử lý sẽ được hợp nhất với bất kỳ ngoại lệ nào được nêu ra hoặc được nêu lại từ bên trong các mệnh đề except*. Nhóm ngoại lệ được hợp nhất này sẽ lan truyền trên.:
>>> thử:
... raise ExceptionGroup("ví dụ",
... [ValueError(1), TypeError(2), OSError(3), OSError(4)])
... ngoại trừ* TypeError as e:
... print(f'caught {type(e)} với các {e.Exceptions}' lồng nhau
... ngoại trừ* OSError as e:
... print(f'caught {type(e)} với các {e.Exceptions}' lồng nhau
...
đã bắt được <class 'ExceptionGroup'> lồng nhau (TypeError(2),)
đã bắt gặp <class 'ExceptionGroup'> lồng nhau (OSError(3), OSError(4))
+ Nhóm ngoại lệ Traceback (cuộc gọi gần đây nhất):
| Tệp "<doctest default[0]>", dòng 2, trong <module>
| tăng ExceptionGroup("ví dụ",
| [ValueError(1), TypeError(2), OSError(3), OSError(4)])
| ExceptionGroup: ví dụ (1 ngoại lệ phụ)
+-+---------------- 1 ----------------
| Giá trịLỗi: 1
+-----------------------------------
Nếu ngoại lệ được tạo ra từ khối try không phải là nhóm ngoại lệ và loại của nó khớp với một trong các mệnh đề except*, thì nó sẽ bị nhóm ngoại lệ bắt và bao bọc bằng một chuỗi thông báo trống. Điều này đảm bảo rằng loại e mục tiêu luôn là BaseExceptionGroup:
>>> thử:
... nâng cao BlockingIOError
... ngoại trừ* BlockingIOError as e:
... in(repr(e))
...
ExceptionGroup('', (BlockingIOError(),))
break, continue và return không thể xuất hiện trong mệnh đề except*.
8.4.3. mệnh đề else¶
Mệnh đề else tùy chọn được thực thi nếu luồng điều khiển rời khỏi bộ try, không có ngoại lệ nào được đưa ra và không có câu lệnh return, continue hoặc break nào được thực thi. Các ngoại lệ trong mệnh đề else không được xử lý bởi các mệnh đề except trước đó.
8.4.4. mệnh đề finally¶
Nếu có finally, nó sẽ chỉ định trình xử lý 'dọn dẹp'. Mệnh đề try được thực thi, bao gồm mọi mệnh đề except và else. Nếu một ngoại lệ xảy ra trong bất kỳ mệnh đề nào và không được xử lý thì ngoại lệ đó sẽ được lưu tạm thời. Mệnh đề finally được thực thi. Nếu có một ngoại lệ đã lưu, nó sẽ được đưa ra lại ở cuối mệnh đề finally. Nếu mệnh đề finally đưa ra một ngoại lệ khác, ngoại lệ đã lưu sẽ được đặt làm bối cảnh của ngoại lệ mới. Nếu mệnh đề finally thực thi câu lệnh return, break hoặc continue, ngoại lệ đã lưu sẽ bị loại bỏ. Ví dụ: hàm này trả về 42.
chắc chắn f():
thử:
1/0
cuối cùng:
trở lại 42
Thông tin ngoại lệ không có sẵn cho chương trình trong quá trình thực thi mệnh đề finally.
Khi một câu lệnh return, break hoặc continue được thực thi trong bộ try của câu lệnh try...finally, mệnh đề finally cũng được thực thi 'trên đường ra'.
Giá trị trả về của hàm được xác định bởi câu lệnh return cuối cùng được thực thi. Vì mệnh đề finally luôn được thực thi nên câu lệnh return được thực thi trong mệnh đề finally sẽ luôn là câu lệnh được thực thi cuối cùng. Hàm sau trả về 'cuối cùng'.
def foo():
thử:
quay lại 'thử'
cuối cùng:
trở lại 'cuối cùng'
Thay đổi trong phiên bản 3.8: Trước Python 3.8, câu lệnh continue là bất hợp pháp trong mệnh đề finally do có vấn đề trong quá trình triển khai.
Thay đổi trong phiên bản 3.14: Trình biên dịch phát ra SyntaxWarning khi return, break hoặc continue xuất hiện trong khối finally (xem PEP 765).
8.5. Tuyên bố with¶
Câu lệnh with được sử dụng để bao bọc việc thực thi một khối bằng các phương thức được xác định bởi trình quản lý bối cảnh (xem phần Với Trình quản lý bối cảnh câu lệnh). Điều này cho phép đóng gói các mẫu sử dụng try...except...finally thông thường để thuận tiện cho việc tái sử dụng.
with_stmt: "with" ( "("with_stmt_contents","? ")" |with_stmt_contents) ":"suitewith_stmt_contents:with_item(","with_item)* with_item:expression["as"target]
Việc thực thi câu lệnh with với một "item" được tiến hành như sau:
Biểu thức ngữ cảnh (biểu thức được đưa ra trong
with_item) được đánh giá để có được trình quản lý ngữ cảnh.Zz000zz của trình quản lý bối cảnh được tải để sử dụng sau.
Zz000zz của trình quản lý bối cảnh được tải để sử dụng sau.
Phương thức
__enter__()của trình quản lý bối cảnh được gọi.Nếu một mục tiêu được đưa vào câu lệnh
withthì giá trị trả về từ__enter__()sẽ được gán cho nó.Ghi chú
Câu lệnh
withđảm bảo rằng nếu phương thức__enter__()trả về không có lỗi thì__exit__()sẽ luôn được gọi. Do đó, nếu xảy ra lỗi trong quá trình gán vào danh sách mục tiêu, lỗi đó sẽ được xử lý giống như lỗi xảy ra trong bộ phần mềm. Xem bước 7 bên dưới.Bộ phần mềm được thực thi.
Phương thức
__exit__()của trình quản lý bối cảnh được gọi. Nếu một ngoại lệ khiến bộ phần mềm bị thoát thì loại, giá trị và truy nguyên của nó sẽ được chuyển dưới dạng đối số cho__exit__(). Mặt khác, ba đối sốNonesẽ được cung cấp.Nếu bộ phần mềm bị thoát do một ngoại lệ và giá trị trả về từ phương thức
__exit__()là sai thì ngoại lệ đó sẽ được đưa ra lại. Nếu giá trị trả về là đúng, ngoại lệ sẽ bị loại bỏ và việc thực thi tiếp tục với câu lệnh sau câu lệnhwith.Nếu bộ phần mềm bị thoát vì bất kỳ lý do nào không phải là ngoại lệ, thì giá trị trả về từ
__exit__()sẽ bị bỏ qua và việc thực thi sẽ tiếp tục ở vị trí bình thường đối với loại thoát đã được thực hiện.
Đoạn mã sau:
với EXPRESSION là TARGET:
SUITE
về mặt ngữ nghĩa tương đương với:
người quản lý = (EXPRESSION)
nhập = người quản lý.__enter__
thoát = người quản lý.__exit__
giá trị = nhập()
hit_ngoại trừ = Sai
thử:
TARGET = giá trị
SUITE
ngoại trừ:
hit_ngoại trừ = Đúng
nếu không thoát(*sys.exc_info()):
nâng cao
cuối cùng:
nếu không nhấn_ngoại trừ:
thoát (Không, Không, Không)
ngoại trừ việc special method lookup ngầm định được sử dụng cho __enter__() và __exit__().
Với nhiều mục, trình quản lý bối cảnh được xử lý như thể nhiều câu lệnh with được lồng nhau
với A() là a, B() là b:
SUITE
về mặt ngữ nghĩa tương đương với:
với A() là:
với B() là b:
SUITE
Bạn cũng có thể viết trình quản lý bối cảnh nhiều mục thành nhiều dòng nếu các mục được bao quanh bởi dấu ngoặc đơn. Ví dụ:
với (
A() là một,
B() là b,
):
SUITE
Thay đổi trong phiên bản 3.1: Hỗ trợ nhiều biểu thức ngữ cảnh.
Thay đổi trong phiên bản 3.10: Hỗ trợ sử dụng nhóm dấu ngoặc đơn để ngắt câu lệnh thành nhiều dòng.
8.6. Tuyên bố match¶
Added in version 3.10.
Câu lệnh so khớp được sử dụng để khớp mẫu. Cú pháp:
match_stmt: 'match'subject_expr":" NEWLINE INDENTcase_block+ DEDENT subject_expr: `!star_named_expression` "," `!star_named_expressions`? | `!named_expression` case_block: 'case'patterns[guard] ":" `!block`
Ghi chú
Phần này sử dụng dấu ngoặc đơn để biểu thị soft keywords.
Khớp mẫu lấy một mẫu làm đầu vào (theo sau case) và giá trị chủ đề (theo sau match). Mẫu (có thể chứa các mẫu phụ) được khớp với giá trị chủ đề. Kết quả là:
Trận đấu thành công hay thất bại (còn được gọi là mẫu thành công hay thất bại).
Có thể ràng buộc các giá trị phù hợp với một tên. Các điều kiện tiên quyết cho việc này sẽ được thảo luận thêm dưới đây.
Từ khóa match và case là soft keywords.
8.6.1. Tổng quan¶
Dưới đây là tổng quan về luồng logic của câu lệnh so khớp:
Biểu thức chủ đề
subject_exprđược đánh giá và thu được giá trị chủ đề. Nếu biểu thức chủ đề chứa dấu phẩy thì một bộ dữ liệu sẽ được tạo bằng the standard rules.Mỗi mẫu trong
case_blockđược cố gắng khớp với giá trị chủ đề. Các quy tắc cụ thể cho sự thành công hay thất bại được mô tả dưới đây. Nỗ lực so khớp cũng có thể liên kết một số hoặc tất cả các tên độc lập trong mẫu. Các quy tắc ràng buộc mẫu chính xác khác nhau tùy theo loại mẫu và được chỉ định bên dưới. Name bindings made during a successful pattern match outlive the executed block and can be used after the match statement.Ghi chú
Trong quá trình khớp mẫu không thành công, một số mẫu phụ có thể thành công. Đừng dựa vào các ràng buộc được thực hiện cho một trận đấu thất bại. Ngược lại, đừng dựa vào các biến không thay đổi sau một trận đấu thất bại. Hành vi chính xác phụ thuộc vào việc thực hiện và có thể khác nhau. Đây là một quyết định có chủ ý được đưa ra nhằm cho phép các hoạt động triển khai khác nhau bổ sung thêm các tính năng tối ưu hóa.
Nếu mẫu thành công, bộ bảo vệ tương ứng (nếu có) sẽ được đánh giá. Trong trường hợp này tất cả các ràng buộc tên được đảm bảo đã xảy ra.
Nếu bộ bảo vệ đánh giá là đúng hoặc bị thiếu,
blockbên trongcase_blocksẽ được thực thi.Nếu không,
case_blocktiếp theo sẽ được thử như mô tả ở trên.Nếu không còn khối trường hợp nào nữa thì câu lệnh so khớp sẽ được hoàn thành.
Ghi chú
Nói chung, người dùng không bao giờ nên dựa vào mẫu đang được đánh giá. Tùy thuộc vào việc triển khai, trình thông dịch có thể lưu các giá trị vào bộ đệm hoặc sử dụng các tối ưu hóa khác để bỏ qua các đánh giá lặp lại.
Một tuyên bố đối sánh mẫu:
>>> cờ = Sai
>>> trận đấu (100, 200):
... trường hợp (100, 300): # Mismatch: 200 != 300
... print('Trường hợp 1')
... trường hợp (100, 200) nếu cờ: # Successful khớp, nhưng bảo vệ không thành công
... print('Trường hợp 2')
... case (100, y): # Matches và liên kết y với 200
... print(f'Case 3, y: {y}')
... trường hợp _: # Pattern chưa thử
... print('Trường hợp 4, tôi khớp bất cứ thứ gì!')
...
Trường hợp 3, y: 200
Trong trường hợp này, if flag là người bảo vệ. Đọc thêm về điều đó trong phần tiếp theo.
8.6.2. Vệ binh¶
guard: "if" `!named_expression`
Một guard (là một phần của case) phải thành công để mã bên trong khối case được thực thi. Nó có dạng: if theo sau là một biểu thức.
Luồng logic của khối case với guard như sau:
Kiểm tra xem mẫu trong khối
caseđã thành công chưa. Nếu mẫu không thành công,guardkhông được đánh giá và khốicasetiếp theo sẽ được kiểm tra.Nếu mẫu thành công, hãy đánh giá
guard.Nếu điều kiện
guardđược đánh giá là đúng thì khối trường hợp sẽ được chọn.Nếu điều kiện
guardđược đánh giá là sai thì khối trường hợp không được chọn.Nếu
guardđưa ra một ngoại lệ trong quá trình đánh giá, ngoại lệ đó sẽ xuất hiện.
Các vệ sĩ được phép có tác dụng phụ vì chúng là biểu hiện. Việc đánh giá bảo vệ phải tiến hành từ khối trường hợp đầu tiên đến khối trường hợp cuối cùng, lần lượt từng khối trường hợp, bỏ qua các khối trường hợp có (các) mẫu không thành công. (Tức là, việc đánh giá bảo vệ phải diễn ra theo thứ tự.) Việc đánh giá bảo vệ phải dừng khi khối trường hợp được chọn.
8.6.3. Khối trường hợp không thể chối cãi¶
Khối trường hợp không thể bác bỏ là khối trường hợp khớp tất cả. Một tuyên bố đối sánh có thể có nhiều nhất một khối trường hợp không thể bác bỏ và nó phải là khối cuối cùng.
Một khối vụ án được coi là không thể bác bỏ được nếu nó không có người bảo vệ và mẫu của nó là không thể bác bỏ được. Một khuôn mẫu được coi là không thể bác bỏ nếu chúng ta có thể chứng minh chỉ bằng cú pháp của nó rằng nó sẽ luôn thành công. Chỉ những mẫu sau đây là không thể bác bỏ:
8.6.4. mẫu¶
Ghi chú
Phần này sử dụng các ký hiệu ngữ pháp ngoài tiêu chuẩn EBNF:
ký hiệu
SEP.RULE+là viết tắt củaRULE (SEP RULE)*ký hiệu
!RULElà cách viết tắt của một xác nhận nhìn về phía trước tiêu cực
Cú pháp cấp cao nhất cho patterns là:
patterns:open_sequence_pattern|patternpattern:as_pattern|or_patternclosed_pattern: |literal_pattern|capture_pattern|wildcard_pattern|value_pattern|group_pattern|sequence_pattern|mapping_pattern|class_pattern
Các mô tả bên dưới sẽ bao gồm mô tả "bằng những thuật ngữ đơn giản" về tác dụng của một mẫu nhằm mục đích minh họa (ghi công cho Raymond Hettinger vì tài liệu đã truyền cảm hứng cho hầu hết các mô tả). Lưu ý rằng những mô tả này hoàn toàn nhằm mục đích minh họa và may not phản ánh cách triển khai cơ bản. Hơn nữa, chúng không bao gồm tất cả các hình thức hợp lệ.
8.6.4.1. HOẶC Mẫu¶
Mẫu OR là hai hoặc nhiều mẫu được phân tách bằng thanh dọc |. Cú pháp:
or_pattern: "|".closed_pattern+
Chỉ mẫu con cuối cùng có thể là irrefutable và mỗi mẫu con phải liên kết cùng một bộ tên để tránh sự mơ hồ.
Mẫu OR khớp lần lượt từng mẫu con của nó với giá trị chủ đề cho đến khi thành công. Mẫu OR sau đó được coi là thành công. Ngược lại, nếu không có mẫu con nào thành công thì mẫu OR sẽ thất bại.
Nói một cách đơn giản, P1 | P2 | ... sẽ cố gắng khớp với P1, nếu thất bại, nó sẽ cố gắng khớp với P2, thành công ngay lập tức nếu có thành công, ngược lại thì thất bại.
8.6.4.2. NHƯ mẫu¶
Mẫu AS khớp với mẫu OR ở bên trái từ khóa as đối với một chủ đề. Cú pháp:
as_pattern:or_pattern"as"capture_pattern
Nếu mẫu OR thất bại thì mẫu AS cũng thất bại. Mặt khác, mẫu AS liên kết chủ đề với tên ở bên phải từ khóa as và thành công. capture_pattern không thể là _.
Nói một cách đơn giản, P as NAME sẽ khớp với P và nếu thành công, nó sẽ đặt NAME = <subject>.
8.6.4.3. Mẫu chữ¶
Mẫu chữ tương ứng với hầu hết literals trong Python. Cú pháp:
literal_pattern:signed_number|signed_number"+" NUMBER |signed_number"-" NUMBER |strings| "None" | "True" | "False" signed_number: ["-"] NUMBER
Quy tắc strings và mã thông báo NUMBER được xác định trong standard Python grammar. Chuỗi trích dẫn ba được hỗ trợ. Chuỗi thô và chuỗi byte được hỗ trợ. dây f và dây chữ T không được hỗ trợ.
Các dạng signed_number '+' NUMBER và signed_number '-' NUMBER dùng để biểu thị complex numbers; họ yêu cầu một số thực ở bên trái và một số ảo ở bên phải. Ví dụ. 3 + 4j.
Nói một cách đơn giản, LITERAL sẽ chỉ thành công nếu <subject> == LITERAL. Đối với các singleton None, True và False, toán tử is được sử dụng.
8.6.4.4. Chụp mẫu¶
Mẫu chụp liên kết giá trị chủ đề với tên. Cú pháp:
capture_pattern: !'_' NAME
Một dấu gạch dưới _ không phải là mẫu chụp (đây là những gì !'_' thể hiện). Thay vào đó, nó được coi là wildcard_pattern.
Trong một mẫu nhất định, một tên cụ thể chỉ có thể bị ràng buộc một lần. Ví dụ. case x, x: ... không hợp lệ trong khi case [x] | x: ... được cho phép.
Chụp mẫu luôn thành công. Ràng buộc tuân theo các quy tắc phạm vi được thiết lập bởi toán tử biểu thức gán trong PEP 572; tên sẽ trở thành biến cục bộ trong phạm vi hàm chứa gần nhất trừ khi có câu lệnh global hoặc nonlocal có thể áp dụng.
Nói một cách đơn giản, NAME sẽ luôn thành công và nó sẽ thiết lập NAME = <subject>.
8.6.4.5. Mẫu ký tự đại diện¶
Mẫu ký tự đại diện luôn thành công (khớp với mọi thứ) và không liên kết tên. Cú pháp:
wildcard_pattern: '_'
_ là soft keyword trong bất kỳ mẫu nào, nhưng chỉ trong các mẫu. Nó là một mã định danh, như thường lệ, ngay cả trong các biểu thức chủ đề match, các khối guards và case.
Nói một cách đơn giản, _ sẽ luôn thành công.
8.6.4.6. Mẫu giá trị¶
Mẫu giá trị đại diện cho một giá trị được đặt tên trong Python. Cú pháp:
value_pattern:attrattr:name_or_attr"." NAME name_or_attr:attr| NAME
Tên có dấu chấm trong mẫu được tra cứu bằng Python name resolution rules tiêu chuẩn. Mẫu thành công nếu giá trị được tìm thấy so sánh bằng giá trị chủ đề (sử dụng toán tử đẳng thức ==).
Nói một cách đơn giản NAME1.NAME2 sẽ chỉ thành công nếu <subject> == NAME1.NAME2
Ghi chú
Nếu cùng một giá trị xuất hiện nhiều lần trong cùng một câu lệnh so khớp, trình thông dịch có thể lưu vào bộ đệm giá trị đầu tiên được tìm thấy và sử dụng lại giá trị đó thay vì lặp lại cùng một thao tác tra cứu. Bộ đệm này được liên kết chặt chẽ với việc thực thi nhất định của một câu lệnh so khớp nhất định.
8.6.4.7. Mẫu nhóm¶
Mẫu nhóm cho phép người dùng thêm dấu ngoặc đơn xung quanh các mẫu để nhấn mạnh việc phân nhóm dự định. Mặt khác, nó không có cú pháp bổ sung. Cú pháp:
group_pattern: "(" pattern ")"
Nói một cách đơn giản, (P) có tác dụng tương tự như P.
8.6.4.8. Mẫu trình tự¶
Một mẫu trình tự chứa một số mẫu con được so khớp với các phần tử trình tự. Cú pháp tương tự như việc giải nén một danh sách hoặc bộ dữ liệu.
sequence_pattern: "[" [maybe_sequence_pattern] "]" | "(" [open_sequence_pattern] ")" open_sequence_pattern:maybe_star_pattern"," [maybe_sequence_pattern] maybe_sequence_pattern: ",".maybe_star_pattern+ ","? maybe_star_pattern:star_pattern|patternstar_pattern: "*" (capture_pattern|wildcard_pattern)
Không có sự khác biệt nếu sử dụng dấu ngoặc đơn hoặc dấu ngoặc vuông cho các mẫu chuỗi (tức là (...) so với [...] ).
Ghi chú
Một mẫu duy nhất được đặt trong dấu ngoặc đơn không có dấu phẩy ở cuối (ví dụ: (3 | 4)) là group pattern. Mặc dù một mẫu đơn được đặt trong dấu ngoặc vuông (ví dụ: [3 | 4]) vẫn là một mẫu tuần tự.
Nhiều nhất một mẫu con sao có thể ở dạng mẫu tuần tự. Mẫu con ngôi sao có thể xuất hiện ở bất kỳ vị trí nào. Nếu không có mẫu con hình sao thì mẫu chuỗi là mẫu chuỗi có độ dài cố định; mặt khác nó là một mẫu chuỗi có độ dài thay đổi.
Sau đây là luồng logic để khớp mẫu trình tự với giá trị chủ đề:
Nếu giá trị chủ đề không phải là một chuỗi [2] thì mẫu chuỗi không thành công.
Nếu giá trị chủ đề là một phiên bản của
str,byteshoặcbytearraythì mẫu trình tự không thành công.Các bước tiếp theo phụ thuộc vào việc mẫu trình tự có độ dài cố định hay thay đổi.
Nếu mẫu trình tự có độ dài cố định:
Nếu độ dài của chuỗi chủ đề không bằng số lượng mẫu con thì mẫu chuỗi không thành công
Các mẫu con trong mẫu trình tự được khớp với các mục tương ứng của chúng trong chuỗi chủ đề từ trái sang phải. Việc so khớp sẽ dừng ngay khi mẫu con không thành công. Nếu tất cả các mẫu con thành công trong việc khớp mục tương ứng của chúng thì mẫu trình tự sẽ thành công.
Mặt khác, nếu mẫu trình tự có độ dài thay đổi:
Nếu độ dài của chuỗi chủ đề nhỏ hơn số lượng mẫu con không phải sao thì mẫu chuỗi không thành công.
Các mẫu con không phải ngôi sao hàng đầu được khớp với các mục tương ứng của chúng như đối với các chuỗi có độ dài cố định.
Nếu bước trước thành công, mẫu con ngôi sao khớp với danh sách được tạo thành từ các mục chủ đề còn lại, loại trừ các mục còn lại tương ứng với các mẫu con không phải ngôi sao theo sau mẫu con ngôi sao.
Các mẫu con không phải dấu sao còn lại được khớp với các mục chủ đề tương ứng của chúng, như đối với một chuỗi có độ dài cố định.
Ghi chú
Độ dài của chuỗi chủ đề được lấy thông qua
len()(tức là thông qua giao thức__len__()). Độ dài này có thể được trình thông dịch lưu vào bộ nhớ đệm theo cách tương tự như value patterns.
Nói một cách đơn giản, [P1, P2, P3, ... , P<N>] chỉ khớp nếu tất cả những điều sau đây xảy ra:
kiểm tra
<subject>là một chuỗilen(subject) == <N>P1khớp với<subject>[0](lưu ý rằng trận đấu này cũng có thể liên kết tên)P2khớp với<subject>[1](lưu ý rằng trận đấu này cũng có thể liên kết tên)... v.v. cho mẫu/phần tử tương ứng.
8.6.4.9. Mẫu ánh xạ¶
Mẫu ánh xạ chứa một hoặc nhiều mẫu khóa-giá trị. Cú pháp tương tự như việc xây dựng một từ điển. Cú pháp:
mapping_pattern: "{" [items_pattern] "}" items_pattern: ",".key_value_pattern+ ","? key_value_pattern: (literal_pattern|value_pattern) ":"pattern|double_star_patterndouble_star_pattern: "**"capture_pattern
Nhiều nhất một mẫu sao đôi có thể nằm trong mẫu ánh xạ. Mẫu sao đôi phải là mẫu con cuối cùng trong mẫu ánh xạ.
Các khóa trùng lặp trong các mẫu ánh xạ không được phép. Các khóa chữ trùng lặp sẽ tăng SyntaxError. Hai khóa có cùng giá trị sẽ tạo ra ValueError khi chạy.
Sau đây là luồng logic để khớp mẫu ánh xạ với giá trị chủ đề:
Nếu giá trị chủ đề không phải là ánh xạ [3] thì mẫu ánh xạ không thành công.
Nếu mọi khóa được cung cấp trong mẫu ánh xạ đều có trong ánh xạ chủ đề và mẫu cho mỗi khóa khớp với mục tương ứng của ánh xạ chủ đề thì mẫu ánh xạ sẽ thành công.
Nếu các khóa trùng lặp được phát hiện trong mẫu ánh xạ, mẫu đó được coi là không hợp lệ. Một
SyntaxErrorđược nâng lên cho các giá trị bằng chữ trùng lặp; hoặcValueErrorcho các khóa được đặt tên có cùng giá trị.
Ghi chú
Các cặp khóa-giá trị được khớp bằng cách sử dụng dạng hai đối số của phương thức get() của đối tượng ánh xạ. Các cặp khóa-giá trị trùng khớp phải có sẵn trong ánh xạ và không được tạo nhanh chóng thông qua __missing__() hoặc __getitem__().
Nói một cách đơn giản, {KEY1: P1, KEY2: P2, ... } chỉ khớp nếu tất cả những điều sau đây xảy ra:
kiểm tra
<subject>có phải là bản đồ khôngKEY1 in <subject>P1khớp với<subject>[KEY1]... v.v. cho cặp KEY/mẫu tương ứng.
8.6.4.10. Mẫu lớp¶
Một mẫu lớp đại diện cho một lớp và các đối số từ khóa và vị trí của nó (nếu có). Cú pháp:
class_pattern:name_or_attr"(" [pattern_arguments","?] ")" pattern_arguments:positional_patterns[","keyword_patterns] |keyword_patternspositional_patterns: ",".pattern+ keyword_patterns: ",".keyword_pattern+ keyword_pattern: NAME "="pattern
Từ khóa giống nhau không được lặp lại trong các mẫu lớp.
Sau đây là luồng logic để khớp mẫu lớp với giá trị chủ đề:
Nếu
name_or_attrkhông phải là phiên bản củatypedựng sẵn, hãy tăngTypeError.Nếu giá trị chủ đề không phải là phiên bản của
name_or_attr(được kiểm tra quaisinstance()), mẫu lớp sẽ không thành công.Nếu không có đối số mẫu nào thì mẫu đó sẽ thành công. Mặt khác, các bước tiếp theo sẽ phụ thuộc vào việc có các mẫu đối số từ khóa hoặc vị trí hay không.
Đối với một số loại dựng sẵn (được chỉ định bên dưới), một mẫu con vị trí duy nhất được chấp nhận sẽ khớp với toàn bộ chủ đề; đối với các loại này, mẫu từ khóa cũng hoạt động như đối với các loại khác.
Nếu chỉ có các mẫu từ khóa, chúng sẽ được xử lý lần lượt như sau:
Từ khóa được tra cứu như một thuộc tính của chủ đề.
Nếu điều này tạo ra một ngoại lệ khác với
AttributeError, ngoại lệ đó sẽ xuất hiện.Nếu điều này làm tăng
AttributeErrorthì mẫu lớp đã thất bại.Mặt khác, mẫu con được liên kết với mẫu từ khóa sẽ được khớp với giá trị thuộc tính của chủ đề. Nếu điều này không thành công thì mẫu lớp không thành công; nếu điều này thành công, kết quả sẽ chuyển sang từ khóa tiếp theo.
Nếu tất cả các mẫu từ khóa thành công thì mẫu lớp sẽ thành công.
Nếu có bất kỳ mẫu vị trí nào, chúng sẽ được chuyển đổi thành mẫu từ khóa bằng cách sử dụng thuộc tính
__match_args__trên lớpname_or_attrtrước khi khớp:Tương đương với
getattr(cls, "__match_args__", ())được gọi.Nếu điều này gây ra một ngoại lệ, ngoại lệ đó sẽ nổi lên.
Nếu giá trị trả về không phải là một bộ, thì quá trình chuyển đổi không thành công và
TypeErrorđược tăng lên.Nếu có nhiều mẫu vị trí hơn
len(cls.__match_args__),TypeErrorsẽ được nâng lên.Mặt khác, mẫu vị trí
iđược chuyển đổi thành mẫu từ khóa bằng cách sử dụng__match_args__[i]làm từ khóa.__match_args__[i]phải là một chuỗi; nếu khôngTypeErrorđược nâng lên.Nếu có từ khóa trùng lặp,
TypeErrorsẽ được nâng lên.
Khi tất cả các mẫu vị trí đã được chuyển đổi thành mẫu từ khóa, kết quả khớp sẽ tiếp tục như thể chỉ có các mẫu từ khóa.
Đối với các loại tích hợp sau đây, việc xử lý các mẫu con vị trí là khác nhau:
Các lớp này chấp nhận một đối số vị trí duy nhất và mẫu ở đó được khớp với toàn bộ đối tượng chứ không phải một thuộc tính. Ví dụ:
int(0|1)khớp với giá trị0nhưng không khớp với giá trị0.0.
Nói một cách đơn giản, CLS(P1, attr=P2) chỉ khớp nếu điều sau xảy ra:
isinstance(<subject>, CLS)chuyển đổi
P1thành mẫu từ khóa bằngCLS.__match_args__Đối với mỗi đối số từ khóa
attr=P2:hasattr(<subject>, "attr")P2khớp với<subject>.attr
... v.v. cho cặp đối số/mẫu từ khóa tương ứng.
8.7. định nghĩa hàm¶
Định nghĩa hàm xác định đối tượng hàm do người dùng định nghĩa (xem phần Hệ thống phân cấp loại tiêu chuẩn):
funcdef: [decorators] "def"funcname[type_params] "(" [parameter_list] ")" ["->"expression] ":"suitedecorators:decorator+ decorator: "@"assignment_expressionNEWLINE parameter_list:defparameter(","defparameter)* "," "/" ["," [parameter_list_no_posonly]] |parameter_list_no_posonlyparameter_list_no_posonly:defparameter(","defparameter)* ["," [parameter_list_starargs]] |parameter_list_starargsparameter_list_starargs: "*" [star_parameter] (","defparameter)* ["," [parameter_star_kwargs]] | "*" (","defparameter)+ ["," [parameter_star_kwargs]] |parameter_star_kwargsparameter_star_kwargs: "**"parameter[","] parameter:identifier[":"expression] star_parameter:identifier[":" ["*"]expression] defparameter:parameter["="expression] funcname:identifier
Định nghĩa hàm là một câu lệnh có thể thực thi được. Việc thực thi của nó liên kết tên hàm trong không gian tên cục bộ hiện tại với một đối tượng hàm (một trình bao bọc xung quanh mã thực thi của hàm). Đối tượng hàm này chứa tham chiếu đến không gian tên toàn cục hiện tại làm không gian tên toàn cục sẽ được sử dụng khi hàm được gọi.
Định nghĩa hàm không thực thi phần thân hàm; điều này chỉ được thực thi khi hàm được gọi. [4]
Một định nghĩa hàm có thể được bao bọc bởi một hoặc nhiều biểu thức decorator. Các biểu thức trang trí được đánh giá khi hàm được xác định, trong phạm vi chứa định nghĩa hàm. Kết quả phải là một kết quả có thể gọi được, được gọi với đối tượng hàm làm đối số duy nhất. Giá trị trả về được liên kết với tên hàm thay vì đối tượng hàm. Nhiều trang trí được áp dụng theo kiểu lồng nhau. Ví dụ: đoạn mã sau
@f1(arg)
@f2
def func(): vượt qua
gần tương đương với
def func(): vượt qua
func = f1(arg)(f2(func))
ngoại trừ chức năng ban đầu không tạm thời bị ràng buộc với tên func.
Thay đổi trong phiên bản 3.9: Các chức năng có thể được trang trí bằng bất kỳ assignment_expression hợp lệ nào. Trước đây, ngữ pháp hạn chế hơn nhiều; xem PEP 614 để biết chi tiết.
Danh sách type parameters có thể được đưa ra trong dấu ngoặc vuông giữa tên hàm và dấu ngoặc đơn mở cho danh sách tham số của nó. Điều này cho bộ kiểm tra kiểu tĩnh biết rằng hàm này là chung. Trong thời gian chạy, các tham số loại có thể được truy xuất từ thuộc tính __type_params__ của hàm. Xem Hàm chung để biết thêm.
Thay đổi trong phiên bản 3.12: Danh sách tham số loại là tính năng mới trong Python 3.12.
Khi một hoặc nhiều parameters có dạng parameter = expression, hàm này được cho là có "giá trị tham số mặc định". Đối với tham số có giá trị mặc định, argument tương ứng có thể bị bỏ qua khỏi cuộc gọi, trong trường hợp đó giá trị mặc định của tham số được thay thế. Nếu một tham số có giá trị mặc định thì tất cả các tham số theo sau cho đến "*" cũng phải có giá trị mặc định --- đây là một hạn chế về cú pháp không được ngữ pháp thể hiện.
Default parameter values are evaluated from left to right when the function definition is executed. Điều này có nghĩa là biểu thức được đánh giá một lần khi hàm được xác định và giá trị "được tính trước" tương tự được sử dụng cho mỗi lệnh gọi. Điều này đặc biệt quan trọng để hiểu khi giá trị tham số mặc định là đối tượng có thể thay đổi, chẳng hạn như danh sách hoặc từ điển: nếu hàm sửa đổi đối tượng (ví dụ: bằng cách thêm một mục vào danh sách), giá trị tham số mặc định có hiệu lực được sửa đổi. Đây thường không phải là những gì đã được dự định. Một cách giải quyết vấn đề này là sử dụng None làm mặc định và kiểm tra nó một cách rõ ràng trong phần nội dung của hàm, ví dụ::
def whats_on_the_telly(chim cánh cụt=Không):
nếu chim cánh cụt là Không có:
chim cánh cụt = []
penguin.append("tài sản của sở thú")
chim cánh cụt trở về
Ngữ nghĩa của lệnh gọi hàm được mô tả chi tiết hơn trong phần Cuộc gọi. Lệnh gọi hàm luôn gán giá trị cho tất cả tham số được đề cập trong danh sách tham số, từ đối số vị trí, từ đối số từ khóa hoặc từ giá trị mặc định. Nếu có dạng "*identifier", nó sẽ được khởi tạo thành một bộ dữ liệu nhận bất kỳ tham số vị trí dư thừa nào, mặc định là bộ dữ liệu trống. Nếu có biểu mẫu "**identifier", nó sẽ được khởi tạo thành một ánh xạ có thứ tự mới nhận bất kỳ đối số từ khóa dư thừa nào, mặc định là một ánh xạ trống mới cùng loại. Các tham số sau "*" hoặc "*identifier" là các tham số chỉ có từ khóa và chỉ có thể được chuyển bằng đối số từ khóa. Các tham số trước "/" là các tham số chỉ có vị trí và chỉ có thể được truyền bằng các đối số vị trí.
Thay đổi trong phiên bản 3.8: Cú pháp tham số hàm / có thể được sử dụng để biểu thị các tham số chỉ có vị trí. Xem PEP 570 để biết chi tiết.
Các tham số có thể có annotation có dạng ": expression" theo sau tên tham số. Bất kỳ tham số nào cũng có thể có chú thích, ngay cả những tham số có dạng *identifier hoặc **identifier. (Trong trường hợp đặc biệt, các tham số có dạng *identifier có thể có chú thích ": *expression".) Các hàm có thể có chú thích "return" có dạng "-> expression" sau danh sách tham số. Các chú thích này có thể là bất kỳ biểu thức Python hợp lệ nào. Sự hiện diện của các chú thích không làm thay đổi ngữ nghĩa của hàm. Xem Chú thích để biết thêm thông tin về chú thích.
Thay đổi trong phiên bản 3.11: Các tham số có dạng "*identifier" có thể có chú thích ": *expression". Xem PEP 646.
Cũng có thể tạo các hàm ẩn danh (các hàm không bị ràng buộc với tên) để sử dụng ngay trong các biểu thức. Điều này sử dụng biểu thức lambda, được mô tả trong phần Lambda. Lưu ý rằng biểu thức lambda chỉ là cách viết tắt của định nghĩa hàm đơn giản hóa; một hàm được xác định trong câu lệnh "def" có thể được chuyển đi hoặc gán cho một tên khác giống như một hàm được xác định bởi biểu thức lambda. Biểu mẫu "def" thực sự mạnh hơn vì nó cho phép thực thi nhiều câu lệnh và chú thích.
Programmer's note: Hàm là đối tượng hạng nhất. Câu lệnh "def" được thực thi bên trong định nghĩa hàm sẽ xác định một hàm cục bộ có thể được trả về hoặc truyền đi. Các biến miễn phí được sử dụng trong hàm lồng nhau có thể truy cập các biến cục bộ của hàm chứa def. Xem phần Đặt tên và ràng buộc để biết chi tiết.
Xem thêm
- PEP 3107 - Chú thích chức năng
Đặc tả ban đầu cho chú thích chức năng.
- PEP 484 - Gợi ý gõ
Định nghĩa ý nghĩa tiêu chuẩn cho chú thích: gợi ý loại.
- PEP 526 - Cú pháp cho chú thích biến
Khả năng gõ khai báo biến gợi ý, bao gồm biến lớp và biến thể hiện.
- PEP 563 - Hoãn đánh giá các chú thích
Hỗ trợ chuyển tiếp các tham chiếu trong chú thích bằng cách giữ chú thích ở dạng chuỗi trong thời gian chạy thay vì đánh giá háo hức.
- PEP 318 - Trang trí cho hàm và phương thức
Trang trí chức năng và phương pháp đã được giới thiệu. Trình trang trí lớp đã được giới thiệu trong PEP 3129.
8.8. Định nghĩa lớp¶
Một định nghĩa lớp định nghĩa một đối tượng lớp (xem phần Hệ thống phân cấp loại tiêu chuẩn):
classdef: [decorators] "class"classname[type_params] [inheritance] ":"suiteinheritance: "(" [argument_list] ")" classname:identifier
Một định nghĩa lớp là một câu lệnh có thể thực thi được. Danh sách kế thừa thường đưa ra danh sách các lớp cơ sở (xem Siêu lớp để biết các mục đích sử dụng nâng cao hơn), vì vậy mỗi mục trong danh sách sẽ đánh giá thành một đối tượng lớp cho phép phân lớp con. Theo mặc định, các lớp không có danh sách kế thừa sẽ kế thừa từ lớp cơ sở object; do đó,
lớp Foo:
vượt qua
tương đương với
lớp Foo (đối tượng):
vượt qua
Sau đó, bộ của lớp sẽ được thực thi trong khung thực thi mới (xem Đặt tên và ràng buộc), sử dụng không gian tên cục bộ mới được tạo và không gian tên toàn cầu ban đầu. (Thông thường, bộ phần mềm chứa hầu hết các định nghĩa hàm.) Khi bộ phần mềm của lớp kết thúc quá trình thực thi, khung thực thi của nó sẽ bị loại bỏ nhưng không gian tên cục bộ của nó được lưu. [5] Sau đó, một đối tượng lớp được tạo bằng cách sử dụng danh sách kế thừa cho các lớp cơ sở và vùng tên cục bộ đã lưu cho từ điển thuộc tính. Tên lớp được liên kết với đối tượng lớp này trong không gian tên cục bộ ban đầu.
Thứ tự các thuộc tính được xác định trong nội dung lớp được giữ nguyên trong __dict__ của lớp mới. Lưu ý rằng điều này chỉ đáng tin cậy ngay sau khi lớp được tạo và chỉ dành cho các lớp được xác định bằng cú pháp định nghĩa.
Việc tạo lớp có thể được tùy chỉnh rất nhiều bằng cách sử dụng metaclasses.
Các lớp học cũng có thể được trang trí: giống như khi trang trí các chức năng,
@f1(arg)
@f2
lớp Foo: vượt qua
gần tương đương với
lớp Foo: vượt qua
Foo = f1(arg)(f2(Foo))
Các quy tắc đánh giá cho các biểu thức trang trí cũng giống như các quy tắc đánh giá cho các biểu thức trang trí hàm. Kết quả sau đó được liên kết với tên lớp.
Thay đổi trong phiên bản 3.9: Các lớp học có thể được trang trí bằng bất kỳ assignment_expression hợp lệ nào. Trước đây, ngữ pháp hạn chế hơn nhiều; xem PEP 614 để biết chi tiết.
Danh sách type parameters có thể được đưa ra trong dấu ngoặc vuông ngay sau tên lớp. Điều này cho bộ kiểm tra kiểu tĩnh biết rằng lớp đó là chung. Khi chạy, các tham số kiểu có thể được lấy từ thuộc tính __type_params__ của lớp. Xem Lớp học chung để biết thêm.
Thay đổi trong phiên bản 3.12: Danh sách tham số loại là tính năng mới trong Python 3.12.
Programmer's note: Các biến được định nghĩa trong định nghĩa lớp là các thuộc tính của lớp; chúng được chia sẻ bởi các trường hợp. Các thuộc tính phiên bản có thể được đặt trong một phương thức với self.name = value. Cả hai thuộc tính lớp và phiên bản đều có thể truy cập được thông qua ký hiệu "self.name" và thuộc tính phiên bản sẽ ẩn thuộc tính lớp có cùng tên khi được truy cập theo cách này. Thuộc tính lớp có thể được sử dụng làm mặc định cho các thuộc tính thể hiện, nhưng việc sử dụng các giá trị có thể thay đổi có thể dẫn đến kết quả không mong muốn. Descriptors có thể được sử dụng để tạo các biến thể hiện với các chi tiết triển khai khác nhau.
Xem thêm
8.9. Coroutine¶
Added in version 3.5.
8.9.1. Định nghĩa hàm coroutine¶
async_funcdef: [decorators] "async" "def"funcname"(" [parameter_list] ")" ["->"expression] ":"suite
Việc thực thi các coroutine Python có thể bị tạm dừng và tiếp tục lại ở nhiều thời điểm (xem coroutine). Biểu thức await, async for và async with chỉ có thể được sử dụng trong phần nội dung của hàm coroutine.
Các hàm được xác định bằng cú pháp async def luôn là các hàm coroutine, ngay cả khi chúng không chứa từ khóa await hoặc async.
SyntaxError sẽ sử dụng biểu thức yield from bên trong phần thân của hàm coroutine.
Một ví dụ về hàm coroutine:
async def func(param1, param2):
do_stuff()
đang chờ some_coroutine()
Thay đổi trong phiên bản 3.7: await và async hiện là từ khóa; trước đây chúng chỉ được xử lý như vậy bên trong phần thân của một hàm coroutine.
8.9.2. Tuyên bố async for¶
async_for_stmt: "async" for_stmt
Một asynchronous iterable cung cấp một phương thức __aiter__ trả về trực tiếp một asynchronous iterator, có thể gọi mã không đồng bộ trong phương thức __anext__ của nó.
Câu lệnh async for cho phép lặp lại thuận tiện trên các lần lặp không đồng bộ.
Đoạn mã sau:
không đồng bộ cho TARGET trong ITER:
SUITE
khác:
SUITE2
Về mặt ngữ nghĩa tương đương với:
iter = (ITER).__aiter__()
đang chạy = Đúng
trong khi chạy:
thử:
TARGET = đang chờ iter.__anext__()
ngoại trừ StopAsyncIteration:
đang chạy = Sai
khác:
SUITE
khác:
SUITE2
ngoại trừ việc special method lookup ngầm định được sử dụng cho __aiter__() và __anext__().
SyntaxError sẽ sử dụng câu lệnh async for bên ngoài phần thân của hàm coroutine.
8.9.3. Tuyên bố async with¶
async_with_stmt: "async" with_stmt
Một asynchronous context manager là một context manager có thể tạm dừng thực thi trong các phương thức enter và exit của nó.
Đoạn mã sau:
không đồng bộ với EXPRESSION là TARGET:
SUITE
về mặt ngữ nghĩa tương đương với:
người quản lý = (EXPRESSION)
aenter = người quản lý.__aenter__
aexit = người quản lý.__aexit__
giá trị = đang chờ aenter()
hit_ngoại trừ = Sai
thử:
TARGET = giá trị
SUITE
ngoại trừ:
hit_ngoại trừ = Đúng
nếu không chờ aexit(*sys.exc_info()):
nâng cao
cuối cùng:
nếu không nhấn_ngoại trừ:
đang chờ aexit(Không, Không, Không)
ngoại trừ việc special method lookup ngầm định được sử dụng cho __aenter__() và __aexit__().
SyntaxError sẽ sử dụng câu lệnh async with bên ngoài phần thân của hàm coroutine.
Xem thêm
- PEP 492 - Coroutine với cú pháp không đồng bộ và chờ đợi
Đề xuất đã biến coroutine thành một khái niệm độc lập thích hợp trong Python và bổ sung thêm cú pháp hỗ trợ.
8.10. Nhập danh sách tham số¶
Added in version 3.12.
Thay đổi trong phiên bản 3.13: Hỗ trợ cho các giá trị mặc định đã được thêm vào (xem PEP 696).
type_params: "["type_param(","type_param)* "]" type_param:typevar|typevartuple|paramspectypevar:identifier(":"expression)? ("="expression)? typevartuple: "*"identifier("="expression)? paramspec: "**"identifier("="expression)?
Functions (bao gồm coroutines), classes và type aliases có thể chứa danh sách tham số loại:
def max[T](args: list[T]) -> T:
...
async def amax[T](args: list[T]) -> T:
...
Túi hạng [T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...
gõ ListOrSet[T] = list[T] | đặt [T]
Về mặt ngữ nghĩa, điều này chỉ ra rằng bí danh của hàm, lớp hoặc kiểu là chung đối với một biến kiểu. Thông tin này chủ yếu được sử dụng bởi các trình kiểm tra kiểu tĩnh và trong thời gian chạy, các đối tượng chung hoạt động giống như các đối tượng không chung chung của chúng.
Tham số loại được khai báo trong dấu ngoặc vuông ([]) ngay sau tên hàm, lớp hoặc bí danh loại. Các tham số loại có thể truy cập được trong phạm vi của đối tượng chung, nhưng không thể truy cập được ở nơi khác. Vì vậy, sau khi khai báo def func[T](): pass, tên T không có sẵn trong phạm vi mô-đun. Dưới đây, ngữ nghĩa của các đối tượng chung được mô tả chính xác hơn. Phạm vi của các tham số loại được mô hình hóa bằng một hàm đặc biệt (về mặt kỹ thuật là annotation scope) bao bọc việc tạo đối tượng chung.
Các hàm, lớp và bí danh loại chung có thuộc tính __type_params__ liệt kê các tham số loại của chúng.
Tham số loại có ba loại:
typing.TypeVar, được giới thiệu bằng một cái tên đơn giản (ví dụ:T). Về mặt ngữ nghĩa, điều này thể hiện một kiểu duy nhất đối với trình kiểm tra kiểu.typing.TypeVarTuple, được giới thiệu bằng tên có tiền tố là một dấu hoa thị (ví dụ:*Ts). Về mặt ngữ nghĩa, đây là viết tắt của một bộ thuộc bất kỳ số loại nào.typing.ParamSpec, được giới thiệu bằng tên có tiền tố là hai dấu hoa thị (ví dụ:**P). Về mặt ngữ nghĩa, đây là viết tắt của các tham số có thể gọi được.
Khai báo typing.TypeVar có thể định nghĩa bounds và constraints bằng dấu hai chấm (:) theo sau là một biểu thức. Một biểu thức sau dấu hai chấm biểu thị giới hạn (ví dụ: T: int). Về mặt ngữ nghĩa, điều này có nghĩa là typing.TypeVar chỉ có thể biểu thị các loại là kiểu con của giới hạn này. Một bộ biểu thức được đặt trong ngoặc đơn sau dấu hai chấm biểu thị một tập hợp các ràng buộc (ví dụ: T: (str, bytes)). Mỗi thành viên của bộ dữ liệu phải là một loại (một lần nữa, điều này không được thực thi khi chạy). Các biến loại bị ràng buộc chỉ có thể đảm nhận một trong các loại trong danh sách các ràng buộc.
Đối với typing.TypeVars được khai báo bằng cú pháp danh sách tham số loại, giới hạn và ràng buộc không được đánh giá khi đối tượng chung được tạo mà chỉ khi giá trị được truy cập rõ ràng thông qua các thuộc tính __bound__ và __constraints__. Để thực hiện điều này, các giới hạn hoặc ràng buộc được đánh giá trong một annotation scope riêng biệt.
typing.TypeVarTuples và typing.ParamSpecs không thể có giới hạn hoặc ràng buộc.
Tất cả ba loại tham số loại cũng có thể có default value, được sử dụng khi tham số loại không được cung cấp rõ ràng. Điều này được thêm vào bằng cách thêm một dấu bằng (=) theo sau là một biểu thức. Giống như các giới hạn và ràng buộc của các biến kiểu, giá trị mặc định không được đánh giá khi đối tượng được tạo mà chỉ khi thuộc tính __default__ của tham số kiểu được truy cập. Để đạt được mục đích này, giá trị mặc định được đánh giá theo một annotation scope riêng biệt. Nếu không có giá trị mặc định nào được chỉ định cho tham số loại, thuộc tính __default__ được đặt thành đối tượng trọng điểm đặc biệt typing.NoDefault.
Ví dụ sau đây cho biết tập hợp đầy đủ các khai báo tham số kiểu được phép:
def overly_generic[
SimpleTypeVar,
KiểuVarWithDefault = int,
KiểuVarWithBound: int,
LoạiVarWithConstraint: (str, byte),
*SimpleTypeVarTuple = (int, float),
**SimpleParamSpec = (str, bytearray),
](
a: SimpleTypeVar,
b: TypeVarWithDefault,
c: TypeVarWithBound,
d: Có thể gọi được [SimpleParamSpec, TypeVarWithConstraints],
*e: SimpleTypeVarTuple,
): ...
8.10.1. Hàm chung¶
Các hàm chung được khai báo như sau:
def func[T](arg: T): ...
Cú pháp này tương đương với:
chú thích-def TYPE_PARAMS_OF_func():
T = gõ.TypeVar("T")
def func(arg: T): ...
func.__type_params__ = (T,)
chức năng trả về
func = TYPE_PARAMS_OF_func()
Ở đây annotation-def biểu thị annotation scope, thực tế không bị ràng buộc với bất kỳ tên nào trong thời gian chạy. (Một quyền tự do khác được thực hiện trong bản dịch: cú pháp không thông qua quyền truy cập thuộc tính trên mô-đun typing mà tạo trực tiếp một phiên bản của typing.TypeVar.)
Các chú thích của các hàm chung được đánh giá trong phạm vi chú thích được sử dụng để khai báo các tham số kiểu, nhưng các giá trị mặc định và trang trí của hàm thì không.
Ví dụ sau minh họa các quy tắc xác định phạm vi cho những trường hợp này, cũng như các loại tham số loại bổ sung:
@trang trí
def func[T: int, *Ts, **P](*args: *Ts, arg: Callable[P, T] = some_default):
...
Ngoại trừ lazy evaluation của giới hạn TypeVar, điều này tương đương với:
DEFAULT_OF_arg = some_default
chú thích-def TYPE_PARAMS_OF_func():
chú thích-def BOUND_OF_T():
trả về int
# In thực tế, BOUND_OF_T() chỉ được đánh giá theo yêu cầu.
T = type.TypeVar("T",bound=BOUND_OF_T())
Ts = gõ.TypeVarTuple("Ts")
P = gõ.ParamSpec("P")
def func(*args: *Ts, arg: Callable[P, T] = DEFAULT_OF_arg):
...
func.__type_params__ = (T, Ts, P)
chức năng trả về
func = trang trí(TYPE_PARAMS_OF_func())
Các tên viết hoa như DEFAULT_OF_arg không thực sự bị ràng buộc khi chạy.
8.10.2. Lớp học chung¶
Các lớp generic được khai báo như sau:
Túi lớp [T]: ...
Cú pháp này tương đương với:
chú thích-def TYPE_PARAMS_OF_Bag():
T = gõ.TypeVar("T")
Túi lớp(typing.Generic[T]):
__type_params__ = (T,)
...
túi trả lại
Túi = TYPE_PARAMS_OF_Bag()
Ở đây một lần nữa annotation-def (không phải từ khóa thực) chỉ ra annotation scope và tên TYPE_PARAMS_OF_Bag không thực sự bị ràng buộc trong thời gian chạy.
Các lớp generic kế thừa ngầm từ typing.Generic. Các lớp cơ sở và đối số từ khóa của các lớp chung được đánh giá trong phạm vi loại cho các tham số loại và các trình trang trí được đánh giá bên ngoài phạm vi đó. Điều này được minh họa bằng ví dụ này:
@trang trí
Túi lớp(Base[T], arg=T): ...
Điều này tương đương với:
chú thích-def TYPE_PARAMS_OF_Bag():
T = gõ.TypeVar("T")
túi lớp(Base[T], type.Generic[T], arg=T):
__type_params__ = (T,)
...
túi trả lại
Túi = trang trí(TYPE_PARAMS_OF_Bag())
8.10.3. Bí danh loại chung¶
Câu lệnh type cũng có thể được sử dụng để tạo bí danh loại chung:
gõ ListOrSet[T] = list[T] | đặt [T]
Ngoại trừ giá trị lazy evaluation, giá trị này tương đương với:
chú thích-def TYPE_PARAMS_OF_ListOrSet():
T = gõ.TypeVar("T")
chú thích-def VALUE_OF_ListOrSet():
danh sách trả về[T] | đặt [T]
# In thực tế, giá trị được đánh giá một cách lười biếng
trả về gõ.TypeAliasType("ListOrSet", VALUE_OF_ListOrSet(), type_params=(T,))
ListOrSet = TYPE_PARAMS_OF_ListOrSet()
Ở đây, annotation-def (không phải từ khóa thực sự) biểu thị annotation scope. Các tên viết hoa như TYPE_PARAMS_OF_ListOrSet không thực sự bị ràng buộc khi chạy.
8.11. Chú thích¶
Thay đổi trong phiên bản 3.14: Các chú thích bây giờ được đánh giá một cách lười biếng theo mặc định.
Các biến và tham số hàm có thể mang annotations, được tạo bằng cách thêm dấu hai chấm sau tên, theo sau là một biểu thức
x: chú thích = 1
def f(param: chú thích): ...
Các hàm cũng có thể mang chú thích trả về theo sau một mũi tên
def f() -> chú thích: ...
Các chú thích thường được sử dụng cho type hints, nhưng điều này không được ngôn ngữ thực thi và nói chung các chú thích có thể chứa các biểu thức tùy ý. Sự hiện diện của các chú thích không làm thay đổi ngữ nghĩa thời gian chạy của mã, ngoại trừ nếu một số cơ chế được sử dụng để xem xét nội tâm và sử dụng các chú thích (chẳng hạn như dataclasses hoặc functools.singledispatch()).
Theo mặc định, các chú thích được đánh giá một cách lười biếng trong annotation scope. Điều này có nghĩa là chúng không được đánh giá khi mã chứa chú thích được đánh giá. Thay vào đó, trình thông dịch lưu thông tin có thể được sử dụng để đánh giá chú thích sau này nếu được yêu cầu. Mô-đun annotationlib cung cấp các công cụ để đánh giá chú thích.
Nếu có future statement from __future__ import annotations thì tất cả các chú thích sẽ được lưu trữ dưới dạng chuỗi
>>> từ __future__ nhập chú thích
>>> def f(param: chú thích): ...
>>> f.__chú thích__
{'param': 'chú thích'}
Tuyên bố trong tương lai này sẽ không được dùng nữa và bị xóa trong phiên bản tương lai của Python, nhưng không phải trước khi Python 3.13 hết vòng đời (xem PEP 749). Khi nó được sử dụng, các công cụ xem xét nội tâm như annotationlib.get_annotations() và typing.get_type_hints() ít có khả năng giải quyết các chú thích trong thời gian chạy.
Chú thích cuối trang