Меня немного смущает поведение приведенного ниже кода, и мне интересно, может ли кто-нибудь пролить свет на этот вопрос. По сути, у меня есть матрица под названием mat
, которая является пустой ndarray
. Я получаю его диагональ, используя mat.diagonal()
, и присваиваю ее переменной diag
. Я изменил все диагональные значения mat
на 100. Теперь я обнаружил, что все значения diag
тоже изменились на 100, что, похоже, указывает на то, что diag
напрямую ссылается на элементы в mat
. Тем не менее, когда я проверяю адрес памяти первого элемента в dia
g и сравниваю его с адресом mat
, они отличаются. Как правильно на это смотреть?
import numpy as np
import pandas as pd
mat_df = pd.DataFrame(data=[[1,2,3], [4,5,6], [7,8,9]])
print(mat_df)
mat = mat_df.values
diag = mat.diagonal()
print(diag)
diag_loc = np.diag_indices_from(mat)
mat[diag_loc] = 100
print(diag)
print(diag[0])
print(id(diag[0]))
print(mat[0][0])
print(id(mat[0][0]))
mat
:
0 1 2
0 1 2 3
1 4 5 6
2 7 8 9
diag
:
[1 5 9]
Значения diag
меняются из-за изменения mat
:
[100 100 100]
первое значение diag
:
100
и его адрес
139863357577488
первое значение мата:
100
и его адрес
139863376059664
Вы не можете узнать адрес с id
. Прежде всего, id
не возвращает адрес (хотя реализация CPython использует адрес памяти для построения id
, это всего лишь одна реализация, и это не адрес как таковой). А во-вторых, это будет только адрес объекта Python (в вашем случае того, который обертывает numpy.int64
).
Этот объект Python просто создан для обертывания любых функций numpy (которые непрозрачны для Python: Python не знает, когда они должны возвращать одни и те же значения).
Простой эксперимент, который вы можете провести, чтобы убедиться, что ваш id
ничего не значит.
id(diag[0])
# 139729998312368
id(diag[0])
# 139730045496016
Видите ли, даже два последовательных абсолютно идентичных вызова не возвращают один и тот же идентификатор!
diag[0]
— это вызов numpy diag.__getitem__(0)
. Завернуты в контейнер Python, который каждый раз разный, как и результат вызова любой функции f(0)
, для которой нет оснований предполагать, что каждый идентичный вызов возвращает один и тот же результат.
Итак, если вы хотите знать, где на самом деле хранятся int64
, вы не можете спросить Python (с его функцией id
), поскольку не только для этого теперь нужен id
, но, что более важно, Python не знает. Где хранятся int64
— это внутренняя проблема библиотеки numpy. Так что вам нужно спросить numpy.
Лучший способ сделать это — использовать base
imho.
diag.base
#array([[100, 2, 3],
# [ 4, 100, 6],
# [ 7, 8, 100]])
diag.base is mat.base
# True
Но если вы настаиваете на том, чтобы у вас был какой-то адрес, вы также можете
diag.ctypes.data
# 61579664
mat.ctypes.data
# 61579664
Или для более полной информации какие данные и как просматриваются массивом
mat.__array_interface__
# {'data': (61579664, False), 'strides': (8, 24), 'descr': [('', '<i8')], 'typestr': '<i8', 'shape': (3, 3), 'version': 3}
diag.__array_interface__
# {'data': (61579664, True), 'strides': (32,), 'descr': [('', '<i8')], 'typestr': '<i8', 'shape': (3,), 'version': 3}
показывая, как эти двое используют одни и те же «данные», но используют разные «шаги» и «форму» для их использования.
Кроме того, если вы проверите diag.flags
, вы увидите, что OWNDATA
есть False
, потому что mat
является «владельцем» данных.
@nocomment Да, действительно. Рабочая часть здесь (та, о которой я имел в виду) — это «детали реализации». Это означает, что на практике это связано с адресом. Но теоретически это всего лишь выбор реализации, а не часть спецификации. В следующей версии это может измениться без предупреждения. И самое главное, это только для одной из реализаций (CPython). Самый распространенный, конечно. Python — это язык, а не интерпретатор и не программа (Cpython является и является наиболее распространенной программой, интерпретирующей язык Python). А язык Python ничего не говорит о том, что id
является адресом.
И кроме того, в любом случае здесь, даже если спрашивающий сказал: «Я на 100% уверен, что мой код будет работать только на текущей версии интерпретатора cpython», и в этом случае было бы приемлемым хаком воспользоваться знаниями об этом. Детали реализации, использовать id
все равно не получится, так как это адрес объекта, но этот объект представляет собой просто динамическую постоянно меняющуюся оболочку вокруг int64
100 и ничего не говорит о том, где 100
действительно хранится
Согласно документации,
diagonal
возвращаетthe returned array is a read-only view