单独的Cython类实例具有内部数组

时间:2016-03-28 22:20:26

标签: python numpy cython

我目前正致力于在更大的基于python的模拟中对单个类进行Cython化。感兴趣的类被称为" Bin"并且主要用作数据容器,但也有一些方法。初步测试很有前途,我的类与纯Python版本的行为完全相同,但有一个主要的例外。当多个实例" Bin"存在,每个数据成员都有自己唯一的内存地址,每个Bin实例也是如此,但我尝试过的任何缓冲区类型最终共享一个内存地址BETWEEN Bin实例。

举例说明:

>>> import Bin
>>> x = Bin.Bin("b1")
>>> y = Bin.Bin("b2")
>>> x.assign_job(1,2)
True
>>> x.get_jobs()
array([2], dtype=int64)
>>> y.assign_job(3,4)
True
>>> y.get_jobs()
array([4], dtype=int4)
>>> x.get_jobs()
array([4], dtype=int64)

因此,尽管在不同的实例上调用方法,但是以某种方式复制或共享底层内存。这真的很奇怪,但只有一个真正可能的罪魁祸首:缓冲区共享一个内存地址。为了证实这种情况,我继续说:

>>> id(x) == id(y)
False
>>> id(x.name) == id(y.name)
False
>>> id(x.get_jobs()) == id(y.get_jobs())
True 

这个似乎是python的一个实例,只是重用了这个问题Why do different methods of same object have the same `id`?中描述的内存地址。然而,我来自C背景,因此我不能100%确定python垃圾收集的行为方式。也许一个与我的代码相关的有意义的例子可能有助于说明python实际上只是重用一个内存地址 - 因为我不相信这就是这里发生的事情。

考虑以下附加测试:

>>> x = Bin.Bin()
>>> y = Bin.Bin()
>>> x.assign_job(1,2)
>>> a = x.get_jobs()
>>> a
array([2], dtype=int64)
>>> y.assign_job(3,4)
>>> b = y.get_jobs()
>>> id(a) == id(b)
False
>>> b
array([4], dtype=int64)
>>> a
array([4], dtype=int64)

我的问题是为什么世界会发生这种情况?

我记得在阅读cython文档时,我们不应该能够" cdef" numpy数组,所以也许我的问题只是我在一个未定义行为的领域进行交易,但无论我需要弄清楚这一点,我都可以继续前进。 我已经浏览了google和stackoverflow一天半,发现没有任何描述类似问题的内容。我也尝试过使用具有相同结果的内存视图。

作为一个说明,在这种情况下,我没有与NumPy结婚。我更愿意坚持使用它主要是因为NumPy数组将用于创建Bin实例的python脚本,其次是因为它比手动管理内存要少得多。

Bin类的简明版:

import numpy as np
cimport numpy as np

cdef class Bin:
    """ Container used to hold jobs. Can be seen as a server or data center """

    cdef public int capacity, consumed, reserved, jobCount, jobCapacity
    cdef public str name
    cdef np.ndarray job_ids
    cdef np.ndarray jobs

def __init__(self, str name="", int capacity=1000, int consumed=0, int reserved=0, int jobCount=0, int jobCapacity=40, np.ndarray job_ids=np.full(40, -1, dtype=int), np.ndarray jobs=np.full(40, -1, dtype=int)):
    self.name = name
    self.capacity = capacity
    self.consumed = consumed
    self.reserved = reserved
    self.jobCount = jobCount
    self.jobCapacity = jobCapacity
    self.job_ids = job_ids
    self.jobs = jobs

def __reduce__(self):
    return (self.__class__, (self.name, self.capacity, self.consumed, self.reserved, self.jobCount, self.jobCapacity, self.job_ids, self.jobs))

def assign_job(self, int jobid, int job):
    return self._assign_job(jobid, job)

cdef bint _assign_job(self, int jobid, int job):
    if self.consumed + self.reserved + job <= self.capacity:
        if self.jobCount == self.jobCapacity:
            self.jobs = np.concatenate((self.jobs, np.full(self.jobCapacity, -1, dtype=int)), axis=0)
            self.job_ids = np.concatenate((self.job_ids, np.full(self.jobCapacity, -1, dtype=int)), axis=0)
        self.jobs[self.jobCount] = job
        self.job_ids[self.jobCount] = jobid
        self.jobCount += 1
        self.consumed += job
        return True
    else:
        return False

def get_jobs(self):
    return self._getJobs()

cdef np.ndarray _getJobs(self):
    cdef np.ndarray[np.int_t, ndim=1, mode='c'] yobs
    yobs = self.jobs
    if yobs != None:
            return yobs[:self.jobCount]
    else:
            return yobs

0 个答案:

没有答案