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, whilefor 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_funcdef
suite:         stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement:     stmt_list NEWLINE | compound_stmt
stmt_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_stmt
try1_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  N:
    foo

đã được dịch sang

ngoại trừ E  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, continuereturn 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 đề exceptelse. 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 ) ":" suite
with_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:

  1. 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.

  2. Zz000zz của trình quản lý bối cảnh được tải để sử dụng sau.

  3. Zz000zz của trình quản lý bối cảnh được tải để sử dụng sau.

  4. Phương thức __enter__() của trình quản lý bối cảnh được gọi.

  5. Nếu một mục tiêu được đưa vào câu lệnh with thì 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.

  6. Bộ phần mềm được thực thi.

  7. 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ố None sẽ đượ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ệnh with.

    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  TARGET:
    SUITE

về mặt ngữ nghĩa tương đương với:

người quản  = (EXPRESSION)
nhập = người quản .__enter__
thoát = người quản .__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__()__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()  a, B()  b:
    SUITE

về mặt ngữ nghĩa tương đương với:

với A() :
    với B()  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()  một,
    B()  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.

Xem thêm

PEP 343 - Câu lệnh "với"

Thông số kỹ thuật, nền tảng và ví dụ cho câu lệnh with của Python.

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 INDENT case_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 matchcasesoft keywords.

Xem thêm

  • PEP 634 -- Khớp mẫu cấu trúc: Đặc điểm kỹ thuật

  • PEP 636 -- Khớp mẫu cấu trúc: Hướng dẫn

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:

  1. 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.

  2. 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.

  3. 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, block bên trong case_block sẽ được thực thi.

    • Nếu không, case_block tiế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:

  1. 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, guard không được đánh giá và khối case tiếp theo sẽ được kiểm tra.

  2. 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ủa RULE (SEP RULE)*

  • ký hiệu !RULE là 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 | pattern
pattern:        as_pattern | or_pattern
closed_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 fdây chữ T không được hỗ trợ.

Các dạng signed_number '+' NUMBERsigned_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, TrueFalse, 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: '_'

_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: attr
attr:          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 | pattern
star_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ủ đề:

  1. 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.

  2. Nếu giá trị chủ đề là một phiên bản của str, bytes hoặc bytearray thì mẫu trình tự không thành công.

  3. 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:

    1. 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

    2. 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:

    1. 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.

    2. 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.

    3. 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.

    4. 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ỗi

  • len(subject) == <N>

  • P1 khớp với <subject>[0] (lưu ý rằng trận đấu này cũng có thể liên kết tên)

  • P2 khớ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_pattern
double_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ủ đề:

  1. Nếu giá trị chủ đề không phải là ánh xạ [3] thì mẫu ánh xạ không thành công.

  2. 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.

  3. 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ặc ValueError cho 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ông

  • KEY1 in <subject>

  • P1 khớ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_patterns
positional_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ủ đề:

  1. Nếu name_or_attr không phải là phiên bản của type dựng sẵn, hãy tăng TypeError.

  2. Nếu giá trị chủ đề không phải là phiên bản của name_or_attr (được kiểm tra qua isinstance()), mẫu lớp sẽ không thành công.

  3. 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:

    1. 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 AttributeError thì 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.

    2. 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ớp name_or_attr trước khi khớp:

    1. 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__), TypeError sẽ đượ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ông TypeError được nâng lên.

      • Nếu có từ khóa trùng lặp, TypeError sẽ được nâng lên.

    2. 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ị 0 như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 P1 thành mẫu từ khóa bằng CLS.__match_args__

  • Đối với mỗi đối số từ khóa attr=P2:

    • hasattr(<subject>, "attr")

    • P2 khớp với <subject>.attr

  • ... v.v. cho cặp đối số/mẫu từ khóa tương ứng.

Xem thêm

  • PEP 634 -- Khớp mẫu cấu trúc: Đặc điểm kỹ thuật

  • PEP 636 -- Khớp mẫu cấu trúc: Hướng dẫn

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] ":" suite
decorators:                decorator+
decorator:                 "@" assignment_expression NEWLINE
parameter_list:            defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]
                             | parameter_list_no_posonly
parameter_list_no_posonly: defparameter ("," defparameter)* ["," [parameter_list_starargs]]
                           | parameter_list_starargs
parameter_list_starargs:   "*" [star_parameter] ("," defparameter)* ["," [parameter_star_kwargs]]
                           | "*" ("," defparameter)+ ["," [parameter_star_kwargs]]
                           | parameter_star_kwargs
parameter_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  Không :
        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] ":" suite
inheritance: "(" [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

PEP 3115 - Siêu dữ liệu trong Python 3000

Đề xuất đã thay đổi cách khai báo siêu dữ liệu theo cú pháp hiện tại và ngữ nghĩa về cách xây dựng các lớp có siêu dữ liệu.

PEP 3129 - Trang trí lớp học

Đề xuất bổ sung thêm trang trí lớp. Trình trang trí hàm và phương thức đã được giới thiệu trong PEP 318.

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 forasync 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: awaitasync 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__()__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 enterexit của nó.

Đoạn mã sau:

không đồng bộ với EXPRESSION  TARGET:
    SUITE

về mặt ngữ nghĩa tương đương với:

người quản  = (EXPRESSION)
aenter = người quản .__aenter__
aexit = người quản .__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__()__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 | paramspec
typevar:      identifier (":" expression)? ("=" expression)?
typevartuple: "*" identifier ("=" expression)?
paramspec:    "**" identifier ("=" expression)?

Functions (bao gồm coroutines), classestype 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:
        ...

 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 boundsconstraints 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____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:  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 = .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 = .TypeVarTuple("Ts")
    P = .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 = .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 = .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:

 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 = .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ề .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()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