2013-03-14 21 views
7

Nhà điều hành is được sử dụng kiểm tra danh tính.Toán tử `is` có sử dụng phương thức __magic__ trong Python không?

tôi đã tự hỏi nếu is điều hành và id() chức năng cuộc gọi bất kỳ phương pháp __magic__, cách == gọi __eq__.

tôi đã có một số vui vẻ kiểm tra ra __hash__:

class Foo(object): 
    def __hash__(self): 
     return random.randint(0, 2 ** 32) 

a = Foo() 
b = {} 
for i in range(5000): 
    b[a] = i 

Hãy suy nghĩ về dict b và giá trị của b[a]

Mỗi tra cứu tiếp theo của d[a] hoặc là một KeyError hoặc một số nguyên ngẫu nhiên.

Nhưng khi docs on the special methods bang

[việc thực hiện mặc định của] x. __hash__() trả về id (x).

Vì vậy, có mối quan hệ giữa hai, nhưng chỉ theo cách khác.

Tôi đã nhìn thấy nhiều questions trên isid đây, và answers đã giúp nhiều confused tâm trí, nhưng tôi không thể tìm thấy câu trả lời cho cái này.

+0

Ví dụ của bạn thực sự khá thú vị. Được yêu thích cho điều đó :) – nneonneo

+0

Định nghĩa của 'băm' mặc định không đúng. Trong CPython, 'PyBaseObject_Type.tp_hash' là' dài _Py_HashPointer (void * p) {long x; size_t y = (size_t) p; y = (y >> 4) | (y << (8 * SIZEOF_VOID_P - 4)); x = (dài) y; if (x == -1) x = -2; return x;} 'Điều này quay địa chỉ bằng 4 bit để giảm va chạm băm, với các bit 2-4 dưới cùng của địa chỉ có lẽ là 0. – eryksun

Trả lời

14

Không, is là so sánh con trỏ thẳng và id chỉ trả lại địa chỉ của đối tượng được truyền tới số long.

Từ ceval.c:

case PyCmp_IS: 
    res = (v == w); 
    break; 
case PyCmp_IS_NOT: 
    res = (v != w); 
    break; 

vw đây chỉ đơn giản là PyObject *.

Từ bltinmodule.c:

static PyObject * 
builtin_id(PyObject *self, PyObject *v) 
{ 
    return PyLong_FromVoidPtr(v); 
} 

PyDoc_STRVAR(id_doc, 
"id(object) -> integer\n\ 
\n\ 
Return the identity of an object. This is guaranteed to be unique among\n\ 
simultaneously existing objects. (Hint: it's the object's memory address.)"); 
+2

+1 để đào vào nguồn (mà tôi quá lười để làm;)) –

+2

Một dòng mã trị giá hàng ngàn bức ảnh. –

+0

Câu trả lời này dành riêng cho CPython; trong các triển khai khác (ít nhất là Jython và PyPy), 'is' không phải là so sánh con trỏ, và' id' không trả về địa chỉ. – abarnert

9

Câu trả lời ngắn gọn là: Không, họ không. Như các tài liệu mà bạn liên kết đến nói:

Các nhà khai thác isis not thử nghiệm cho nhận dạng đối tượng: x is y là đúng nếu và chỉ nếu xy là cùng một đối tượng.

Là "cùng một đối tượng" không phải là thứ bạn được phép ghi đè. Nếu đối tượng của bạn không phải là đối tượng giống như đối tượng khác, nó không thể giả vờ là.


Vậy… Tại sao? Điều gì sẽ gây hại cho việc bạn ghi đè is và/hoặc id? Rõ ràng nó sẽ luôn luôn là một điều ngu ngốc để làm, nhưng Python cho phép bạn làm rất nhiều điều ngu ngốc nếu bạn cố gắng hết sức.

Câu hỏi thường gặp về thiết kế và các tài liệu tương tự không nói. Nhưng tôi nghi ngờ nó chủ yếu là vì nó làm cho nó dễ dàng hơn để gỡ lỗi Python và một số mô-đun thư viện chuẩn sâu hơn, biết có cách nào đó, từ bên trong trình thông dịch, để xác minh rằng hai tên thực sự tham chiếu đến cùng một đối tượng, hoặc in ra id để đảm bảo tên không thay đổi theo thời gian, v.v. Hãy tưởng tượng gỡ lỗi weakref hoặc thậm chí pickle, nếu không có điều đó.


Vì vậy, "chính xác" nghĩa là gì? Vâng, đó là tùy thuộc vào thông dịch viên. Rõ ràng là không thể phân biệt hai trường hợp của cùng một đối tượng ở cấp độ ngôn ngữ, và có thể ở cấp độ thông dịch viên (đặc biệt vì có một API được xác định rõ ràng để cắm vào hầu hết các cài đặt trình thông dịch).

