Странное поведение в np.ndarray` "есть" - PullRequest
0 голосов
/ 01 февраля 2019

Встроенный оператор "is" показывает странное поведение для элемента в np.ndarray.

Хотя идентификаторы rhs и lhs одинаковы, оператор "is" возвращает False (thisповедение специфично для np.ndarray).

a = np.array([1.,])
b = a.view()
print(id(a[0] == id(b[0])))  # True
print(a[0] is b[0])  # False

Это странное поведение даже происходит без копии представления.

a = np.array([1.,])
print(a[0] is a[0])  # False

Кто-нибудь знает механизм этого странного поведения (и, возможно,доказательства или спецификации)?

Постскриптум: Пожалуйста, подумайте над двумя примерами.

  1. Если это список, это явление не наблюдается.
a = [0., 1., 2.,]
b = []
b.append(a[0])
print(a[0] is b[0])  # True
a [0] и b [0] ссылаются на один и тот же объект.
a = np.array([1.,])
b = a.view()
b[0] = 0.
print(a[0])  # 0.0
print(id(a[0]) == id(b[0]))  # True

Примечание: этот вопрос может быть дублированием, но я все еще немного запутался.

a = np.array([1.,])
b = a.view()
x = a[0]
y = b[0]
print(id(a[0]))  # 139746064667728
print(id(b[0]))  # 139746064667728
print(id(a[0]) == id(b[0])) # True
print(id(a[0]) == id(x)) # False
print(id(x) == id(y))  # False
  1. Является ли [0] временным объектом?
  2. Повторно используется идентификатор для временного объекта?
  3. Не противоречит ли оно спецификации?(https://docs.python.org/3.7/reference/expressions.html#is)
6.10.3. Identity comparisons
The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. Object identity is determined using the id() function. x is not y yields the inverse truth value.
Если идентификатор повторно используется для временных объектов, почему в этом случае идентификатор отличается?
>>> id(100000000000000000 + 1) == id(100000000000000001)
True
>>> id(100000000000000000 + 1) == id(100000000000000000)
False

Ответы [ 5 ]

0 голосов
/ 01 февраля 2019

Большая часть путаницы здесь заключается в характере a[0] в случае массива.

Для списка b[0] является действительным элементом b.Мы можем проиллюстрировать это, создав список изменяемых элементов (другие списки):

In [22]: b = [[0],[1],[2],[3]]
In [23]: b1 = b[0]
In [24]: b1
Out[24]: [0]
In [25]: b[0].append(10)
In [26]: b
Out[26]: [[0, 10], [1], [2], [3]]
In [27]: b1
Out[27]: [0, 10]
In [28]: b1.append(20)
In [29]: b
Out[29]: [[0, 10, 20], [1], [2], [3]]

Мутирование b[0] и b1 действуют на один и тот же объект.

Для массива:

In [35]: a = np.array([0,1,2,3])
In [36]: c = a.view()
In [37]: a1 = a[0]
In [38]: a += 1
In [39]: a
Out[39]: array([1, 2, 3, 4])
In [40]: c
Out[40]: array([1, 2, 3, 4])
In [41]: a1
Out[41]: 0

изменение на месте в a не меняет a1, даже если оно изменилось c.

__array_interface__ показывает нам, где хранится буфер данных для массива- в свободном смысле думать об этом как об адресе памяти этого буфера.

In [42]: a.__array_interface__['data']
Out[42]: (31233216, False)
In [43]: c.__array_interface__['data']
Out[43]: (31233216, False)
In [44]: a1.__array_interface__['data']
Out[44]: (28513712, False)

Представление имеет тот же буфер данных.Но a1 нет.a[0:1] - это единственный элемент view из a, который совместно использует буфер данных.

In [45]: a[0:1].__array_interface__['data']
Out[45]: (31233216, False)
In [46]: a[1:2].__array_interface__['data']  # 8 bytes over
Out[46]: (31233224, False)

Так что id(a[0]) почти ничего не говорит нам о a.Сравнение идентификаторов только говорит нам о том, как используются слоты памяти при создании объектов Python или нет.

0 голосов
/ 01 февраля 2019

Это распространяется на id () против оператора is.Безопасно ли сравнивать идентификаторы?Означает ли один и тот же `id` один и тот же объект? .В данном конкретном случае:

  1. a[0] и b[0] создаются заново каждый раз

    In [7]: a[0] is a[0]
    Out[7]: False
    
  2. In id(a[0]) == id(b[0]), каждыйобъект сразу же отбрасывается после получения id, и b[0] как раз попал на id недавно сброшенного a[0].Даже если это происходит каждый раз в вашей версии CPython для этого конкретного выражения (из-за определенного порядка оценки и организации кучи), это деталь реализации, и на нее нельзя полагаться.

0 голосов
/ 01 февраля 2019

Это просто из-за разницы в том, как работает is и ==, оператор is не сравнивает значения, которые он просто проверяет, ссылаются ли два операнда на один и тот же объект.

ДляНапример, если вы делаете:

print(a is a)

Вывод будет: True для получения дополнительной информации посмотрите здесь

Когда Python сравнивает, он выделяет разные позиции для операндов итакое же поведение можно наблюдать с помощью простого теста с использованием функции id.

print(id(a[0]),a[0] is a[0],id(a[0]))

Вывод будет:

140296834593128 False 140296834593248

Ответ на вопрос, который вы задаете в дополнение, почемусписки не ведут себя так, как ведут себя массивные массивы, просто исходя из их конструкции.Np.arrays были разработаны, чтобы быть более эффективными в своих возможностях обработки и более эффективными в их хранении, чем обычный список питонов.

Таким образом, каждый раз, когда вы загружаете или выполняете операцию с массивом numpy, он загружается и ему присваивается другой идентификатор, как вы можете наблюдать из следующего кода:

a = np.array([0., 1., 2.,])
b = []
b.append(a[0])
print(id(a[0]),a[0] is b[0],id(b[0]))

Вот выходные данныеНесколько повторов одного и того же кода в jupyter-lab:

140296834595096 False 140296834594496
140296834595120 False 140296834594496
140296834595120 False 140296834594496
140296834595216 False 140296834594496
140296834595288 False 140296834594496

Заметили что-то странное ?, Идентификаторы массива numpy при каждом повторении отличаются, но идентификатор объекта списка остаетсятак же.Это объясняет странное поведение для numpy массивов в вашем вопросе.

Если вы хотите узнать больше об этом поведении, я предложу numpy docs

0 голосов
/ 01 февраля 2019

Numpy сохраняет данные массива как буфер необработанных данных .Когда вы обращаетесь к данным типа a[0], они читают из буфера и создают для него объект python.Таким образом, двойной вызов a[0] создаст 2 объекта Python.is проверяет идентичность, поэтому 2 разных объекта будут сравниваться false.

Эта иллюстрация должна сделать процесс более понятным:

ПРИМЕЧАНИЕ: идентификационные номера используются последовательнопросто как примеры.ясно, что вы получите случайное число.Множественные идентификаторы 3 в примере также не всегда могут быть одним и тем же номером.Вполне возможно, что они есть, потому что id 3 многократно освобождается и, следовательно, может использоваться повторно.

a = np.array([1.,])
b = a.view()
x = a[0]    # python reads a[0], creates new object id 1.
y = b[0]    # python reads b[0] which reads a[0], creates new object id 2. (1 is used by object x)

print(id(a[0]))  # python reads a[0], creates new object id 3.
                 # After this call, the object id 3 a[0] is no longer used.
                 # Its lifetime has ended and id 3 is freed.

print(id(b[0]))  # python reads b[0] which reads a[0], creates new object id 3. 
                 # id 3 has been freed and is reusable.
                 # After this call, the object id 3 b[0] is no longer used.
                 # Its lifetime has ended and id 3 is freed (again).

print(id(a[0]) == id(b[0])) # This runs in 2 steps.
                            # First id(a[0]) is run. This is just like above, creates a object with id 3.
                            # Then a[0] is disposed of since no references are created to it. id 3 is freed again.
                            # Then id(b[0]) is run. Again, it creates a object with id 3. (Since id 3 is free).
                            # So, id(a[0]) == 3, id(b[0]) == 3. They are equal.

print(id(a[0]) == id(x)) # Following the same thing above, id(a[0]) can create a object of id 3, x maintains its reference to id 1 object. 3 != 1.

print(id(x) == id(y))  # x references id 1 object, y references id 2 object. 1 != 2

Относительно

>>> id(100000000000000000 + 1) == id(100000000000000001)
True
>>> id(100000000000000000 + 1) == id(100000000000000000)
False

распределение идентификаторов и сборка мусора являются реализациейподробности.Что гарантировано, так это то, что в один момент времени ссылки на 2 разных объекта различны, а ссылки на 2 идентичных объекта одинаковы.Проблема заключается в том, что некоторые выражения могут быть не атомарными (то есть не выполняться в один момент времени).

Python может решить использовать или не использовать освобожденные номера идентификаторов по своему усмотрению, в зависимости от реализации.В этом случае решено повторно использовать в одном случае, а не в другом.(вполне вероятно, что в id(100000000000000000 + 1) == id(100000000000000001) Python понимает, что, поскольку число одно и то же, он может эффективно использовать его, потому что 100000000000000001 будет находиться в том же месте в памяти.)

0 голосов
/ 01 февраля 2019

a[0] относится к типу <class 'numpy.float64'>.Когда вы делаете сравнение, он создает два экземпляра класса, поэтому проверка is не удалась.Однако если вы выполните следующее, вы получите то, что хотели, потому что теперь оба ссылаются на один и тот же объект.

x = a[0]
print(x is x)  # True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...