Python có cấm hai mã nhận dạng Unicode trông giống nhau không?


80

Tôi đã thử với các mã nhận dạng Unicode và tình cờ gặp điều này:

>>> 𝑓, x = 1, 2
>>> 𝑓, x
(1, 2)
>>> 𝑓, f = 1, 2
>>> 𝑓, f
(2, 2)

Những gì đang xảy ra ở đây? Tại sao Python lại thay thế đối tượng được tham chiếu bởi 𝑓, nhưng chỉ đôi khi? Hành vi đó được mô tả ở đâu?


9
Đây là một câu hỏi thú vị, nhưng ví dụ có thể tái tạo tối thiểu của bạn có thể chỉ là𝑓=1 f=2 print(𝑓)
khelwood

1
Cảm ơn. Làm cho ví dụ thậm chí còn nhỏ hơn bây giờ.
Erik Cederstrand


1
a, a = 1, 2; a, a. Điều này không liên quan đến fhoặc 𝑓.
user76284

4
Ví dụ 𝑓 = 3; fsẽ đủ.
user76284

Câu trả lời:


80

PEP 3131 - Hỗ trợ Số nhận dạng không phải ASCII cho biết

Tất cả các số nhận dạng được chuyển thành dạng NFKC bình thường trong khi phân tích cú pháp; so sánh các định danh dựa trên NFKC.

Bạn có thể sử dụng unicodedatađể kiểm tra các chuyển đổi:

import unicodedata

unicodedata.normalize('NFKC', '𝑓')
# f

điều này sẽ chỉ ra rằng '𝑓'được chuyển đổi thành 'f'trong phân tích cú pháp. Dẫn đến điều mong đợi:

𝑓  = "Some String"
print(f)
# "Some String"

23
Đây là một câu trả lời tuyệt vời, nhưng là một quyết định tồi tệ của các nhà phát triển cốt lõi Python. Tôi lưu ý rằng trong cuộc thảo luận về PEP này, một trong những ý kiến ​​phản đối là Unicode không được hiểu rõ và có công cụ yếu. Bây giờ, hơn một thập kỷ sau, tôi tự hỏi liệu đã đến lúc nghĩ lại cách viết chữ La tinh của các mã định danh Unicode hay chưa.
Adam Smith

33
@AdamSmith nhưng chuẩn hóa Unicode không phải là romanisation. Bạn có thể có πnhư một mã định danh Python khác biệt với ptốt. Nếu tôi hiểu chính xác, cách gấp NFK * nói về các ký tự mà người dùng Unicode nghĩ rằng bắt đầu phải là cùng một ký tự, nhưng chúng không thể được hợp nhất vì tương thích ngược với một số mã hóa cũ.
lenz

19
Có hai loại ký tự tương đương: chuẩn và tương thích. Tính tương đương hợp quy phải hiển thị cùng một glyph, điều này không xảy ra giữa 𝑓 và f. NFKC chuẩn hóa cả tương đương chuẩn và tương đương, mà tôi đồng ý là một lựa chọn tồi cho một ngôn ngữ lập trình như Python, người phân biệt giữa các trường hợp chữ cái: dự kiến ​​rằng các số nhận dạng hiển thị khác nhau sẽ khác. Python nên sử dụng NFC, điều này đảm bảo 𝑓 và f là những thứ khác nhau.
lvella

27
Một số hình thức chuẩn hóa là cần thiết vì, ví dụ, các ký tự latin có dấu phụ - nếu tôi thấy một ký tự như 'ü' thì đó có thể là một ký tự tổng hợp (u + kết hợp dấu ngoặc kép) hoặc một ký tự đơn được tạo sẵn; người dùng sẽ không có cách nào hợp lý hoặc không muốn phân biệt chúng và phương thức nhập ưa thích của họ có thể chỉ cho phép nhập một trong các tùy chọn này. Vì vậy, tôi mong muốn rằng nếu tôi thấy 'ü' và nhập 'ü' thì ngôn ngữ sẽ coi các ký tự là tương đương ngay cả khi chúng được mã hóa khác nhau, mặc dù chuẩn hóa NFC có thể là đủ cho điều đó.
Peteris

