Helpex - Trao đổi & giúp đỡ Đăng nhập
549

Tại sao phần sau hoạt động không mong muốn trong Python?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?
>>> 257 is 257
True           # Yet the literal numbers compare properly

Tôi đang sử dụng Python 2.5.2. Thử một số phiên bản Python khác nhau, có vẻ như Python 2.3.3 cho thấy hành vi trên trong khoảng từ 99 đến 100.

Dựa trên những điều trên, tôi có thể đưa ra giả thuyết rằng Python được triển khai nội bộ để các số nguyên "nhỏ" được lưu trữ theo cách khác với các số nguyên lớn hơn và istoán tử có thể cho biết sự khác biệt. Tại sao sự trừu tượng bị rò rỉ? Cách tốt hơn là so sánh hai đối tượng tùy ý để xem chúng có giống nhau hay không khi tôi không biết trước chúng có phải là số hay không?

549 hữu ích 2 bình luận 86k xem chia sẻ
11 trả lời 11
419

Hãy xem này:

>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828

Đây là những gì tôi tìm thấy trong tài liệu Python 2, "Đối tượng số nguyên đơn thuần" (Nó cũng tương tự đối với Python 3 ):

Việc triển khai hiện tại giữ một mảng các đối tượng số nguyên cho tất cả các số nguyên từ -5 đến 256, khi bạn tạo một int trong phạm vi đó, bạn thực sự chỉ lấy lại một tham chiếu đến đối tượng hiện có. Vì vậy, có thể thay đổi giá trị của 1. Tôi nghi ngờ hành vi của Python trong trường hợp này là không xác định. :-)

419 hữu ích 5 bình luận chia sẻ
128

Toán tử "is" của Python hoạt động không mong muốn với các số nguyên?

Tóm lại - tôi xin nhấn mạnh: Không sử dụng isđể so sánh các số nguyên.

Đây không phải là hành vi mà bạn nên có bất kỳ kỳ vọng nào.

Thay vào đó, hãy sử dụng ==!=để so sánh tương ứng với bình đẳng và bất bình đẳng. Ví dụ:

>>> a = 1000
>>> a == 1000       # Test integers like this,
True
>>> a != 5000       # or this!
True
>>> a is 1000       # Don't do this! - Don't use `is` to test integers!!
False

Giải trình

Để biết điều này, bạn cần biết những điều sau.

Đầu tiên, islàm gì? Nó là một toán tử so sánh. Từ tài liệu :

Toán tử isis notkiểm tra nhận dạng đối tượng: x is yđúng nếu và chỉ khi x và y là cùng một đối tượng. x is not ymang lại giá trị chân lý nghịch đảo.

Và do đó, những điều sau đây là tương đương.

>>> a is b
>>> id(a) == id(b)

Từ tài liệu :

id Trả lại "danh tính" của một đối tượng. Đây là một số nguyên (hoặc số nguyên dài) đượ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ó thời gian tồn tại không trùng nhau có thể có cùng id()giá trị.

Lưu ý rằng thực tế rằng id của một đối tượng trong CPython (triển khai tham chiếu của Python) là vị trí trong bộ nhớ là một chi tiết triển khai. Các triển khai khác của Python (chẳng hạn như Jython hoặc IronPython) có thể dễ dàng có một triển khai khác cho id.

Vậy use-case để làm isgì? PEP8 mô tả :

Các phép so sánh với các hàm đơn như thế Noneluôn phải được thực hiện với ishoặc is not, không bao giờ được thực hiện với các toán tử bình đẳng.

Câu hỏi

Bạn hỏi và nêu câu hỏi sau (kèm theo mã):

Tại sao phần sau hoạt động không mong muốn trong Python?

>>> a = 256
>>> b = 256
>>> a is b
True           # This is an expected result

không phải là một kết quả mong đợi. Tại sao nó được mong đợi? Nó chỉ có nghĩa là các số nguyên có giá trị được 256tham chiếu bởi cả hai ablà cùng một trường hợp của số nguyên. Các số nguyên là bất biến trong Python, do đó chúng không thể thay đổi. Điều này sẽ không ảnh hưởng đến bất kỳ mã nào. Nó không nên được mong đợi. Nó chỉ đơn thuần là một chi tiết thực hiện.

