Почему расположение памяти двух переменных одинаково для отдельной целочисленной переменной, объявленной отдельно в Python? [Дубликат]

Пожалуйста, проверьте, как только выбранная база данных не была вызвана, потому что некоторая база данных не выбрана

Проверьте

mysql_select_db('database name ')or DIE('Database name is not available!');

перед запросом MySQL, а затем перейдите к следующему шагу

$result = mysql_query('SELECT * FROM Users WHERE UserName LIKE $username');

f($result === FALSE) {
    die(mysql_error());
380
задан Jim Fasarakis Hilliard 23 January 2016 в 15:11
поделиться

11 ответов

Взгляните на это:

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

EDIT: Вот что я нашел в документации Python 2, «Plain Integer Objects» (То же самое для Python 3 ):

Текущая реализация хранит массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, на котором вы просто возвращаетесь ссылку на существующий объект. Поэтому должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено. : -)

302
ответ дан Ringil 25 August 2018 в 20:50
поделиться

Оператор «is» Python ведет себя неожиданно с целыми числами?

Вкратце - позвольте мне подчеркнуть: Не используйте is для сравнения целых чисел.

Это не поведение, о котором вы должны ожидать.

Вместо этого используйте == и != для сравнения для равенства и неравенства соответственно. Например:

>>> 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

Объяснение

Чтобы это знать, вам нужно знать следующее.

Во-первых, что делает is? Это оператор сравнения. Из документации :

Операторы is и is not проверяют идентификатор объекта: x is y истинно тогда и только тогда, когда x и y являются одинаковыми объект. x is not y дает обратное значение истины.

И поэтому следующие эквиваленты.

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

Из документации :

id Вернуть «идентификатор» объекта. Это целое число (или длинное целое число), которое гарантировано будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимся временем жизни могут иметь одинаковое значение id().

Обратите внимание, что тот факт, что идентификатор объекта в CPython (эталонная реализация Python) - это местоположение в память - это деталь реализации. Другие реализации Python (например, Jython или IronPython) могут легко иметь другую реализацию для id.

Итак, каков прецедент для is? PEP8 описывает :

. Сравнение с синглонами типа None всегда должно выполняться с помощью is или is not, никогда не выполняемых операторов равенства.

< / blockquote>

Вопрос

Вы задаете и задаете следующий вопрос (с кодом):

Почему в Python происходит непредвиденное поведение?

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

Ожидаемый результат not . Почему это ожидалось? Это означает, что целые числа, оцененные в 256, на которые ссылаются как a, так и b, являются одним и тем же экземпляром целого числа. Целые числа неизменны в Python, поэтому они не могут измениться. Это не должно влиять на какой-либо код. Этого нельзя ожидать. Это всего лишь деталь реализации.

Но, возможно, мы должны быть рады, что каждый отдельный экземпляр в памяти не будет каждый раз, когда мы укажем, что значение равно 256.

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

Looks например, теперь у нас есть два отдельных экземпляра целых чисел со значением 257 в памяти. Поскольку целые числа неизменны, это отнимает память. Будем надеяться, что мы не будем тратить много денег. Наверное, нет. Но это поведение не гарантируется.

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

Ну, похоже, что ваша конкретная реализация Python пытается быть умной и не создавать избыточно ценные целые числа в памяти, если только она не имеет к. Вы, кажется, указываете, что используете референтную реализацию Python, которая является CPython. Хорошо для CPython.

Возможно, было бы лучше, если бы CPython мог сделать это глобально, если бы он мог сделать это дешево (так как это будет стоить в поиске), возможно, другая реализация.

Но что касается влияния на код, вам все равно, является ли целое число конкретным экземпляром целого числа. Вы должны знать, что такое значение этого экземпляра, и вы использовали бы для этого обычные операторы сравнения, т. Е. ==.

Что is делает

is проверяет, что id двух объектов одинаковы. В CPython id - это место в памяти, но это может быть какое-то другое уникально идентифицирующее число в другой реализации. Чтобы переформулировать это с помощью кода:

>>> a is b

совпадает с

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

Почему мы хотели бы использовать is тогда?