8
Python hỗ trợ Unicode cho các số nhận dạng để tạo điều kiện thuận lợi cho việc sử dụng nó trong việc xác định các số nhận dạng bằng các ngôn ngữ không phải tiếng Anh, chứ không phải để cung cấp quyền truy cập bình đẳng vào tất cả các điểm mã Unicode. Ví dụ: hiện tại khá khó để hack trình phân tích cú pháp để hỗ trợ các toán tử Unicode, vì bất kỳ ký tự không phải ASCII nào trước tiên đều được giả định là một phần của mã định danh, ngay cả khi ký tự Unicode được đề cập không phải là một phần hợp lệ của mã định danh. Ý tưởng không phải là hỗ trợ khai thác Unicode cho các ký tự "thú vị", mà là hỗ trợ các ký tự được tạo bởi bố cục bàn phím không phải tiếng Anh tiêu chuẩn.
chepner

27

Đây là một ví dụ nhỏ, chỉ để cho thấy "tính năng" này khủng khiếp như thế nào:

𝕋𝐡ᵢ𝔰_f𝔢𝘢𝚝𝓊ᵣₑ_𝕤ₕ𝔬𝔲𝖑𝔡_dₑ𝕗ᵢ𝘯i𝘵𝚎ℓy_𝒷𝘦_𝐚_𝚋ᵘg = 42
print(T𝗵ℹ𝚜_𝒇e𝖆𝚝𝙪ᵣe_ₛ𝔥º𝓾𝗹𝙙_𝚍e𝒇ᵢ𝒏ⁱtᵉ𝕝𝘆_𝖻ℯ_𝔞_𝖇𝖚𝓰)
# => 42

Hãy thử nó trực tuyến! (Nhưng vui lòng không sử dụng nó)

Và như đã được @MarkMeyer đề cập, hai số nhận dạng có thể khác biệt mặc dù chúng trông giống nhau ("CHỮ VIẾT HOA CYRILLIC A" và "CHỮ VIẾT HOA LATIN A")

А = 42
print(A)
# => NameError: name 'A' is not defined

3
Khiến tôi muốn viết một trang tương đương với jsfuck.com ... python-unicode-hell.com?
Mathieu VIALES

2
@MathieuVIALES 𝓕𝕖𝒆𝑙 𝐟ʳ𝙚ₑ ᵗ𝗈 ᵈ𝚘 𝓈º. Tôi 𝐡a𝔳ᵉ 𝒔𝚘𝙢𝖾 𝒄𝑜𝖽ᵉ 𝖑𝒶𝒚𝑖𝒏𝕘 arₒ𝘶𝘯𝖽. 𝐈 ʷ𝙖n𝓉ℯ𝙙 𝒕𝘰 𝗍𝕣o𝑙𝗅 ⅽ𝔬𝚕𝘭ᵉ𝗮𝓰𝘶𝖊𝔰 ʷ𝚒ₜ𝙝 𝓲ᵗ, 𝕓𝒖t 𝚝ℎₑ 𝗋𝑒𝙨𝓊𝕝𝓉 ⅈ𝔰 𝓳ᵘ𝑠𝙩 t𝚘𝗈 𝗵o𝒓𝑟ible 𝘀𝐨 𝐼 ⁿ𝚎v𝖾𝔯 ᵘ𝓼ₑⅾ ⅈt. 𝕌𝓃𝗍𝚒l 𝕟𝚘𝙬.
Eric Duminil

8
Và tất nhiên: А = 42; print(A)-> "NameError: name 'A' không được xác định"
Mark M

8
Mục đích là không bao giờ mở cửa cho các tên định danh phức tạp tùy tiện, mà là để tạo điều kiện thuận lợi cho việc nhập các số nhận dạng bằng ngôn ngữ mẹ đẻ của lập trình viên (sử dụng bố cục bàn phím dành cho ngôn ngữ đó). Tốt hơn nên sử dụng phân loại điểm mã dưới dạng một chữ cái của Unicode hơn là đóng vai trò trọng tài mà các hệ thống viết có thể và không thể được sử dụng cho số nhận dạng. (Và việc giới hạn một số nhận dạng cho các ký tự từ một hệ thống chữ viết đơn lẻ vượt xa mức lương của trình phân tích cú pháp.)
chepner

12
Không có điểm mã nào trong số đó là một phần của hệ thống chữ viết của bất kỳ ngôn ngữ tự nhiên nào, vì vậy việc bất kỳ điểm mã nào trong số đó được chấp nhận như một phần của mã định danh gần như là "ngẫu nhiên", dựa trên phân loại Unicode chứ không phải bất kỳ chứng thực rõ ràng nào của chính Python.
chepner
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.