Nhưng có lẽ chúng ta nên mừng vì không có một phiên bản riêng biệt mới nào trong bộ nhớ mỗi khi chúng ta nêu một giá trị bằng 256.

>>> a = 257
>>> b = 257
>>> a is b
False          # What happened here? Why is this False?

Có vẻ như bây giờ chúng ta có hai phiên bản số nguyên riêng biệt với giá trị 257trong bộ nhớ. Vì số nguyên là không thay đổi, điều này làm lãng phí bộ nhớ. Hãy hy vọng chúng ta không lãng phí nhiều. Chúng tôi có thể không. Nhưng hành vi này không được đảm bảo.

>>> 257 is 257
True           # Yet the literal numbers compare properly

Chà, điều này có vẻ như việc triển khai Python cụ thể của bạn đang cố gắng trở nên thông minh và không tạo ra các số nguyên có giá trị dư thừa trong bộ nhớ trừ khi nó phải làm vậy. Dường như bạn cho biết bạn đang sử dụng triển khai tham chiếu của Python, đó là CPython. Tốt cho CPython.

Thậm chí có thể tốt hơn nếu CPython có thể làm điều này trên toàn cầu, nếu nó có thể làm như vậy với giá rẻ (vì sẽ có chi phí tra cứu), có lẽ có thể triển khai một cách khác.

Nhưng đối với tác động lên mã, bạn không nên quan tâm xem một số nguyên có phải là một trường hợp cụ thể của một số nguyên hay không. Bạn chỉ nên quan tâm giá trị của trường hợp đó là gì, và bạn sẽ sử dụng các toán tử so sánh thông thường cho điều đó, tức là ==.

Có gì iskhông

iskiểm tra xem idhai đối tượng có giống nhau không. Trong CPython, đó idlà vị trí trong bộ nhớ, nhưng nó có thể là một số nhận dạng duy nhất khác trong một triển khai khác. Để trình bày lại điều này bằng mã:

>>> a is b

giống như

>>> id(a) == id(b)

Tại sao chúng ta muốn sử dụng issau đó?

Nói cách khác, đây có thể là một kiểm tra tương đối rất nhanh, kiểm tra xem hai chuỗi rất dài có giá trị bằng nhau hay không. Nhưng vì nó áp dụng cho tính duy nhất của đối tượng, do đó chúng tôi có các trường hợp sử dụng hạn chế cho nó. Trên thực tế, chúng tôi chủ yếu muốn sử dụng nó để kiểm tra None, đó là một singleton (một cá thể duy nhất tồn tại ở một nơi trong bộ nhớ). Chúng tôi có thể tạo các đĩa đơn khác nếu có khả năng kết hợp chúng, chúng tôi có thể kiểm tra lại is, nhưng chúng tương đối hiếm. Đây là một ví dụ (sẽ hoạt động trong Python 2 và 3), ví dụ:

SENTINEL_SINGLETON = object() # this will only be created one time.

def foo(keyword_argument=None):
    if keyword_argument is None:
        print('no argument given to foo')
    bar()
    bar(keyword_argument)
    bar('baz')

def bar(keyword_argument=SENTINEL_SINGLETON):
    # SENTINEL_SINGLETON tells us if we were not passed anything
    # as None is a legitimate potential argument we could get.
    if keyword_argument is SENTINEL_SINGLETON:
        print('no argument given to bar')
    else:
        print('argument to bar: {0}'.format(keyword_argument))

foo()

Những bản in:

no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz

Và như vậy chúng ta thấy, với isvà một trạm gác, chúng ta có thể phân biệt giữa khi nào barđược gọi mà không có đối số và khi nào nó được gọi với None. Đây là các trường hợp sử dụng chính cho is- không sử dụng nó để kiểm tra tính bình đẳng của các số nguyên, chuỗi, bộ giá trị hoặc những thứ khác như thế này.

128 hữu ích 5 bình luận chia sẻ
62

Tôi đến muộn nhưng, bạn muốn một số nguồn với câu trả lời của bạn? Tôi sẽ cố gắng và từ này theo cách giới thiệu để nhiều người hơn có thể theo dõi.


