我目前正致力于在更大的基于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