Python 3.x cython中MemoryView的地址相同,但指向不同的对象 问题

Python 3.x cython中MemoryView的地址相同,但指向不同的对象 问题,python-3.x,numpy,cython,typed-memory-views,Python 3.x,Numpy,Cython,Typed Memory Views,在cython中定义不同对象时,MemoryView将返回相同的地址。但是,当索引到时,数组本身将被修改 背景。 我用cython编写了基类和派生类。我注意到,当我对类应用多处理时,底层缓冲区在不同的进程中发生了变化,这是无意的。在酸洗过程中,我编写了一个简单的uuu reduce uuuu方法和uu deepcopy uu方法来重建原始对象。为了清楚起见,我将复杂性降低到下面的代码中。现在我的问题是,为什么MemoryView返回相同的地址?此外,为什么即使memoryview是相同的,num

在cython中定义不同对象时,MemoryView将返回相同的地址。但是,当索引到时,数组本身将被修改

背景。 我用cython编写了基类和派生类。我注意到,当我对类应用多处理时,底层缓冲区在不同的进程中发生了变化,这是无意的。在酸洗过程中,我编写了一个简单的uuu reduce uuuu方法和uu deepcopy uu方法来重建原始对象。为了清楚起见,我将复杂性降低到下面的代码中。现在我的问题是,为什么MemoryView返回相同的地址?此外,为什么即使memoryview是相同的,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.]

我们称之为类型化内存视图并不是一个单一的类:它根据上下文(Cython代码,纯Python代码)在后台更改其标识

那么让我们从

%%cython 
cdef class Temp:
    cdef double[::1] inp
这里的
double[::1]inp
的类型不是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函数,因此必须从
self.inp
(只能调用
id
)创建一个新的临时Python对象(memoryview),然后直接销毁。临时Python对象的创建是通过

知道了这一点,就很容易解释为什么id是相等的:尽管是不同的对象,但临时内存视图巧合地具有相同的地址(因此具有相同的
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
意味着相同的对象是错误的。只有当物体的寿命重叠时,这种假设才成立。

有趣。这就解释了我对你第一个例子的观察。我不知道那个内存实现。是否仍有将这些MemView绑定到cdef类的方法?例如,使用cython.binding指令?再次感谢您的回答!澄清一下,这可能不是一个问题,因为链接和您所说的它们没有时间重叠,但由于这些类将在单独的进程中运行,它们可能有时间重叠。因此,我怀疑memviews(如果它们指向相同的地址)会覆盖数据。这不是故意的。根据我从模拟中得到的结果,情况似乎是这样。我可以将它们转换为适当的Ndarray,但不幸的是,与单核运行时相比,这会将性能降低1/3。@ead(我认为)的意思是,当您调用
id(memview)
时,它会创建一个临时Python对象,仅用于显示“地址”,这就是打印的内容。就数据被更改的内容而言,
id(memview.base)
是最重要的,而且它的行为与您预期的一样。@GlobalTraveler抱歉,但我确实理解您的担忧:即使memviews本身有相同的地址,它们指向不同的缓冲区。对于我缺乏知识(python开发人员正在进行编译),我深表歉意。我有这个用python编写的模拟代码,现在通过正确的类型转换为cdef类。但是,它使用memview快速访问模拟的状态。但是,当我使用MultiProcessing.Pool并行运行这些时,我注意到当在不同的进程中访问缓冲区时,它们都会发生变化,这意味着(a)我的pickle/unpickle方法不能按预期工作,或者(b)缓冲区仍然指向同一个对象。一个可行的替代方案是在每个过程中重建。