Một điều tốt về CPython là bạn thực sự có thể xem nguồn cho việc này. Tôi sẽ sử dụng các liên kết cho bản phát hành 3.5 , nhưng việc tìm kiếm các liên kết 2.x tương ứng là rất nhỏ.

Trong CPython, hàm C-API xử lý việc tạo một intđối tượng mới PyLong_FromLong(long v). Mô tả cho chức năng này là:

Việc triển khai hiện tại giữ một mảng các đối tượng số nguyên cho tất cả các số nguyên từ -5 đến 256, khi bạn tạo một int trong phạm vi đó, bạn thực sự chỉ lấy lại một tham chiếu đến đối tượng hiện có . Vì vậy, có thể thay đổi giá trị của 1. Tôi nghi ngờ hành vi của Python trong trường hợp này là không xác định. :-)

(Chữ nghiêng của tôi)

Không biết bạn thế nào nhưng tôi thấy điều này và nghĩ: Hãy tìm mảng đó!

Nếu bạn chưa tìm hiểu mã C triển khai CPython, bạn nên làm như vậy ; mọi thứ được tổ chức khá tốt và dễ đọc. Đối với trường hợp của chúng ta, chúng ta cần tìm trong Objectsthư mục con của cây thư mục mã nguồn chính .

PyLong_FromLonggiao dịch với longcác đối tượng nên không khó để suy ra rằng chúng ta cần nhìn vào bên trong longobject.c. Sau khi nhìn vào bên trong, bạn có thể nghĩ rằng mọi thứ đang hỗn loạn; họ có, nhưng đừng sợ, chức năng chúng tôi đang tìm kiếm đang làm lạnh ở dòng 230 chờ chúng tôi kiểm tra. Đó là một hàm nhỏ nên phần thân chính (không bao gồm phần khai báo) có thể dễ dàng dán vào đây:

PyObject *
PyLong_FromLong(long ival)
{
    // omitting declarations

    CHECK_SMALL_INT(ival);

    if (ival < 0) {
        /* negate: cant write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
    }

    /* Fast path for single-digit ints */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v; 
}

Bây giờ, chúng tôi không phải C master-code-haxxorz nhưng chúng tôi cũng không ngốc, chúng tôi có thể thấy rằng đang CHECK_SMALL_INT(ival);nhìn trộm chúng tôi một cách quyến rũ; chúng ta có thể hiểu nó có liên quan đến điều này. Hãy cùng kiểm tra nào:

#define CHECK_SMALL_INT(ival) \
    do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
        return get_small_int((sdigit)ival); \
    } while(0)

Vì vậy, đó là một macro gọi hàm get_small_intnếu giá trị ivalthỏa mãn điều kiện:

if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)

Vì vậy, những gì là NSMALLNEGINTSNSMALLPOSINTS? Macro! Chúng đây :

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS           257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS           5
#endif

Vì vậy, điều kiện của chúng tôi là if (-5 <= ival && ival < 257)cuộc gọi get_small_int.

Tiếp theo, chúng ta hãy nhìn vào get_small_inttất cả vinh quang của nó (tốt, chúng ta sẽ chỉ nhìn vào cơ thể của nó vì đó là nơi có những điều thú vị):

PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);

Được rồi, hãy khai báo a PyObject, khẳng định rằng điều kiện trước đó được giữ và thực hiện phép gán:

v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];

small_intstrông rất giống mảng mà chúng tôi đã tìm kiếm, và đúng như vậy! Chúng tôi có thể chỉ cần đọc tài liệu chết tiệt và chúng tôi sẽ biết tất cả! :

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

Vì vậy, yup, đây là chàng trai của chúng tôi. Khi bạn muốn tạo một mới inttrong phạm vi, [NSMALLNEGINTS, NSMALLPOSINTS)bạn sẽ chỉ nhận lại một tham chiếu đến một đối tượng đã tồn tại đã được phân bổ trước.

Vì tham chiếu đề cập đến cùng một đối tượng, việc phát hành id()trực tiếp hoặc kiểm tra danh tính đối với isnó sẽ trả về chính xác cùng một đối tượng .

Nhưng, khi nào chúng được phân bổ ??