Это может быть очень быстрой проверкой, чтобы сказать, проверяя, являются ли две очень длинные строки равными по стоимости. Но поскольку это относится к уникальности объекта, мы, таким образом, имеем ограниченные прецеденты. Фактически, мы в основном хотим использовать его для проверки на None, который является одноэлементным (единственный экземпляр, существующий в одном месте в памяти). Мы могли бы создать другие синглтоны, если есть потенциал для их объединения, что мы можем проверить с помощью is, но они относительно редки. Вот пример (будет работать в Python 2 и 3), например

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()

Что печатает:

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

Итак, мы видим, что с is и дозорным, мы могут различать, когда bar вызывается без аргументов и когда он вызывается с помощью None. Это первичные варианты использования для is - do not использовать его для проверки равенства целых чисел, строк, кортежей или других подобных вещей.

61
ответ дан Aaron Hall 25 August 2018 в 20:50
поделиться

Есть еще одна проблема, которая не указана ни в одном из существующих ответов. Python разрешено объединять любые два неизменных значения, и предварительно созданные значения малых значений не являются единственным способом, которым это может случиться. Реализация Python никогда не гарантирована , но все они делают это не более, чем просто малые int.


Во-первых, есть еще некоторые предварительно созданные такие как пустые tuple, str и bytes и некоторые короткие строки (в CPython 3.6 это 256 односимвольных строк Latin-1). Например:

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

Но также даже не созданные заранее значения могут быть одинаковыми. Рассмотрим следующие примеры:

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

И это не ограничено значениями int:

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

Очевидно, что CPython не поставляется с предварительно созданным float для параметра 42.23e100. Итак, что здесь происходит?

Компилятор CPython будет объединять постоянные значения некоторых известных неизменяемых типов, таких как int, float, str, bytes, в одном модуле компиляции. Для модуля весь модуль является единицей компиляции, но в интерактивном интерпретаторе каждый оператор представляет собой отдельный блок компиляции. Поскольку c и d определены в отдельных утверждениях, их значения не объединяются. Поскольку e и f определены в том же самом заявлении, их значения сливаются.


Вы можете видеть, что происходит, разобрав байт-код. Попробуйте определить функцию, которая выполняет e, f = 128, 128, а затем называет dis.dis на ней, и вы увидите, что существует одно постоянное значение (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

. Вы можете заметить, что компилятор сохранил 128 как константу, даже если он фактически не используется байтовым кодом, что дает вам представление о том, как мало оптимизирует компилятор CPython. Это означает, что (непустые) кортежи на самом деле не сливаются:

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

Поместите это в функцию, dis, и посмотрите на co_consts - есть 1 и 2, два (1, 2) кортежа, которые имеют одинаковые 1 и 2, но не идентичны, и кортеж ((1, 2), (1, 2)), который имеет два разных одинаковых кортежа.


Есть еще одна оптимизация, которую выполняет CPython: string interning. В отличие от сгибания константы компилятора, это не ограничивается литералами исходного кода:

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

С другой стороны, он ограничен типом str и строками типа внутреннего хранилища «ascii compact», «compact» или «legacy ready» , и во многих случаях только «ascii compact» будет интернирован.


Во всяком случае, правила для чего значения должны быть, могут быть или не могут отличаться от реализации до реализации, а также между версиями одной и той же реализации и, возможно, даже между прогонами одного и того же кода в одной и той же копии одной и той же реализации.

Возможно, стоит изучить правила для одного конкретного Python для удовольствия. Но не стоит полагаться на них в вашем коде. Единственное безопасное правило:

  • Не записывайте код, который предполагает наличие двух равных, но отдельно созданных неизменяемых значений.
  • Не записывайте код, который предполагает два равных, но отдельно

Или, другими словами, использовать is только для проверки документированных синглетов (например, None) или которые создаются только в одном месте в код (например, идиома _sentinel = object()).

2
ответ дан abarnert 25 August 2018 в 20:50
поделиться

Для объектов неизменяемых значений, таких как int, строки или даты, идентификация объекта не особенно полезна. Лучше думать о равенстве. Идентификация - это, по сути, деталь реализации объектов значения - поскольку они неизменяемы, нет эффективной разницы между наличием нескольких ссылок на один и тот же объект или несколько объектов.

12
ответ дан babbageclunk 25 August 2018 в 20:50
поделиться

Я думаю, что ваши гипотезы верны. Эксперимент с id (идентификация объекта):

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)