Tất cả các triển khai chính đều xử lý việc này bằng cách trì hoãn khái niệm nhận dạng ở cấp thấp hơn. CPython so sánh giá trị của các PyObject* con trỏ, Jython nhận dạng so sánh tài liệu tham khảo Java, PyPy làm một is trên các đối tượng objectspace ...

Đó là giá trị nhìn vào PyPy source, mà đòi hỏi sự "x is y iff xy là cùng một đối tượng "đúng theo cả hai hướng. Biểu thức cấp cao nhất x is y là đúng iff, bất kỳ đối tượng wxwy trong không gian đối tượng thích hợp là, wy.is_(wx) là đúng và is_ được triển khai dưới dạng wy is wx. Vì vậy, x is y ở cấp N iff y is x ở cấp N-1.


Chú ý rằng điều này có nghĩa bạn có thể khá dễ dàng sử dụng PyPy để xây dựng một phương ngữ của Python nơi isthể được ghi đè, chỉ bằng cách gắn is_ đến một phương pháp dunder __is__ ở cấp cao hơn. Nhưng có một cách đơn giản để làm điều tương tự:

def is_(x, y): 
    if hasattr(x, '__is__'): 
     return x.__is__(y) 
    elif hasattr(y, '__is__'): 
     return y.__is__(x) 
    else: 
     return x is y 

Bây giờ chơi với is_(x, y) thay vì x is y, và xem nếu bạn có thể tìm thấy bất kỳ rắc rối vui vẻ trước khi thực hiện công việc khó khăn của việc sửa đổi thông dịch viên (thậm chí nếu nó isn' trong trường hợp này).


Vì vậy, is phải làm gì với id? Có thể is được triển khai ở trên cùng của id — ví dụ: x is y chỉ cần kiểm tra id(x) == id(y)? Vâng, id:

Trả lại “danh tính” của một đối tượng. Đây là một số nguyên được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt thời gian tồn tại của nó. Hai đối tượng có tuổi thọ không chồng chéo có thể có cùng giá trị id().

Vì vậy, id của một đối tượng là duy nhất và liên tục trong suốt cuộc đời của mình, và x is y là đúng khi và chỉ khi họ đang cùng một đối tượng, do đó x is y là đúng khi và chỉ khi id(x) == id(y), phải không?

Vâng, id có thể được phục hồi theo bất cứ điều gì bạn muốn và không được phép ảnh hưởng đến is.Nếu bạn đã phác thảo định nghĩa rất cẩn thận (hãy nhớ rằng nếu bạn loại bỏ tham chiếu builtins thành id, bất kỳ triển khai nào được sử dụng ở đó thậm chí không được bảo đảm tồn tại nữa hoặc hoạt động chính xác nếu nó tồn tại ...), bạn có thể xác định is trên đầu trang của việc triển khai mặc định là id.

Nhưng nó sẽ là một điều kỳ lạ để làm. Trong CPython, trong đó id(x) chỉ "trả về địa chỉ của đối tượng trong bộ nhớ", điều tương tự như giá trị của con trỏ tới đối tượng trong bộ nhớ. Nhưng đó chỉ là một tạo phẩm của CPython; không có gì nói rằng việc triển khai khác phải làm cho id trả về giá trị cơ bản được sử dụng để so sánh danh tính dưới dạng số nguyên. Trong thực tế, nó không rõ ràng như thế nào bạn thậm chí sẽ làm điều đó trong một thực hiện bằng văn bản trong một ngôn ngữ mà không có con trỏ (có thể được đúc sang số nguyên). Trong PyPy, các id của một đối tượng thậm chí có thể là một giá trị tính lần đầu tiên nó được truy cập và stashed trong một từ điển trong không gian đối tượng, được khóa bởi chính đối tượng đó.


Đối với __hash__, bạn đang hiểu sai phần quan trọng của tài liệu.

[...] x.__hash__() trả về id(x).

Phần bạn đã bỏ qua làm cho nó rõ ràng rằng điều này chỉ đúng đối với các trường hợp của lớp do người dùng xác định (không xác định lại __hash__). Điều này rõ ràng là không đúng đối với, ví dụ: tuple. Trong ngắn hạn, danh tính không có gì để làm với băm, ngoại trừ cho một số đối tượng nhận dạng làm cho một giá trị băm thuận tiện.

+2

"Nếu đối tượng của bạn không phải là đối tượng tương tự như đối tượng khác, nó không thể giả vờ là được." +1 – mgilson

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