Trong quá trình khởi tạo bằng_PyLong_Init Python, bạn sẽ sẵn lòng nhập vòng lặp for để thực hiện việc này cho bạn:

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {

Kiểm tra nguồn để đọc nội dung vòng lặp!

Tôi hy vọng lời giải thích của tôi đã làm cho bạn C mọi thứ rõ ràng bây giờ (chơi chữ rõ ràng là có ý định).


Nhưng 257 is 257,? Có chuyện gì vậy?

Điều này thực sự dễ giải thích hơn, và tôi đã cố gắng làm như vậy rồi ; đó là do Python sẽ thực thi câu lệnh tương tác này dưới dạng một khối duy nhất:

>>> 257 is 257

Trong quá trình phàn nàn về câu lệnh này, CPython sẽ thấy rằng bạn có hai chữ phù hợp và sẽ sử dụng cùng một PyLongObjectđại diện 257. Bạn có thể thấy điều này nếu bạn tự biên dịch và kiểm tra nội dung của nó:

>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)

Khi CPython thực hiện thao tác, bây giờ nó sẽ tải cùng một đối tượng chính xác:

>>> import dis
>>> dis.dis(codeObj)
  1           0 LOAD_CONST               0 (257)   # dis
              3 LOAD_CONST               0 (257)   # dis again
              6 COMPARE_OP               8 (is)

Vì vậy, issẽ trở lại True.

62 hữu ích 0 bình luận chia sẻ
61

Nó phụ thuộc vào việc bạn đang tìm kiếm xem 2 thứ có bằng nhau hay cùng một đối tượng hay không.

iskiểm tra xem chúng có phải là cùng một đối tượng, không chỉ bằng nhau. Các int nhỏ có thể đang trỏ đến cùng một vị trí bộ nhớ để tiết kiệm không gian

In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144

Bạn nên sử dụng ==để so sánh bình đẳng của các đối tượng tùy ý. Bạn có thể chỉ định hành vi với các thuộc tính __eq____ne__.

61 hữu ích 1 bình luận chia sẻ
38

Như bạn có thể kiểm tra trong tệp nguồn intobject.c , Python lưu trữ các số nguyên nhỏ để tăng hiệu quả. Mỗi khi bạn tạo tham chiếu đến một số nguyên nhỏ, bạn đang tham chiếu đến số nguyên nhỏ được lưu trong bộ nhớ cache, không phải là một đối tượng mới. 257 không phải là một số nguyên nhỏ, vì vậy nó được tính như một đối tượng khác.

Tốt hơn là sử dụng ==cho mục đích đó.

38 hữu ích 0 bình luận chia sẻ
20

Tôi nghĩ rằng giả thuyết của bạn là đúng. Thử nghiệm với id(nhận dạng của đối tượng):

In [1]: id(255)
Out[1]: 146349024

In [2]: id(255)
Out[2]: 146349024

In [3]: id(257)
Out[3]: 146802752

In [4]: id(257)
Out[4]: 148993740

In [5]: a=255

In [6]: b=255

In [7]: c=257

In [8]: d=257

In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)

Có vẻ như các số <= 255được coi là chữ và bất kỳ thứ gì ở trên được xử lý theo cách khác!

20 hữu ích 1 bình luận chia sẻ
14

Đối với các đối tượng giá trị không thay đổi, như ints, string hoặc datetimes, nhận dạng đối tượng không đặc biệt hữu ích. Tốt hơn hết là hãy nghĩ về sự bình đẳng. Danh tính về cơ bản là một chi tiết triển khai cho các đối tượng giá trị - vì chúng là bất biến, không có sự khác biệt hiệu quả giữa việc có nhiều tham chiếu cho cùng một đối tượng hoặc nhiều đối tượng.

14 hữu ích 0 bình luận chia sẻ
13

Có một vấn đề khác chưa được chỉ ra trong bất kỳ câu trả lời hiện có nào. Python được phép hợp nhất hai giá trị bất biến bất kỳ và các giá trị int nhỏ được tạo trước không phải là cách duy nhất điều này có thể xảy ra. Việc triển khai Python không bao giờ được đảm bảo thực hiện điều này, nhưng tất cả chúng đều làm được điều đó không chỉ là những int nhỏ.


Đối với một điều, có một số giá trị được tạo trước khác, chẳng hạn như giá trị trống tuple, strbytesvà một số chuỗi ngắn (trong CPython 3.6, đó là 256 chuỗi ký tự Latin-1). Ví dụ:

>>> a = ()
>>> b = ()
>>> a is b
True

Nhưng cũng có thể, ngay cả các giá trị không được tạo trước cũng có thể giống hệt nhau. Hãy xem xét các ví dụ sau:

>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True

Và điều này không giới hạn ở intcác giá trị:

>>> g, h = 42.23e100, 42.23e100
>>> g is h
True

Rõ ràng, CPython không đi kèm với floatgiá trị được tạo trước cho 42.23e100. Vì vậy, những gì đang xảy ra ở đây?

Trình biên dịch CPython sẽ sáp nhập giá trị không đổi của một số loại tiếng-bất biến như int, float, str, bytes, trong đơn vị biên soạn cùng. Đối với một mô-đun, toàn bộ mô-đun là một đơn vị biên dịch, nhưng ở trình thông dịch tương tác, mỗi câu lệnh là một đơn vị biên dịch riêng biệt. Vì cdđược xác định trong các câu lệnh riêng biệt, các giá trị của chúng không được hợp nhất. Vì efđược xác định trong cùng một câu lệnh, nên các giá trị của chúng được hợp nhất.


Bạn có thể xem những gì đang xảy ra bằng cách tháo gỡ mã bytecode. Hãy thử xác định một hàm thực hiện e, f = 128, 128và sau đó gọi hàm dis.disđó, và bạn sẽ thấy rằng có một giá trị không đổi duy nhất(128, 128)

>>> def f(): i, j = 258, 258
>>> dis.dis(f)
  1           0 LOAD_CONST               2 ((128, 128))
              2 UNPACK_SEQUENCE          2
              4 STORE_FAST               0 (i)
              6 STORE_FAST               1 (j)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480

Bạn có thể nhận thấy rằng trình biên dịch đã được lưu trữ 128dưới dạng một hằng số mặc dù nó không thực sự được sử dụng bởi mã bytecode, điều này cung cấp cho bạn ý tưởng về cách trình biên dịch của CPython tối ưu hóa rất ít. Điều đó có nghĩa là các bộ giá trị (không trống) thực sự không được hợp nhất:

>>> k, l = (1, 2), (1, 2)
>>> k is l
False

Đặt nó trong một hàm, disnó và nhìn vào co_consts—có a 1và a 2, hai (1, 2)bộ giá trị giống nhau 12nhưng không giống nhau, và một ((1, 2), (1, 2))bộ giá trị có hai bộ giá trị khác nhau bằng nhau.


Có một cách tối ưu hóa nữa mà CPython thực hiện: xâu chuỗi. Không giống như việc gấp liên tục của trình biên dịch, điều này không bị hạn chế đối với các ký tự mã nguồn:

>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True

Mặt khác, nó bị giới hạn về strloại và các chuỗi thuộc loại lưu trữ nội bộ "ascii compact", "compact" hoặc "kế thừa sẵn sàng" , và trong nhiều trường hợp, chỉ "ascii compact" mới được thực tập.


Ở bất kỳ mức độ nào, các quy tắc cho những giá trị nào phải là, có thể có hoặc không thể khác nhau giữa các triển khai và giữa các phiên bản của cùng một triển khai và thậm chí có thể giữa các lần chạy cùng một mã trên cùng một bản sao của cùng một triển khai .

Có thể đáng để học các quy tắc cho một Python cụ thể để tận hưởng niềm vui của nó. Nhưng nó không đáng để dựa vào chúng trong mã của bạn. Quy tắc an toàn duy nhất là:

  • Không viết mã giả định hai giá trị bất biến bằng nhau nhưng được tạo riêng biệt là giống hệt nhau (không sử dụng x is y, sử dụng x == y)
  • Không viết mã giả định hai giá trị bất biến bằng nhau nhưng được tạo riêng biệt là khác biệt (không sử dụng x is not y, sử dụng x != y)

Hoặc, nói cách khác, chỉ sử dụng isđể kiểm tra các tệp đơn được ghi lại (giống như None) hoặc chỉ được tạo ở một nơi trong mã (như _sentinel = object()thành ngữ).

13 hữu ích 2 bình luận chia sẻ
8