Похоже, что числа <= 255 рассматриваются как литералы, а что-то выше, обрабатывается по-разному!

31
ответ дан Jim Fasarakis Hilliard 25 August 2018 в 20:50
поделиться

Это также происходит со строками:

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

Теперь все кажется прекрасным.

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

Это тоже ожидается.

>>> 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)

Теперь это неожиданным.

4
ответ дан sobolevn 25 August 2018 в 20:50
поделиться

Посмотрите здесь

Текущая реализация сохраняет массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне вы фактически просто возвращаете ссылку на существующий объект.

3
ответ дан user5319825 25 August 2018 в 20:50
поделиться

Как вы можете проверить исходный файл intobject.c , Python кэширует мелкие целые числа для эффективности. Каждый раз, когда вы создаете ссылку на небольшое целое число, вы ссылаетесь на кэшированное маленькое целое число, а не на новый объект. 257 не является маленьким целым числом, поэтому он вычисляется как другой объект.

Для этого лучше использовать ==.

55
ответ дан Vallentin 25 August 2018 в 20:50
поделиться

is является оператором равенства идентичности (функционирующим как id(a) == id(b)); просто два одинаковых числа не обязательно являются одним и тем же объектом. По соображениям производительности некоторые маленькие целые числа являются memoized , поэтому они будут иметь тенденцию быть одинаковыми (это можно сделать, поскольку они являются неизменяемыми).

PHP ===, с другой стороны, описывается как проверка равенства и типа: x == y and type(x) == type(y) в соответствии с комментарием Пауло Фрейтаса. Этого достаточно для общих чисел, но отличается от is для классов, которые определяют __eq__ абсурдным образом:

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

PHP, по-видимому, допускает то же самое для «встроенных» классов (что Я подразумеваю реализацию на уровне C, а не в PHP). Немного менее абсурдным использованием может быть объект таймера, который имеет различное значение каждый раз, когда он используется как число. Весьма почему вы хотели бы эмулировать Visual Basic Now, а не показывать, что это оценка с time.time(), которую я не знаю.

Грег Хьюджилл (OP) сделал один пояснительный комментарий «Моя цель это сравнение идентичности объекта, а не равенства стоимости. За исключением чисел, где я хочу рассматривать идентичность объекта так же, как равенство стоимости. "

У этого был бы еще один ответ, поскольку мы должны классифицировать вещи как числа или нет, чтобы выбрать, сравнивать ли мы с == или is. CPython определяет протокол number , включая PyNumber_Check, но это не доступно из самого Python.

Мы могли бы попытаться использовать isinstance со всеми которые мы знаем, но это неизбежно было бы неполным. Модуль типов содержит список StringTypes, но не NumberTypes. Начиная с Python 2.6, встроенные числовые классы имеют базовый класс numbers.Number , но он имеет ту же проблему:

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

Кстати, NumPy будет производить отдельные экземпляры с низкими номерами.

На самом деле я не знаю ответа на этот вариант вопроса. Я предполагаю, что теоретически можно использовать ctypes для вызова PyNumber_Check, но даже эту функцию обсуждали , и это, конечно, не переносимо. Мы просто должны быть менее конкретными в отношении того, что мы тестируем сейчас.

В конце концов, эта проблема связана с тем, что Python первоначально не имел дерева типов с предикатами, такими как Scheme number? или класса Haskell Num . is проверяет идентификацию объекта, а не равенство значения. PHP также имеет красочную историю, где ===, по-видимому, ведет себя как is только на объектах в PHP5, но не на PHP4 . Таковы растущие трудности перемещения по языкам (включая версии одного).

8
ответ дан Yann Vernier 25 August 2018 в 20:50
поделиться

Я опаздываю, но вы хотите получить какой-то источник с вашим ответом? *

