2016-10-24 59 views
55

Tại sao cuộc gọi chức năng set xóa các phân tách, nhưng phân tích cú pháp một chữ được đặt không?Đặt theo nghĩa đen cho kết quả khác nhau từ chức năng thiết lập cuộc gọi

>>> x = Decimal('0') 
>>> y = complex(0,0) 
>>> set([0, x, y]) 
{0} 
>>> {0, x, y} 
{Decimal('0'), 0j} 

(Python 2.7.12. Có thể cùng một nguyên nhân gốc rễ như đối với this câu hỏi tương tự)

+0

liên quan: [Dict/Set Parsing thứ tự nhất quán] (https://stackoverflow.com/q/34623846/4279) – jfs

Trả lời

53

Sets thử nghiệm cho bình đẳng, và cho đến khi có phiên bản Python mới, thứ tự mà họ làm điều này có thể khác nhau dựa trên biểu mẫu, bạn trao các giá trị cho tập hợp đang được xây dựng, như tôi sẽ hiển thị bên dưới.

Kể từ 0 == x là đúng 0 == y là đúng, nhưng x == ysai, hành vi ở đây thực sự là không xác định, như các thiết lập giả định rằng x == y phải đúng nếu hai bài kiểm tra đầu tiên là đúng quá.

Nếu bạn đảo ngược danh sách truyền cho set(), sau đó bạn sẽ có được kết quả tương tự như sử dụng một chữ, vì thứ tự của sự bình đẳng kiểm tra những thay đổi:

>>> set([y, x, 0]) 
set([0j, Decimal('0')]) 

và tương tự cho đảo ngược nghĩa đen:

>>> {y, x, 0} 
set([0]) 

Điều đang xảy ra là thiết lập chữ tải các giá trị vào ngăn xếp và sau đó giá trị ngăn xếp được thêm vào đối tượng bộ mới theo thứ tự ngược lại.

Miễn là 0 được tải trước tiên, hai đối tượng kia sau đó được thử nghiệm với 0 đã có trong bộ này. Khoảnh khắc một trong hai đối tượng khác được nạp đầu tiên, kiểm tra bình đẳng không và bạn nhận được hai đối tượng nói thêm:

>>> {y, 0, x} 
set([Decimal('0'), 0j]) 
>>> {x, 0, y} 
set([0j, Decimal('0')]) 

Điều đó đặt literals thêm các yếu tố ngược lại là một món quà lỗi trong tất cả các phiên bản của Python có hỗ trợ cú pháp , cho đến tận Python 2.7.12 và 3.5.2. Gần đây nó đã được sửa, xem issue 26020 (một phần của 2.7.13, 3.5.3 và 3.6, chưa có bản nào được phát hành). Nếu bạn nhìn vào 2.7.12, bạn có thể thấy rằng BUILD_SET in ceval.c đọc ngăn xếp từ trên xuống dưới:

# oparg is the number of elements to take from the stack to add 
for (; --oparg >= 0;) { 
    w = POP(); 
    if (err == 0) 
     err = PySet_Add(x, w); 
    Py_DECREF(w); 
} 

trong khi bytecode thêm yếu tố để ngăn xếp theo thứ tự ngược (đẩy 0 trên stack đầu tiên):

>>> from dis import dis 
>>> dis(compile('{0, x, y}', '', 'eval')) 
    2   0 LOAD_CONST    1 (0) 
       3 LOAD_GLOBAL    0 (x) 
       6 LOAD_GLOBAL    1 (y) 
       9 BUILD_SET    3 
      12 RETURN_VALUE 

Khắc phục là đọc các phần tử từ ngăn xếp theo thứ tự ngược lại; các Python 2.7.13 version sử dụng PEEK() thay vì POP() (và một STACKADJ() để loại bỏ các yếu tố từ ngăn xếp sau):

for (i = oparg; i > 0; i--) { 
    w = PEEK(i); 
    if (err == 0) 
     err = PySet_Add(x, w); 
    Py_DECREF(w); 
} 
STACKADJ(-oparg); 

Vấn đề kiểm tra bình đẳng có nguyên nhân gốc rễ giống như câu hỏi khác; lớp Decimal() có một số vấn đề bình đẳng với complex ở đây, được sửa trong Python 3.2 (bằng cách thực hiện Decimal() support comparisons to complex and a few other numeric types it didn't support before).

+10

Bạn có nghĩ rằng đó là một lỗi mà bình đẳng không bắc cầu cho ba loại? –

+7

@StevenRumbalski: Tôi nghĩ vậy, vâng. Tại sao không nên '0j' bằng' Decimal ('0') 'khi' 0j == 0' là true và 'Decimal ('0') == 0' cũng đúng. Python 3.2 đã sửa lỗi này bằng cách cập nhật các so sánh 'thập phân '. –

+2

'bytearray ('ab') == 'ab' == u'ab'' cũng phá vỡ sự chuyển đổi, nhưng vì' bytearray' không bị bẻ khóa nên nó không có hậu quả. – wim

7

Tất cả đều theo thứ tự mà bộ này được xây dựng, kết hợp với lỗi mà bạn đã phát hiện với your other question. Có vẻ như chữ được xây dựng theo thứ tự ngược lại để chuyển đổi từ một danh sách.

>>> {0, x, y} 
set([0j, Decimal('0')]) 
>>> {y, x, 0} 
set([0]) 
+0

À, vâng, thật sâu sắc. Làm thế nào về một tài liệu tham khảo nguồn cpython cho thấy làm thế nào các chữ được phân tích cú pháp từ phải sang trái? – wim

+0

@wim: Tôi gặp khó khăn khi tìm mã cho điều đó, chỉ vì lỗi gần đây đã được sửa. –

+0

@wim vui vẻ như âm thanh đó, tôi chưa bao giờ biến mất thông qua mã nguồn Python. –

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