is toán tử bình đẳng danh tính (hoạt động giống như id(a) == id(b)); chỉ là hai số bằng nhau không nhất thiết phải là cùng một đối tượng. Vì lý do hiệu suất, một số số nguyên nhỏ tình cờ được ghi nhớ để chúng có xu hướng giống nhau (điều này có thể được thực hiện vì chúng là bất biến).

===Mặt khác, toán tử của PHP được mô tả là kiểm tra sự bình đẳng và kiểu: x == y and type(x) == type(y)theo nhận xét của Paulo Freitas. Điều này sẽ đủ cho các số phổ biến, nhưng khác isvới các lớp định nghĩa __eq__theo cách vô lý:

class Unequal:
    def __eq__(self, other):
        return False

PHP rõ ràng cho phép điều tương tự đối với các lớp "tích hợp sẵn" (mà tôi hiểu có nghĩa là được triển khai ở cấp độ C, không phải trong PHP). Một cách sử dụng ít vô lý hơn một chút có thể là một đối tượng bộ đếm thời gian, có giá trị khác mỗi khi nó được sử dụng như một số. Khá lý do tại sao bạn muốn mô phỏng Visual Basic's Nowthay vì hiển thị rằng đó là một đánh giá với time.time()tôi không biết.

Greg Hewgill (OP) đã đưa ra một nhận xét làm rõ "Mục tiêu của tôi là so sánh danh tính đối tượng, thay vì bình đẳng về giá trị. Ngoại trừ số, tôi muốn coi nhận dạng đối tượng giống như bình đẳng về giá trị."

Đây sẽ là một câu trả lời khác, vì chúng ta phải phân loại mọi thứ dưới dạng số hay không, để chọn xem chúng ta so sánh với ==hoặc is. CPython xác định giao thức số , bao gồm cả PyNumber_Check, nhưng điều này không thể truy cập được từ chính Python.

Chúng tôi có thể cố gắng sử dụng isinstancevới tất cả các loại số mà chúng tôi biết, nhưng điều này chắc chắn sẽ không đầy đủ. Mô-đun loại chứa danh sách StringTypes nhưng không có NumberTypes. Kể từ Python 2.6, các lớp số được xây dựng có một lớp cơ sở numbers.Number, nhưng nó có cùng một vấn đề:

import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)

Nhân tiện, NumPy sẽ tạo ra các trường hợp số thấp riêng biệt.

Tôi thực sự không biết câu trả lời cho biến thể này của câu hỏi. Tôi cho rằng về mặt lý thuyết người ta có thể sử dụng ctypes để gọi PyNumber_Check, nhưng ngay cả chức năng đó cũng đã được tranh luận , và nó chắc chắn không di động. Chúng tôi sẽ chỉ cần ít cụ thể hơn về những gì chúng tôi thử nghiệm bây giờ.

Cuối cùng, vấn đề này bắt nguồn từ việc Python ban đầu không có một cây kiểu với các vị từ như Scheme's number? , hoặc Haskell's type class Num . iskiểm tra danh tính đối tượng, không bình đẳng giá trị. PHP cũng có một lịch sử đầy màu sắc, nơi ===dường như ischỉ hoạt động trên các đối tượng trong PHP5, chứ không phải PHP4 . Đó là những khó khăn ngày càng tăng của việc di chuyển giữa các ngôn ngữ (bao gồm cả các phiên bản của một).

8 hữu ích 0 bình luận chia sẻ
4

Nó cũng xảy ra với các chuỗi:

>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Bây giờ mọi thứ có vẻ ổn.

>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)

Điều đó cũng được mong đợi.

>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)

>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)

Bây giờ điều đó thật bất ngờ.

4 hữu ích 2 bình luận chia sẻ
4

Có gì mới trong Python 3.8: Những thay đổi trong hành vi Python :

Trình biên dịch bây giờ tạo ra một Cảnh báo cú pháp khi kiểm tra danh tính ( isis not) được sử dụng với một số loại ký tự nhất định (ví dụ: chuỗi, int). Những điều này thường có thể hoạt động tình cờ trong CPython, nhưng không được đảm bảo bởi thông số ngôn ngữ. Cảnh báo khuyên người dùng nên sử dụng các bài kiểm tra bình đẳng ( ==!=) để thay thế.

4 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ python int operators identity python-internals , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading