代码之家  ›  专栏  ›  技术社区  ›  cvanelteren

cython中MemoryView的地址相同,但指向不同的对象

  •  1
  • cvanelteren  · 技术社区  · 7 年前

    问题

    在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.]
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   ead    7 年前

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

    那么让我们从

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

    在这里 double[::1] inp 是一种 __Pyx_memviewslice

    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对象(memoryview) self.inp (只能打电话 身份证件 )然后直接销毁。临时Python对象的创建是通过 __pyx_memoryview_fromslice .

    知道了这一点,就很容易解释为什么ID是相等的:尽管是不同的对象,但临时内存视图碰巧有相同的地址(因此也是相同的) 身份证件

    Python中到处都有类似的场景,下面是 an example for method-objects ,甚至是更简单的:

    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
    

    简而言之,你的期望是一样的 身份证件 意味着同一个对象是错误的。只有当对象的生命周期重叠时,此假设才成立。

    推荐文章