Хорошо, что в CPython вы можете увидеть источник этого. На данный момент я собираюсь использовать ссылки для релиза 3.5; поиск соответствующих 2.x является тривиальным.

В CPython функция C-API, которая обрабатывает создание нового объекта int, - PyLong_FromLong(long v) . Описание этой функции:

Текущая реализация хранит массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, который вы на самом деле просто получаете назад ссылку на существующий объект . Поэтому должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено. : -)

Не знаю о вас, но я вижу это и думаю: Найдем этот массив!

Если у вас есть " t fiddled с кодом C, реализующим CPython , вы должны , все довольно организовано и доступно для чтения. Для нашего случая нам нужно посмотреть в подкаталоге Objects/ дерева каталогов основного исходного кода .

PyLong_FromLong имеет дело с long, поэтому нетрудно вывести, что нам нужно заглянуть внутрь longobject.c . Посмотрев внутрь, вы можете подумать, что вещи хаотичны; они, но не боятся, функция, которую мы ищем, пугает в line 230 , ожидая, что мы это проверим. Это небольшая функция, поэтому основное тело (исключая декларации) легко вставлено здесь:

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; 
}

Теперь мы не C master-code-haxxorz , но мы «Также не глупый, мы можем видеть, что CHECK_SMALL_INT(ival); заглядывает к нам все соблазнительно; мы можем понять, что это как-то связано с этим. Давайте посмотрим:

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

Итак, это макрос, который вызывает функцию get_small_int, если значение ival удовлетворяет условию:

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

Итак, каковы NSMALLNEGINTS и NSMALLPOSINTS? Если вы догадались о макросах, вы ничего не получаете, потому что это не был таким сложным вопросом. В любом случае, здесь они :

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

Итак, наши состояние - if (-5 <= ival && ival < 257) call get_small_int.

Некуда идти, но продолжайте наше путешествие, взглянув на get_small_int во всей красе (ну, мы просто посмотрим на это тело, потому что это были интересные вещи ):

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

Хорошо, объявите PyObject, утвердите, что предыдущее условие выполнено и выполняет назначение:

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

small_ints выглядит так же, как массив, который мы искали .. и это! Мы могли бы просто прочитать проклятую документацию, и мы все знали бы! :

/* 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];

Так что, это наш парень , Когда вы хотите создать новый int в диапазоне [NSMALLNEGINTS, NSMALLPOSINTS), вы просто вернете ссылку на уже существующий объект, который был предварительно выделен.

Поскольку ссылка ссылается на один и тот же объект, выдача id() непосредственно или проверка подлинности с is на нем вернет точно то же самое.

Но когда они выделены ??

Во время инициализации в _PyLong_Init Python с радостью войдет в цикл for, сделайте это для вас:

for (ival = -NSMALLNEGINTS; ival <  NSMALLPOSINTS; ival++, v++) {
    // Look me up!
}

Надеюсь, мои объяснения заставили вас C (каламбур явно задуматься) теперь ясно.


Но, 257 - 257? Что случилось?

Это на самом деле проще объяснить, , и я уже пытался сделать это ; это связано с тем, что Python выполнит этот интерактивный оператор:

>>> 257 is 257

как один блок. Во время компиляции этого утверждения CPython увидит, что у вас есть два подходящих литерала, и будет использовать тот же PyLongObject, представляющий 257. Вы можете это увидеть, если сами делаете компиляцию и изучаете ее содержимое:

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

Когда CPython выполняет операцию; теперь он будет загружать тот же самый объект:

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

Таким образом, is вернет True.


* - Я попробую и слово это более вводным образом, чтобы большинство могло следовать дальше.

31
ответ дан Jim Fasarakis Hilliard 25 August 2018 в 20:51
поделиться

Это зависит от того, хотите ли вы посмотреть, равны ли 2 вещи или один и тот же объект.

is проверяет, являются ли они одним и тем же объектом, а не только равными. Маленькие ints, вероятно, указывают на одно и то же место памяти для эффективности пространства

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

Вы должны использовать == для сравнения равенства произвольных объектов. Вы можете указать поведение с атрибутами __eq__ и __ne__.

55
ответ дан Vallentin 25 August 2018 в 20:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: