Адрес памяти в Cython одинаков, но указывает на другой объект - PullRequest
0 голосов
/ 12 декабря 2018

Проблема

При определении различных объектов в Cython представления памяти возвращают один и тот же адрес.Тем не менее, сам массив будет изменен при индексации в.

Background.

У меня есть базовый класс и производный класс, написанный на Cython.Я заметил, что когда я применял к классам многопроцессорность, базовые буферы изменялись в разных процессах, что не было предназначено.Во время процедуры травления я написал простой метод __reduce__ и метод __deepcopy__, который перестраивает исходный объект.Для ясности я сократил сложность до кода ниже.Теперь мой вопрос: почему просмотры памяти возвращают один и тот же адрес?Кроме того, почему сам массив numpy изменен правильно, даже если просмотр памяти тот же

#distutils: language=c++
import numpy as np
cimport numpy as np
cdef class Temp:
    cdef double[::1] inp
    def __init__(self, inp):
        print(f'id of inp = {id(inp)}')
        self.inp = inp

cdef np.ndarray x = np.ones(10)
cdef Temp a       = Temp(x)
cdef Temp b       = Temp(x)
cdef Temp c       = Temp(x.copy())
b.inp[0] = -1
c.inp[2] = 10
print(f'id of a.inp = {id(a.inp)}\nid of b.inp = {id(b.inp))}\nid of c.inp = {id(c.inp)}')
print(f'id of a.inp.base = {id(a.inp.base)}\nid of b.inp.base = {id(b.inp.base))}\nid of c.inp.base = {id(c.inp.base)}')

print('a.inp.base',a.inp.base)
print('b.inp.base',b.inp.base) # expected to be the same as a
print('c.inp.base',c.inp.base) # expected to be different to a/b

Вывод:

id of inp = 139662709551872
id of inp = 139662709551872
id of inp = 139662709551952
id of a.inp = 139662450248672
id of b.inp = 139662450248672
id of c.inp = 139662450248672
id of a.inp.base = 139662709551872
id of b.inp.base = 139662709551872
id of c.inp.base = 139662709551952
a.inp.base [-1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
b.inp.base [-1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
c.inp.base [ 1.  1. 10.  1.  1.  1.  1.  1.  1.  1.]

1 Ответ

0 голосов
/ 12 декабря 2018

То, что мы называем представлением типизированной памяти, не является отдельным классом: в зависимости от контекста (код Cython, чистый код Python) оно меняет свою идентичность под капотом.

Так что давайте начнем с

%%cython 
cdef class Temp:
    cdef double[::1] inp

Здесь double[::1] inp имеет тип __Pyx_memviewslice, который не является объектом Python:

typedef struct {
  struct {{memview_struct_name}} *memview;
  char *data;
  Py_ssize_t shape[{{max_dims}}];
  Py_ssize_t strides[{{max_dims}}];
  Py_ssize_t suboffsets[{{max_dims}}];
} {{memviewslice_name}};

Что происходит, когда мы вызываем id(self.inp)?Очевидно, id является чисто Python-функцией, поэтому новый временный объект Python (представление памяти) должен быть создан из self.inp (только для возможности вызова id) и уничтожен непосредственно после этого.Создание временного Python-объекта выполняется с помощью __pyx_memoryview_fromslice.

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

В Python есть похожие сценарии, здесь является примером для объектов-методов , или даже более простым:

class A:
    pass
# the life times of temporary objects don't overlap, so the ids can be the equal
id(A())==id(A())
# output: True

# the life times of objects overlap, so the id cannot be equal 
a,b=A(), A()
id(a)==id(b)
# output: False

Итак, в двух словах: вы ожидаете, что один и тот же id означает, что тот же объект неправильный,Это предположение имеет место только тогда, когда время жизни объектов перекрывается.

...