Đâ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
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ộ]
Đặ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
Trong 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
Trong 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
Có 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
- Biểu thức gán không được đặt trong dấu ngoặc đơn bị cấm ở cấp cao nhất của câu lệnh biểu thức. Ví dụ
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
3Quy tắc này được đưa vào để đơn giản hóa sự lựa chọn cho người dùng giữa câu lệnh gán và biểu thức gán – không có vị trí cú pháp nào mà cả hai đều hợp lệ
- Các biểu thức gán không được đặt trong dấu ngoặc đơn bị cấm ở cấp cao nhất của phía bên tay phải của câu lệnh gán. Ví dụ
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
4Một lần nữa, quy tắc này được đưa vào để tránh hai cách nói giống nhau về mặt hình ảnh.
- Các biểu thức gán không được đặt trong dấu ngoặc đơn bị cấm đối với giá trị của đối số từ khóa trong cuộc gọi. Ví dụ
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
5Quy tắc này được đưa vào để không cho phép mã quá khó hiểu và bởi vì việc phân tích đối số từ khóa đã đủ phức tạp rồi
- Các biểu thức gán không được đặt trong ngoặc đơn bị cấm ở cấp cao nhất của giá trị mặc định của hàm. Ví dụ
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
Quy tắc này được đưa vào để ngăn chặn các tác dụng phụ ở vị trí mà ngữ nghĩa chính xác của nó đã gây nhầm lẫn cho nhiều người dùng [cf. đề xuất kiểu phổ biến đối với các giá trị mặc định có thể thay đổi] và cũng để lặp lại lệnh cấm tương tự trong các cuộc gọi [dấu đầu dòng trước đó]
- Các biểu thức gán không được mở ngoặc đơn bị cấm làm chú thích cho các đối số, giá trị trả về và phép gán. Ví dụ
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
7Lý do ở đây tương tự như hai trường hợp trước;
- Các biểu thức gán không được đặt trong dấu ngoặc đơn bị cấm trong các hàm lambda. Ví dụ
group = re.match[data].group[1] if re.match[data] else None
0Điều này cho phép
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
70 luôn liên kết ít chặt chẽ hơndef foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
59; . Trong trường hợp tên sẽ được sử dụng nhiều lần, biểu thức có thể cần được đặt trong ngoặc đơn, vì vậy lệnh cấm này sẽ hiếm khi ảnh hưởng đến mã - Biểu thức gán bên trong chuỗi f yêu cầu dấu ngoặc đơn. Ví dụ
group = re.match[data].group[1] if re.match[data] else None
1Điều này cho thấy rằng cái giống như toán tử gán trong chuỗi f không phải lúc nào cũng là toán tử gán. Trình phân tích chuỗi f sử dụng
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
8 để biểu thị các tùy chọn định dạng. Để duy trì khả năng tương thích ngược, việc sử dụng toán tử gán bên trong chuỗi f phải được đặt trong ngoặc đơn. Như đã lưu ý ở trên, việc sử dụng toán tử gán này không được khuyến nghị
Mộ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
Nhó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. ]
Để 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
- Trong cách hiểu chính tả
group = re.match[data].group[1] if re.match[data] else None
18,group = re.match[data].group[1] if re.match[data] else None
19 hiện được đánh giá trướcgroup = re.match[data].group[1] if re.match[data] else None
20. Chúng tôi đề xuất thay đổi điều này đểgroup = re.match[data].group[1] if re.match[data] else None
20 được đánh giá trướcgroup = re.match[data].group[1] if re.match[data] else None
19. [Trong một màn hình chính tả nhưgroup = re.match[data].group[1] if re.match[data] else None
23, đây đã là trường hợp và cả tronggroup = re.match[data].group[1] if re.match[data] else None
24, điều này rõ ràng phải tương đương với khả năng hiểu chính tả. ]
Quan 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
- Nhiều mục tiêu không được hỗ trợ trực tiếp
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
0 - Các mục tiêu chuyển nhượng đơn lẻ không phải là một
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
7 không được hỗ trợmatch1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
1 - Ưu tiên xung quanh dấu phẩy là khác nhau
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
2 - Không hỗ trợ đóng gói và giải nén lặp lại [cả dạng thông thường và dạng mở rộng]
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
3 - Chú thích loại nội tuyến không được hỗ trợ
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
4 - Bài tập tăng cường không được hỗ trợ
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
5
Nhữ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
- để thống nhất với các ngoại lệ tương tự khác và để tránh khóa tên ngoại lệ không nhất thiết sẽ cải thiện sự rõ ràng cho người dùng cuối, lớp con
group = re.match[data].group[1] if re.match[data] else None
27 được đề xuất ban đầu củagroup = re.match[data].group[1] if re.match[data] else None
05 đã bị loại bỏ để chỉ tăng trực tiếpgroup = re.match[data].group[1] if re.match[data] else None
05. - do hạn chế trong quy trình phân tích bảng ký hiệu của CPython, việc triển khai tham chiếu tăng
group = re.match[data].group[1] if re.match[data] else None
05 cho tất cả các cách sử dụng biểu thức được đặt tên bên trong các biểu thức có thể hiểu, thay vì chỉ tăng chúng khi mục tiêu biểu thức được đặt tên xung đột với một trong các biến lặp trong phần hiểu. Điều này có thể được xem xét lại với các ví dụ đủ thuyết phục, nhưng độ phức tạp bổ sung cần thiết để thực hiện hạn chế chọn lọc hơn dường như không đáng giá đối với các trường hợp sử dụng thuần túy giả thuyết
env_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
- Hiện hành
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
6 - cải tiến
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
7
Trá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
- Hiện hành
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
8 - cải tiến
match1 = pattern1.match[data] match2 = pattern2.match[data] if match1: result = match1.group[1] elif match2: result = match2.group[2] else: result = None
9
Mã 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. ]
- Hiện hành
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
0 - cải tiến
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
1
tz 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ó
- Hiện hành
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
2 - cải tiến
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
3
Gọ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
- Hiện hành
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
4 - cải tiến
match1 = pattern1.match[data] if match1: result = match1.group[1] else: match2 = pattern2.match[data] if match2: result = match2.group[2] else: result = None
5
Khả 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]
Cá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
Mộ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 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
Phiê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
Nhìn chung ngữ nghĩa giống như đề xuất hiện tại, nhưng được đánh vần khác
group = re.match[data].group[1] if re.match[data] else None
42def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
20Vì
group = re.match[data].group[1] if re.match[data] else None
42 đã có nghĩa trong các câu lệnhgroup = re.match[data].group[1] if re.match[data] else None
44,group = re.match[data].group[1] if re.match[data] else None
45 vàgroup = re.match[data].group[1] if re.match[data] else None
46 [với các ngữ nghĩa khác nhau], điều này sẽ tạo ra sự nhầm lẫn không cần thiết hoặc yêu cầu cách viết hoa đặc biệt [e. g. cấm chỉ định trong tiêu đề của các câu lệnh này][Lưu ý rằng
group = re.match[data].group[1] if re.match[data] else None
47 không chỉ gán giá trị củagroup = re.match[data].group[1] if re.match[data] else None
48 chogroup = re.match[data].group[1] if re.match[data] else None
49 – nó gọigroup = re.match[data].group[1] if re.match[data] else None
50 và gán kết quả của việc đó chogroup = re.match[data].group[1] if re.match[data] else None
49. ]Các lý do bổ sung để thích
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
59 hơn cách đánh vần này bao gồm- Trong
group = re.match[data].group[1] if re.match[data] else None
53, mục tiêu của bài tập không xuất hiện trước mặt bạn – nó chỉ đọc nhưgroup = re.match[data].group[1] if re.match[data] else None
54 và nhìn bề ngoài thì quá giống vớigroup = re.match[data].group[1] if re.match[data] else None
55 - Trong tất cả các tình huống khác khi mệnh đề
group = re.match[data].group[1] if re.match[data] else None
56 được cho phép, ngay cả những người đọc có kỹ năng trung gian cũng có thể dự đoán mệnh đề đó [tuy nhiên là tùy chọn] bởi từ khóa bắt đầu dòng và ngữ pháp liên kết chặt chẽ từ khóa đó với mệnh đề asgroup = re.match[data].group[1] if re.match[data] else None
57group = re.match[data].group[1] if re.match[data] else None
58group = re.match[data].group[1] if re.match[data] else None
59
Ngược lại, biểu thức gán không thuộc về
group = re.match[data].group[1] if re.match[data] else None
31 hoặcgroup = re.match[data].group[1] if re.match[data] else None
34 bắt đầu dòng và chúng tôi cũng cố ý cho phép biểu thức gán trong các ngữ cảnh khác - Nhịp điệu song song giữa
group = re.match[data].group[1] if re.match[data] else None
62group = re.match[data].group[1] if re.match[data] else None
63
củng cố nhận dạng trực quan các biểu thức chuyển nhượng
- Trong
group = re.match[data].group[1] if re.match[data] else None
64def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
21Cú pháp này được lấy cảm hứng từ các ngôn ngữ như R và Haskell và một số máy tính có thể lập trình. [Lưu ý rằng không thể có mũi tên hướng sang trái
group = re.match[data].group[1] if re.match[data] else None
65 trong Python, vì nó sẽ được hiểu là dấu trừ đơn nguyên và nhỏ hơn. ] Cú pháp này có một lợi thế nhỏ so với 'as' ở chỗ nó không xung đột vớigroup = re.match[data].group[1] if re.match[data] else None
46,group = re.match[data].group[1] if re.match[data] else None
45 vàgroup = re.match[data].group[1] if re.match[data] else None
44, nhưng nếu không thì tương đương. Nhưng nó hoàn toàn không liên quan đến cách sử dụng khác của Python đối vớigroup = re.match[data].group[1] if re.match[data] else None
69 [chú thích kiểu trả về hàm] và so vớidef foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
59 [xuất hiện từ Algol-58], nó có truyền thống yếu hơn nhiều- Tô điểm cho các tên địa phương có dấu chấm ở đầu
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
22Điều này có lợi thế là việc sử dụng bị rò rỉ có thể dễ dàng được phát hiện, loại bỏ một số dạng mơ hồ về cú pháp. Tuy nhiên, đây sẽ là nơi duy nhất trong Python mà phạm vi của một biến được mã hóa thành tên của nó, khiến cho việc tái cấu trúc trở nên khó khăn hơn
- Thêm một
group = re.match[data].group[1] if re.match[data] else None
71 vào bất kỳ câu lệnh nào để tạo các ràng buộc tên cục bộdef foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
23Thứ tự thực thi bị đảo ngược [phần thân thụt lề được thực hiện trước, tiếp theo là “tiêu đề”]. Điều này yêu cầu một từ khóa mới, trừ khi một từ khóa hiện có được sử dụng lại [rất có thể là
group = re.match[data].group[1] if re.match[data] else None
72]. Xem PEP 3150 để thảo luận trước về chủ đề này [với từ khóa được đề xuất làgroup = re.match[data].group[1] if re.match[data] else None
73] group = re.match[data].group[1] if re.match[data] else None
74def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
24Cú pháp này có ít xung đột hơn so với
group = re.match[data].group[1] if re.match[data] else None
56 [chỉ xung đột với ký hiệugroup = re.match[data].group[1] if re.match[data] else None
76], nhưng nếu không thì có thể so sánh với nó. Thay vì song song vớigroup = re.match[data].group[1] if re.match[data] else None
77 [có thể hữu ích nhưng cũng có thể gây nhầm lẫn], điều này không có sự tương đồng, nhưng gợi nhiều liên tưởng
Mộ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
Mộ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
group = re.match[data].group[1] if re.match[data] else None
85,group = re.match[data].group[1] if re.match[data] else None
86 hoặcgroup = re.match[data].group[1] if re.match[data] else None
87def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
26Điều này đưa biểu thức con đến một vị trí ở giữa vòng lặp 'for' và biểu thức. Nó giới thiệu một từ khóa ngôn ngữ bổ sung, tạo ra xung đột. Trong số ba,
group = re.match[data].group[1] if re.match[data] else None
85 đọc rõ ràng nhất, nhưng cũng có khả năng xung đột lớn nhất [e. g. SQLAlchemy và numpy có các phương thứcgroup = re.match[data].group[1] if re.match[data] else None
85, cũng nhưgroup = re.match[data].group[1] if re.match[data] else None
90 trong thư viện chuẩn]group = re.match[data].group[1] if re.match[data] else None
91def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
27Như trên, nhưng sử dụng lại từ khóa
group = re.match[data].group[1] if re.match[data] else None
46. Không đọc quá tệ và không cần từ khóa ngôn ngữ bổ sung. Tuy nhiên, bị giới hạn trong khả năng hiểu và không thể dễ dàng chuyển đổi thành cú pháp vòng lặp for "viết tay". Có vấn đề C mà dấu bằng trong một biểu thức hiện có thể tạo liên kết tên, thay vì thực hiện so sánh. Sẽ đặt ra câu hỏi tại sao “với TÊN = EXPR. ” không thể được sử dụng như một tuyên bố của riêng mìnhgroup = re.match[data].group[1] if re.match[data] else None
93def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
28Theo tùy chọn 2, nhưng sử dụng
group = re.match[data].group[1] if re.match[data] else None
56 thay vì dấu bằng. Căn chỉnh về mặt cú pháp với các cách sử dụng khác củagroup = re.match[data].group[1] if re.match[data] else None
56 để liên kết tên, nhưng một phép chuyển đổi đơn giản sang dạng viết tắt của vòng lặp for sẽ tạo ra các ngữ nghĩa khác biệt đáng kể;
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;
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]
Mộ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
Nó đã đượ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
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
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;
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. ]
Vì 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
- Nếu có thể sử dụng các câu lệnh gán hoặc biểu thức gán, hãy ưu tiên các câu lệnh;
- Nếu việc sử dụng các biểu thức gán sẽ dẫn đến sự mơ hồ về thứ tự thực hiện, hãy cấu trúc lại nó để sử dụng các câu lệnh thay thế
Cá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
Đâ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
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 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 mở rộng minh họa này không ngụ ý rằng 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
- Mã gốc [EXPR thường tham khảo VAR]
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
47 - Bản dịch [đừng lo xung đột tên]
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
48
Hãy thêm một biểu thức gán đơn giản
- Mã gốc
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
49 - Dịch
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
50
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
- Mã gốc
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
51 - Dịch
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
52
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
- Mã gốc
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
53 - Dịch
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
54
Cuối cùng, hãy lồng hai cách hiểu
- Mã gốc
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
55 - Dịch
def foo[answer = p := 42]: # INVALID ... def foo[answer=[p := 42]]: # Valid, though not great style ...
56
Bở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”]. Ví dụ