Đây là một đề xuất để tạo một cách gán cho các biến trong một biểu thức bằng cách sử dụng ký hiệu def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...0 Show
Là một phần của thay đổi này, cũng có một bản cập nhật cho thứ tự đánh giá mức độ hiểu từ điển để đảm bảo các biểu thức khóa được thực thi trước các biểu thức giá trị (cho phép khóa được liên kết với một tên và sau đó được sử dụng lại như một phần của việc tính toán giá trị tương ứng) Trong quá trình thảo luận về PEP này, người điều hành được gọi một cách không chính thức là “người điều hành hải mã”. Tên chính thức của cấu trúc là “Biểu thức gán” (theo tiêu đề PEP), nhưng chúng cũng có thể được gọi là “Biểu thức được đặt tên” (e. g. việc triển khai tham chiếu CPython sử dụng tên đó trong nội bộ) cơ sở lý luậnĐặt tên cho kết quả của biểu thức là một phần quan trọng của lập trình, cho phép sử dụng tên mô tả thay cho biểu thức dài hơn và cho phép sử dụng lại. Hiện tại, tính năng này chỉ khả dụng ở dạng câu lệnh, khiến tính năng này không khả dụng ở dạng hiểu danh sách và các ngữ cảnh biểu thức khác Ngoài ra, việc đặt tên cho các phần phụ của một biểu thức lớn có thể hỗ trợ trình gỡ lỗi tương tác, cung cấp các móc hiển thị hữu ích và các kết quả từng phần. Không có cách nào để nắm bắt các biểu thức con nội tuyến, điều này sẽ yêu cầu tái cấu trúc mã gốc; . Việc loại bỏ nhu cầu tái cấu trúc giúp giảm khả năng mã vô tình bị thay đổi như một phần của quá trình gỡ lỗi (nguyên nhân phổ biến của Heisenbugs) và dễ dàng hơn để ra lệnh cho lập trình viên khác Tầm quan trọng của mã thựcTrong quá trình phát triển PEP này, nhiều người (cả người ủng hộ và người chỉ trích) có xu hướng một mặt tập trung vào các ví dụ về đồ chơi, mặt khác lại tập trung vào các ví dụ quá phức tạp. Sự nguy hiểm của ví dụ đồ chơi là gấp đôi. chúng thường quá trừu tượng để khiến bất kỳ ai cũng phải thốt lên “ồ, thật hấp dẫn”, và chúng dễ dàng bị bác bỏ với câu “Dù sao thì tôi cũng sẽ không bao giờ viết nó theo cách đó đâu” Sự nguy hiểm của các ví dụ quá phức tạp là chúng cung cấp một ống hút tiện lợi cho những người chỉ trích đề xuất để bác bỏ (“điều đó thật khó hiểu”) Tuy nhiên, có một số sử dụng cho cả ví dụ cực kỳ đơn giản và cực kỳ phức tạp. chúng rất hữu ích để làm rõ ngữ nghĩa dự định. Do đó, sẽ có một số dưới đây Tuy nhiên, để hấp dẫn, các ví dụ nên bắt nguồn từ mã thực, tôi. e. mã được viết mà không hề nghĩ đến PEP này, như một phần của ứng dụng hữu ích, dù lớn hay nhỏ. Tim Peters đã cực kỳ hữu ích bằng cách xem qua kho lưu trữ mã cá nhân của anh ấy và chọn các ví dụ về mã mà anh ấy đã viết (theo quan điểm của anh ấy) sẽ rõ ràng hơn nếu được viết lại bằng cách sử dụng (ít) các biểu thức gán. kết luận của mình. đề xuất hiện tại sẽ cho phép một sự cải thiện khiêm tốn nhưng rõ ràng trong một vài đoạn mã Một cách sử dụng khác của mã thực là để quan sát gián tiếp giá trị mà các lập trình viên đặt vào tính gọn nhẹ. Guido van Rossum đã tìm kiếm thông qua cơ sở mã Dropbox và phát hiện ra một số bằng chứng cho thấy các lập trình viên đánh giá cao việc viết ít dòng hơn các dòng ngắn hơn Trường hợp tại điểm. Guido đã tìm thấy một số ví dụ trong đó một lập trình viên lặp lại một biểu thức con, làm chậm chương trình, để tiết kiệm một dòng mã, e. g. thay vì viết match = re.match(data) group = match.group(1) if match else None họ sẽ viết group = re.match(data).group(1) if re.match(data) else None Một ví dụ khác minh họa rằng các lập trình viên đôi khi phải làm nhiều việc hơn để tiết kiệm thêm một mức thụt đầu dòng match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None Mã này cố khớp với def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...2 ngay cả khi def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...3 khớp (trong trường hợp đó, khớp trên def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...2 không bao giờ được sử dụng). Việc viết lại hiệu quả hơn sẽ là ________số 8 Cú pháp và ngữ nghĩaTrong hầu hết các ngữ cảnh có thể sử dụng các biểu thức Python tùy ý, một biểu thức được đặt tên có thể xuất hiện. Đây là dạng def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...0 trong đó def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...6 là bất kỳ biểu thức Python hợp lệ nào không phải là một bộ dữ liệu không được mở ngoặc đơn và def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...7 là một mã định danh Giá trị của một biểu thức được đặt tên như vậy giống như biểu thức được kết hợp, với tác dụng phụ bổ sung mà mục tiêu được gán giá trị đó def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...2 Trường hợp đặc biệtCó một số chỗ không cho phép biểu thức gán để tránh sự mơ hồ hoặc nhầm lẫn của người dùng
Phạm vi mục tiêuMột biểu thức gán không giới thiệu một phạm vi mới. Trong hầu hết các trường hợp, phạm vi mà mục tiêu sẽ bị ràng buộc là tự giải thích. nó là phạm vi hiện tại. Nếu phạm vi này chứa khai báo def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...73 hoặc def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...74 cho mục tiêu, thì biểu thức gán sẽ tôn trọng điều đó. Một lambda (là một định nghĩa hàm rõ ràng, nếu ẩn danh) được tính là một phạm vi cho mục đích này Có một trường hợp đặc biệt. một biểu thức gán xảy ra trong một danh sách, tập hợp hoặc đọc chính tả hoặc trong một biểu thức trình tạo (dưới đây được gọi chung là "sự hiểu") liên kết mục tiêu trong phạm vi chứa, tôn trọng khai báo def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...73 hoặc def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...74 cho mục tiêu trong phạm vi đó, nếu một . Với mục đích của quy tắc này, phạm vi chứa của một cách hiểu lồng nhau là phạm vi chứa cách hiểu ngoài cùng. Lambda được tính là phạm vi chứa Động lực cho trường hợp đặc biệt này là gấp đôi. Đầu tiên, nó cho phép chúng ta nắm bắt một cách thuận tiện một “nhân chứng” cho một biểu thức def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...77, hoặc một ví dụ ngược lại cho def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...78, chẳng hạn group = re.match(data).group(1) if re.match(data) else None2 Thứ hai, nó cho phép một cách nhỏ gọn để cập nhật trạng thái có thể thay đổi từ một cách hiểu, chẳng hạn group = re.match(data).group(1) if re.match(data) else None3 Tuy nhiên, tên đích của biểu thức gán không được giống với tên đích của ____ xuất hiện trong bất kỳ cách hiểu nào có chứa biểu thức gán. Các tên sau là cục bộ đối với cách hiểu mà chúng xuất hiện, do đó, sẽ mâu thuẫn nếu việc sử dụng cùng tên để chỉ phạm vi chứa cách hiểu ngoài cùng thay thế Ví dụ: group = re.match(data).group(1) if re.match(data) else None00 không hợp lệ. phần group = re.match(data).group(1) if re.match(data) else None01 xác định rằng group = re.match(data).group(1) if re.match(data) else None02 là cục bộ để hiểu, nhưng phần group = re.match(data).group(1) if re.match(data) else None03 khẳng định rằng group = re.match(data).group(1) if re.match(data) else None02 không phải là cục bộ để hiểu. Lý do tương tự cũng làm cho các ví dụ này không hợp lệ group = re.match(data).group(1) if re.match(data) else None4 Mặc dù về mặt kỹ thuật có thể gán ngữ nghĩa nhất quán cho những trường hợp này, nhưng rất khó để xác định liệu những ngữ nghĩa đó có thực sự có ý nghĩa hay không khi không có trường hợp sử dụng thực tế. Theo đó, việc triển khai tham chiếu sẽ đảm bảo rằng các trường hợp như vậy tăng group = re.match(data).group(1) if re.match(data) else None05, thay vì thực thi với hành vi được xác định bởi việc triển khai Hạn chế này áp dụng ngay cả khi biểu thức gán không bao giờ được thực hiện group = re.match(data).group(1) if re.match(data) else None5 Đối với nội dung hiểu (phần trước từ khóa “for” đầu tiên) và biểu thức bộ lọc (phần sau “if” và trước bất kỳ “for” lồng nhau nào), hạn chế này chỉ áp dụng cho các tên đích cũng được sử dụng làm biến lặp trong . Các biểu thức lambda xuất hiện ở những vị trí này giới thiệu một phạm vi chức năng rõ ràng mới và do đó có thể sử dụng các biểu thức gán mà không có hạn chế bổ sung nào Do các ràng buộc thiết kế trong quá trình triển khai tham chiếu (bộ phân tích bảng ký hiệu không thể dễ dàng phát hiện khi các tên được sử dụng lại giữa biểu thức có thể hiểu lặp lại ở ngoài cùng bên trái và phần còn lại của khả năng hiểu), các biểu thức đã đặt tên hoàn toàn không được phép như một phần của biểu thức có thể hiểu lặp lại (phần group = re.match(data).group(1) if re.match(data) else None6 Một ngoại lệ khác được áp dụng khi một biểu thức gán xuất hiện trong một cách hiểu có phạm vi chứa là phạm vi lớp. Nếu các quy tắc trên dẫn đến mục tiêu được gán trong phạm vi của lớp đó, thì biểu thức gán rõ ràng là không hợp lệ. Trường hợp này cũng tăng group = re.match(data).group(1) if re.match(data) else None05 group = re.match(data).group(1) if re.match(data) else None7 (Lý do cho ngoại lệ sau là phạm vi hàm ẩn được tạo để hiểu – hiện tại không có cơ chế thời gian chạy nào để hàm tham chiếu đến một biến trong phạm vi lớp chứa và chúng tôi không muốn thêm cơ chế như vậy. Nếu vấn đề này đã được giải quyết, trường hợp đặc biệt này có thể bị xóa khỏi đặc điểm kỹ thuật của biểu thức gán. Lưu ý rằng sự cố đã tồn tại đối với việc sử dụng một biến được xác định trong phạm vi lớp từ khả năng hiểu. ) Xem Phụ lục B để biết một số ví dụ về cách các quy tắc cho mục tiêu hiểu chuyển thành mã tương đương Ưu tiên tương đối của def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ... 59Nhóm toán tử def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 chặt chẽ hơn dấu phẩy ở tất cả các vị trí cú pháp hợp lệ, nhưng kém chặt chẽ hơn tất cả các toán tử khác, bao gồm group = re.match(data).group(1) if re.match(data) else None09, group = re.match(data).group(1) if re.match(data) else None10, group = re.match(data).group(1) if re.match(data) else None11 và biểu thức điều kiện ( group = re.match(data).group(1) if re.match(data) else None12). Như sau từ phần “Các trường hợp ngoại lệ” ở trên, nó không bao giờ được phép ở cùng cấp độ với def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9. Trong trường hợp muốn phân nhóm khác, nên sử dụng dấu ngoặc đơn Toán tử def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 có thể được sử dụng trực tiếp trong đối số lệnh gọi hàm vị trí; Một số ví dụ để làm rõ những gì hợp lệ hoặc không hợp lệ về mặt kỹ thuật group = re.match(data).group(1) if re.match(data) else None8 Hầu hết các ví dụ “hợp lệ” ở trên đều không được khuyến nghị, vì những người đọc mã nguồn Python là con người, những người chỉ lướt qua một số mã có thể bỏ sót sự khác biệt. Nhưng những trường hợp đơn giản không phản cảm group = re.match(data).group(1) if re.match(data) else None9 PEP này khuyến nghị luôn đặt khoảng trắng xung quanh def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59, tương tự như khuyến nghị của PEP 8 cho def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9 khi được sử dụng để gán, trong khi phần sau không cho phép khoảng trắng xung quanh def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9 được sử dụng cho đối số từ khóa. ) Thay đổi thứ tự đánh giáĐể có ngữ nghĩa được xác định chính xác, đề xuất yêu cầu thứ tự đánh giá phải được xác định rõ. Về mặt kỹ thuật, đây không phải là một yêu cầu mới, vì các lệnh gọi hàm có thể đã có tác dụng phụ. Python đã có một quy tắc là các biểu thức con thường được đánh giá từ trái sang phải. Tuy nhiên, các biểu thức gán làm cho các tác dụng phụ này rõ ràng hơn và chúng tôi đề xuất một thay đổi duy nhất đối với thứ tự đánh giá hiện tại
Sự khác nhau giữa biểu thức gán và câu lệnh gánQuan trọng nhất, vì def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 là một biểu thức, nó có thể được sử dụng trong các ngữ cảnh mà các câu lệnh là bất hợp pháp, bao gồm các hàm lambda và khả năng hiểu Ngược lại, biểu thức gán không hỗ trợ các tính năng nâng cao có trong câu lệnh gán
Thông số kỹ thuật thay đổi trong quá trình thực hiệnNhững thay đổi sau đây đã được thực hiện dựa trên kinh nghiệm triển khai và đánh giá bổ sung sau khi PEP được chấp nhận lần đầu tiên và trước Python 3. 8 đã được phát hành
ví dụVí dụ từ thư viện chuẩn PythonĐịa điểm. pyenv_base chỉ được sử dụng trên những dòng này, việc gán nó vào if sẽ di chuyển nó làm “tiêu đề” của khối
_pydecimal. pyTránh lồng nhau group = re.match(data).group(1) if re.match(data) else None31 và loại bỏ một mức thụt đầu dòng
sao chép. pyMã trông đều đặn hơn và tránh nhiều if lồng nhau. (Xem Phụ lục A để biết nguồn gốc của ví dụ này. )
ngày giờ. pytz chỉ được sử dụng cho group = re.match(data).group(1) if re.match(data) else None32, di chuyển nhiệm vụ của nó bên trong if sẽ giúp hiển thị phạm vi của nó
sysconfig. pyGọi group = re.match(data).group(1) if re.match(data) else None33 trong điều kiện group = re.match(data).group(1) if re.match(data) else None34 và gọi group = re.match(data).group(1) if re.match(data) else None35 trên các dòng if làm cho mã gọn hơn mà không khó hiểu hơn
Đơn giản hóa việc hiểu danh sáchKhả năng hiểu danh sách có thể ánh xạ và lọc hiệu quả bằng cách nắm bắt điều kiện match1 = pattern1.match(data) if match1: result = match1.group(1) else: match2 = pattern2.match(data) if match2: result = match2.group(2) else: result = None6 Tương tự như vậy, một biểu thức con có thể được sử dụng lại trong biểu thức chính, bằng cách đặt tên cho nó khi sử dụng lần đầu match1 = pattern1.match(data) if match1: result = match1.group(1) else: match2 = pattern2.match(data) if match2: result = match2.group(2) else: result = None7 Lưu ý rằng trong cả hai trường hợp, biến group = re.match(data).group(1) if re.match(data) else None36 bị ràng buộc trong phạm vi chứa (i. e. cùng mức với group = re.match(data).group(1) if re.match(data) else None37 hoặc group = re.match(data).group(1) if re.match(data) else None38) Chụp các giá trị điều kiệnCác biểu thức gán có thể được sử dụng để tạo hiệu quả tốt trong tiêu đề của câu lệnh group = re.match(data).group(1) if re.match(data) else None31 hoặc group = re.match(data).group(1) if re.match(data) else None34 match1 = pattern1.match(data) if match1: result = match1.group(1) else: match2 = pattern2.match(data) if match2: result = match2.group(2) else: result = None8 Riêng với vòng lặp group = re.match(data).group(1) if re.match(data) else None34, điều này có thể loại bỏ nhu cầu phải có vòng lặp vô hạn, phép gán và điều kiện. Nó cũng tạo ra sự song song mượt mà giữa một vòng lặp chỉ sử dụng một lệnh gọi hàm làm điều kiện của nó và một vòng lặp sử dụng lệnh đó làm điều kiện của nó nhưng cũng sử dụng giá trị thực Cái nĩaMột ví dụ từ thế giới UNIX cấp thấp match1 = pattern1.match(data) if match1: result = match1.group(1) else: match2 = pattern2.match(data) if match2: result = match2.group(2) else: result = None9 Các đề xuất thay thế bị từ chốiCác đề xuất tương tự như đề xuất này thường xuyên xuất hiện trên ý tưởng python. Dưới đây là một số cú pháp thay thế, một số cú pháp cụ thể để hiểu, đã bị từ chối để ủng hộ cú pháp nêu trên Thay đổi quy tắc phạm vi để hiểuPhiên bản trước của PEP này đã đề xuất những thay đổi tinh tế đối với các quy tắc phạm vi để hiểu, để làm cho chúng dễ sử dụng hơn trong phạm vi lớp và để thống nhất phạm vi của “phần có thể lặp lại ngoài cùng” và phần còn lại của phần hiểu. Tuy nhiên, phần này của đề xuất sẽ gây ra sự không tương thích ngược và đã bị rút lại để PEP có thể tập trung vào các biểu thức chuyển nhượng cách viết thay thếNhìn chung ngữ nghĩa giống như đề xuất hiện tại, nhưng được đánh vần khác
Câu lệnh điều kiện dạng đặc biệtMột trong những trường hợp sử dụng phổ biến nhất là câu lệnh group = re.match(data).group(1) if re.match(data) else None31 và group = re.match(data).group(1) if re.match(data) else None34. Thay vì một giải pháp tổng quát hơn, đề xuất này tăng cường cú pháp của hai câu lệnh này để thêm một phương tiện nắm bắt giá trị được so sánh def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...25 Điều này hoạt động tốt nếu và CHỈ nếu điều kiện mong muốn dựa trên tính trung thực của giá trị được chụp. Do đó, nó hiệu quả đối với các trường hợp sử dụng cụ thể (khớp regex, đọc ổ cắm trả về group = re.match(data).group(1) if re.match(data) else None80 khi hoàn thành) và hoàn toàn vô dụng trong các trường hợp phức tạp hơn (e. g. trong đó điều kiện là ________ 381 và bạn muốn nắm bắt giá trị của ________ 382). Việc hiểu danh sách cũng không có ích lợi gì. Thuận lợi. Không có sự mơ hồ về cú pháp. Nhược điểm. Chỉ trả lời một phần nhỏ các trường hợp sử dụng có thể, ngay cả trong câu lệnh group = re.match(data).group(1) if re.match(data) else None31/ group = re.match(data).group(1) if re.match(data) else None34 Hiểu biết về vỏ bọc đặc biệtMột trường hợp sử dụng phổ biến khác là hiểu (list/set/dict và genexps). Như trên, các đề xuất đã được thực hiện cho các giải pháp cụ thể để hiểu
Bất kể cách viết được chọn là gì, điều này tạo ra sự khác biệt rõ rệt giữa khả năng hiểu và dạng vòng lặp tay dài không được kiểm soát tương đương. Không còn có thể mở vòng lặp thành dạng câu lệnh mà không làm lại bất kỳ ràng buộc tên nào. Từ khóa duy nhất có thể được sử dụng lại cho nhiệm vụ này là group = re.match(data).group(1) if re.match(data) else None46, do đó mang lại cho nó những ngữ nghĩa khác nhau một cách lén lút trong cách hiểu so với trong một câu lệnh; Giảm quyền ưu tiên của toán tửCó hai ưu tiên hợp lý cho toán tử def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59. Hoặc nó nên ràng buộc càng lỏng lẻo càng tốt, cũng như việc gán câu lệnh; . Đặt ưu tiên của nó giữa các toán tử so sánh và số học (chính xác là. chỉ thấp hơn bitwise OR) cho phép hầu hết các cách sử dụng bên trong điều kiện group = re.match(data).group(1) if re.match(data) else None34 và group = re.match(data).group(1) if re.match(data) else None31 được đánh vần mà không có dấu ngoặc đơn, vì rất có thể bạn muốn nắm bắt giá trị của một thứ gì đó, sau đó thực hiện so sánh trên đó def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...29 Khi find() trả về -1, vòng lặp kết thúc. Nếu def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 liên kết lỏng lẻo như def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9, thì điều này sẽ thu được kết quả so sánh (thường là match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None03 hoặc match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None04), điều này ít hữu ích hơn Mặc dù hành vi này sẽ thuận tiện trong nhiều tình huống, nhưng nó cũng khó giải thích hơn là “sự. = toán tử hoạt động giống như câu lệnh gán”, và do đó, quyền ưu tiên cho def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 đã được đặt gần nhất có thể với quyền ưu tiên của def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9 (ngoại trừ việc nó liên kết chặt chẽ hơn dấu phẩy) Cho phép dấu phẩy ở bên phảiMột số nhà phê bình đã tuyên bố rằng các biểu thức gán nên cho phép các bộ dữ liệu không được mở ngoặc ở bên phải, do đó hai bộ này sẽ tương đương nhau def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...30 (Với phiên bản hiện tại của đề xuất, cái sau sẽ tương đương với match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None07. ) Tuy nhiên, việc áp dụng lập trường này một cách hợp lý sẽ dẫn đến kết luận rằng khi được sử dụng trong một lệnh gọi hàm, các biểu thức gán cũng liên kết ít chặt chẽ hơn dấu phẩy, vì vậy chúng ta sẽ có sự tương đương khó hiểu sau đây def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...31 Tùy chọn ít gây nhầm lẫn hơn là làm cho def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 liên kết chặt chẽ hơn dấu phẩy Luôn yêu cầu dấu ngoặc đơnNó đã được đề xuất là luôn yêu cầu dấu ngoặc đơn xung quanh một biểu thức gán. Điều này sẽ giải quyết nhiều điểm mơ hồ và thực sự sẽ cần có dấu ngoặc đơn để trích xuất biểu thức con mong muốn. Nhưng trong các trường hợp sau, dấu ngoặc đơn thừa cảm thấy dư thừa def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...32 Phản đối thường xuyênTại sao không biến bài tập hiện tại thành một biểu thức?C và các dẫn xuất của nó định nghĩa toán tử def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...9 là một biểu thức, chứ không phải là một câu lệnh như cách của Python. Điều này cho phép thực hiện các bài tập trong nhiều ngữ cảnh hơn, bao gồm cả những ngữ cảnh mà việc so sánh phổ biến hơn. Sự giống nhau về cú pháp giữa match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None10 và match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None11 cho thấy ngữ nghĩa khác biệt đáng kể của chúng. Do đó, đề xuất này sử dụng def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 để làm rõ sự khác biệt Với các biểu thức gán, tại sao phải bận tâm với các câu lệnh gán?Hai hình thức có tính linh hoạt khác nhau. Toán tử def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...59 có thể được sử dụng bên trong một biểu thức lớn hơn; Tại sao không sử dụng phạm vi cục bộ và ngăn ngừa ô nhiễm không gian tên?Các bản sửa đổi trước đây của đề xuất này liên quan đến phạm vi cục bộ (giới hạn trong một câu lệnh), ngăn chặn rò rỉ tên và ô nhiễm không gian tên. Trong khi một lợi thế nhất định trong một số tình huống, điều này làm tăng sự phức tạp trong nhiều tình huống khác và chi phí không được chứng minh bằng lợi ích. Để đơn giản hóa ngôn ngữ, các ràng buộc tên được tạo ở đây hoàn toàn tương đương với bất kỳ ràng buộc tên nào khác, bao gồm cả việc sử dụng ở phạm vi lớp hoặc mô-đun sẽ tạo ra các tên hiển thị bên ngoài. Điều này không khác với các vòng lặp def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...79 hoặc các cấu trúc khác và có thể được giải theo cùng một cách. match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None17 tên một khi nó không còn cần thiết, hoặc đặt trước nó bằng dấu gạch dưới (Tác giả muốn cảm ơn Guido van Rossum và Christoph Groth vì những gợi ý của họ để chuyển đề xuất theo hướng này. [2]) Đề xuất hướng dẫn phong cáchVì phép gán biểu thức đôi khi có thể được sử dụng tương đương với phép gán câu lệnh, câu hỏi nên ưu tiên cái nào sẽ được đặt ra. Vì lợi ích của các hướng dẫn kiểu như PEP 8, hai đề xuất được đề xuất
Sự nhìn nhậnCác tác giả muốn cảm ơn Nick Coghlan và Steven D'Aprano vì những đóng góp đáng kể của họ cho đề xuất này và các thành viên của danh sách gửi thư cố vấn cốt lõi để được hỗ trợ thực hiện Phụ lục A. Phát hiện của Tim PetersĐây là một bài luận ngắn Tim Peters đã viết về chủ đề này Tôi không thích các dòng mã “bận rộn” và cũng không thích đặt logic không liên quan đến khái niệm trên một dòng. Vì vậy, ví dụ, thay vì def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...33 tôi thích def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...34 thay thế. Vì vậy, tôi nghi ngờ rằng tôi sẽ tìm thấy một vài nơi mà tôi muốn sử dụng các biểu thức gán. Tôi thậm chí không xem xét chúng cho các dòng đã kéo dài nửa màn hình. Trong các trường hợp khác, phán quyết “không liên quan” def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...35 là một cải tiến lớn so với briefer def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...36 Hai câu lệnh ban đầu đang thực hiện những điều hoàn toàn khác nhau về mặt khái niệm và việc kết hợp chúng lại với nhau là điều điên rồ về mặt khái niệm Trong các trường hợp khác, việc kết hợp logic liên quan khiến nó khó hiểu hơn, chẳng hạn như viết lại def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...37 như người tóm tắt def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...38 Bài kiểm tra group = re.match(data).group(1) if re.match(data) else None34 ở đó quá tinh vi, chủ yếu dựa vào đánh giá nghiêm ngặt từ trái sang phải trong bối cảnh không đoản mạch hoặc xâu chuỗi phương thức. Bộ não của tôi không được kết nối theo cách đó Nhưng những trường hợp như thế rất hiếm. Liên kết tên rất thường xuyên và “thưa thớt tốt hơn dày đặc” không có nghĩa là “gần như trống rỗng tốt hơn thưa thớt”. Ví dụ: tôi có nhiều hàm trả về match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None19 hoặc match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None20 để thông báo "Tôi không có gì hữu ích để trả về trong trường hợp này, nhưng vì điều đó thường được mong đợi nên tôi sẽ không làm phiền bạn bằng một ngoại lệ". Điều này về cơ bản giống như các hàm tìm kiếm biểu thức chính quy trả về match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None19 khi không khớp. Vì vậy, có rất nhiều mã của mẫu def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...39 Tôi thấy điều đó rõ ràng hơn, và chắc chắn là ít đánh máy và đọc mẫu hơn một chút, vì def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...40 Thật tuyệt khi đánh đổi một lượng nhỏ khoảng trắng ngang để lấy một _dòng_ mã xung quanh khác trên màn hình. Lúc đầu, tôi không quan tâm lắm đến điều này, nhưng nó tăng lên rất thường xuyên và tôi nhanh chóng cảm thấy khó chịu vì thực sự không thể chạy mã briefer. Điều đó làm tôi ngạc nhiên Có những trường hợp khác mà biểu thức gán thực sự tỏa sáng. Thay vì chọn một mã khác từ mã của tôi, Kirill Balunov đã đưa ra một ví dụ đáng yêu từ hàm match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None22 của thư viện tiêu chuẩn trong match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None23 def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...41 Sự thụt lề ngày càng tăng gây hiểu lầm về mặt ngữ nghĩa. logic về mặt khái niệm là phẳng, "thử nghiệm đầu tiên thành công sẽ thắng" def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...42 Việc sử dụng các biểu thức gán dễ dàng cho phép cấu trúc trực quan của mã nhấn mạnh tính phẳng về mặt khái niệm của logic; Một ví dụ nhỏ hơn từ mã của tôi làm tôi thích thú, cả hai đều cho phép đặt logic liên quan vốn có trong một dòng và cho phép loại bỏ mức thụt đầu dòng “nhân tạo” gây phiền nhiễu def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...43 đã trở thành def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...44 group = re.match(data).group(1) if re.match(data) else None31 đó dài bao nhiêu tùy ý tôi, nhưng vẫn dễ theo dõi Vì vậy, nói chung, trong hầu hết các dòng ràng buộc một tên, tôi sẽ không sử dụng các biểu thức gán, nhưng vì cấu trúc đó quá thường xuyên, nên tôi sẽ để lại nhiều chỗ. Trong hầu hết các trường hợp sau, tôi đã tìm thấy một chiến thắng nhỏ cộng lại do tần suất xảy ra và trong phần còn lại, tôi tìm thấy một chiến thắng từ trung bình đến lớn. Tôi chắc chắn sẽ sử dụng nó thường xuyên hơn so với group = re.match(data).group(1) if re.match(data) else None31 bậc ba, nhưng ít thường xuyên hơn đáng kể so với bài tập tăng cường Một ví dụ sốTôi có một ví dụ khác mà tôi khá ấn tượng vào thời điểm đó Trong đó tất cả các biến là số nguyên dương và a ít nhất lớn bằng căn bậc n của x, thuật toán này trả về giá trị sàn của căn bậc n của x (và gần gấp đôi số bit chính xác trên mỗi lần lặp) def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...45 Không rõ tại sao nó hoạt động, nhưng không rõ ràng hơn ở dạng "vòng lặp rưỡi". Thật khó để chứng minh tính đúng đắn nếu không dựa trên cái nhìn sâu sắc đúng đắn (“trung bình số học - bất đẳng thức trung bình hình học”) và biết một số điều không tầm thường về cách hoạt động của các hàm tầng lồng nhau. Đó là, những thách thức nằm ở toán học, không thực sự ở mã hóa Nếu bạn biết tất cả những điều đó, thì dạng biểu thức gán dễ dàng được đọc là "trong khi dự đoán hiện tại quá lớn, hãy đoán nhỏ hơn", trong đó "quá lớn?" Trước mắt tôi, hình thức ban đầu khó hiểu hơn def foo(answer = p := 42): # INVALID ... def foo(answer=(p := 42)): # Valid, though not great style ...46 Phụ lục B. Bản dịch mã thô để hiểuPhụ lục này cố gắng làm rõ (mặc dù không chỉ định) các quy tắc khi mục tiêu xuất hiện trong một cách hiểu hoặc trong một biểu thức trình tạo. Đối với một số ví dụ minh họa, chúng tôi hiển thị mã gốc, chứa phần hiểu và bản dịch, trong đó phần hiểu đã được thay thế bằng một hàm tạo tương đương cộng với một số giàn giáo Vì match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None26 tương đương với match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None27, tất cả các ví dụ này đều sử dụng cách hiểu danh sách mà không làm mất tính tổng quát. Và vì những ví dụ này nhằm làm rõ các trường hợp cạnh của quy tắc, nên chúng không cố trông giống như mã thực Ghi chú. khả năng hiểu đã được thực hiện thông qua tổng hợp các hàm tạo lồng nhau như trong phụ lục này. Phần mới đang thêm các khai báo thích hợp để thiết lập phạm vi dự định của các mục tiêu biểu thức gán (cùng phạm vi mà chúng giải quyết như thể phép gán được thực hiện trong khối chứa khả năng hiểu ngoài cùng). Đối với các mục đích suy luận kiểu, các phần mở rộng minh họa này không có nghĩa là các mục tiêu của biểu thức gán luôn là Tùy chọn (nhưng chúng chỉ ra phạm vi ràng buộc của mục tiêu) Hãy bắt đầu với lời nhắc về mã nào được tạo cho biểu thức trình tạo mà không có biểu thức gán
Hãy thêm một biểu thức gán đơn giản
Hãy thêm một khai báo match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None28 trong match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None29
Hoặc thay vào đó, hãy thêm một khai báo match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None30 vào match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None29
Cuối cùng, hãy lồng hai cách hiểu
Phụ lục C. Không có thay đổi đối với ngữ nghĩa phạm viBởi vì đó là một điểm gây nhầm lẫn, hãy lưu ý rằng không có gì thay đổi về ngữ nghĩa phạm vi của Python. Các phạm vi chức năng-cục bộ tiếp tục được giải quyết tại thời điểm biên dịch và có phạm vi tạm thời không xác định tại thời điểm chạy (“đóng hoàn toàn”). Thí dụ |