2015-12-04 14 views
20

Tôi chỉ cần stumbled khi tình hình lẻ sau:Nhận diện chuẩn hóa: Tại sao ký hiệu vi mô được chuyển thành chữ cái tiếng Hy lạp?

>>> class Test: 
     µ = 'foo' 

>>> Test.µ 
'foo' 
>>> getattr(Test, 'µ') 
Traceback (most recent call last): 
    File "<pyshell#4>", line 1, in <module> 
    getattr(Test, 'µ') 
AttributeError: type object 'Test' has no attribute 'µ' 
>>> 'µ'.encode(), dir(Test)[-1].encode() 
(b'\xc2\xb5', b'\xce\xbc') 

Nhân vật tôi bước vào luôn là dấu hiệu μ trên bàn phím, nhưng đối với một số lý do nó được chuyển đổi. Lý do tại sao điều này xảy ra?

Trả lời

21

Có hai ký tự khác nhau có liên quan ở đây. Một là MICRO SIGN, là một trên bàn phím và một là GREEK SMALL LETTER MU.

Để hiểu những gì đang xảy ra, chúng ta nên có một cái nhìn như thế nào Python xác định danh trong language reference:

identifier ::= xid_start xid_continue* 
id_start  ::= <all characters in general categories Lu, Ll, Lt, Lm, Lo, Nl, the underscore, and characters with the Other_ID_Start property> 
id_continue ::= <all characters in id_start, plus characters in the categories Mn, Mc, Nd, Pc and others with the Other_ID_Continue property> 
xid_start ::= <all characters in id_start whose NFKC normalization is in "id_start xid_continue*"> 
xid_continue ::= <all characters in id_continue whose NFKC normalization is in "id_continue*"> 

Cả hai nhân vật của chúng tôi, SIGN MICRO và THƯ NHỎ Hy Lạp MU, là một phần của nhóm Ll unicode (chữ thường), vì vậy cả hai chữ cái đều có thể được sử dụng ở bất kỳ vị trí nào trong số nhận dạng. Bây giờ lưu ý rằng định nghĩa của identifier thực sự đề cập đến xid_startxid_continue và chúng được định nghĩa là tất cả các ký tự trong định nghĩa không x tương ứng mà chuẩn hóa của NFKC là kết quả của chuỗi ký tự hợp lệ.

Python dường như chỉ quan tâm đến các dạng số nhận dạng được chuẩn hóa. Điều này được xác nhận một chút bên dưới:

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

NFKC là Unicode normalization phân tách các ký tự thành từng phần riêng lẻ. MICRO SIGN phân tách thành GREEK SMALL LETTER MU, và đó chính xác là những gì đang diễn ra ở đó.

Có rất nhiều nhân vật khác cũng bị ảnh hưởng bởi việc chuẩn hóa này. Một ví dụ khác là OHM SIGN phân hủy thành GREEK CAPITAL LETTER OMEGA. Việc sử dụng đó làm số nhận dạng cho kết quả tương tự, tại đây được hiển thị bằng cách sử dụng người dân địa phương:

>>> Ω = 'bar' 
>>> locals()['Ω'] 
Traceback (most recent call last): 
    File "<pyshell#1>", line 1, in <module> 
    locals()['Ω'] 
KeyError: 'Ω' 
>>> [k for k, v in locals().items() if v == 'bar'][0].encode() 
b'\xce\xa9' 
>>> 'Ω'.encode() 
b'\xe2\x84\xa6' 

Vì vậy, cuối cùng, đây chỉ là điều mà Python làm. Thật không may, không có cách nào tốt để phát hiện hành vi này, gây ra lỗi như lỗi được hiển thị. Thông thường, khi số nhận dạng chỉ được gọi là số nhận dạng, tức là số nhận dạng được sử dụng như biến hoặc thuộc tính thực, thì mọi thứ sẽ ổn: Quá trình chuẩn hóa sẽ chạy mỗi lần và tìm thấy số nhận dạng.

Vấn đề duy nhất là truy cập dựa trên chuỗi. Các chuỗi chỉ là các chuỗi, tất nhiên không có sự bình thường xảy ra (đó sẽ là một ý tưởng tồi). Và hai cách được hiển thị ở đây, getattrlocals, cả hai đều hoạt động trên các từ điển. getattr() truy cập thuộc tính của đối tượng qua số __dict__ của đối tượng và locals() trả lại từ điển. Và trong từ điển, chìa khóa có thể là bất kỳ chuỗi nào, vì vậy hoàn toàn tốt đẹp khi có ĐĂNG KÝ MICRO hoặc ĐĂNG NHẬP OHM ở đó.

Trong những trường hợp đó, bạn cần nhớ tự mình thực hiện bình thường hóa.Chúng ta có thể sử dụng unicodedata.normalize cho điều này, mà sau đó cũng cho phép chúng tôi để có được một cách chính xác giá trị của chúng tôi từ bên locals() (hoặc sử dụng getattr):

>>> normalized_ohm = unicodedata.normalize('NFKC', 'Ω') 
>>> locals()[normalized_ohm] 
'bar' 
+1

Đó là rất rõ ràng và kỹ lưỡng. Tôi vẫn cố gắng tránh các ký tự không phải ASCII ngay cả trong chuỗi ký tự, hãy để tên biến duy nhất. Bình thường hóa chỉ là một vấn đề, mọi thứ cũng có thể bị xáo trộn bởi một số biên tập viên, sao chép và dán thay đổi mã hóa, vv 'class Test: mu = 'foo'' – Galax

+1

Miễn là bạn sử dụng UTF-8 cho các tệp nguồn của bạn (mà bạn thực sự nên), bạn tốt trong hầu hết các trường hợp với Python 3, đặc biệt là trong chuỗi ký tự. Nếu bạn có một trình soạn thảo có thể gây rối loạn này, bạn sẽ nhận được một trình soạn thảo tốt hơn;) Và đối với các số nhận dạng, bạn cũng có thể sáng tạo ở đó, ngoại trừ vấn đề được hiển thị có thể gây ra sự cố cho một số hoặc hoàn toàn không được chú ý cho người khác :) – poke

Các vấn đề liên quan