首页 > 解决方案 > 合适的数据类型来保存指向 numpy ndarray 元素的指针

问题描述

对于一个 python 项目,我的任务是结合两个同事的工作并调用他们的函数。要将问题分解为本质,请考虑以下情况:

同事 A 在他的模块“body_class.py”中计算对象的位置和速度值,如下所示:

class Body:
def __init__(self, x, y, z, vx, vy, vz):
    self.x = x
    self.y = y
    self.z = z
    self.vx = vx
    self.vy = vy
    self.vz = vz

对于他编写的其他函数,他希望尽可能保持代码简洁,并用它们各自的名称调用位置和速度向量的分量。出于这个原因,他将它们保存为单独的变量。

同事 B 需要使用所有Body对象的位置和速度值来进行一些时间紧迫的计算,因此他决定使用 numpy ndarrays 来实现更快的矩阵计算。他期望的工作输入是一个 2D numpy ndarray,其中包含所有Body对象的所有位置和速度分量,形式为(三个Body对象的示例):

state = np.array([[2, 0, 3, -2, 0, -3], #Body 1
                 [0, 0, 1, 1, 0, 0],    #Body 2
                 [0, 5, 2, 4, 3, 2]])   #Body 3

有没有办法将使用面向对象的实例变量进行同事 A 的操作的代码清晰度与 numpy 用于同事 B 的计算的效率优势结合起来?我最初的想法是将包含所有对象的位置和速度信息的 ndarray 保存为类Body的类变量(以便同事 B 可以轻松访问此变量“状态”),类似于:

class Body:
states = np.empty([0,6])
def __init__(self, x, y, z, vx, vy, vz):
    self.x = x
    self.y = y
    self.z = z
    self.vx = vx
    self.vy = vy
    self.vz = vz
    Body.states = np.concatenate((self.states, np.array([[self.x, self.y, self.z, self.vx, self.vy, self.vz]])), axis=0)

...然后为每个 Body 对象创建一个实例变量,该变量仅包含指向相应 ndarray 元素的指针/引用(以便同事 A 可以通过例如从类内部调用self.x来访问变量,而无需不断更新这个实例变量和相应的 ndarray 元素),但我不知道这是否可以在 Python 中实现,更不用说这对于这种情况是否是一个很好的解决方案,或者最好的解决方案是否只是一个两种可能性(ndarray VS实例变量)

欢迎任何想法和建议!在此先感谢您的帮助!

更新:

该项目是一个模拟,因此上述两个操作在模拟的每个时间步执行,这使得性能成为问题。所以我正在寻找一种解决方案,它可以将所有 Body 对象的位置和速度值直接存储在一个大的 2D 数组中(这是同事 B 所需的输入),同时仍然可以通过它们的对象实例访问它们名称(例如 b1.x)或其他更有意义的名称,而不必使用数组索引,例如 state[3,2]。

我想防止的是必须首先在它们的实例属性中设置每个 Body 实例的位置和速度值,然后必须通过循环遍历所有 Body 实例并堆叠在每个时间步骤中再次构造大的 2D 数组“状态”他们各自的位置和速度值一起(据我了解,这对性能非常不利,但如果我错了,请纠正我)

标签: pythonnumpynumpy-ndarray

解决方案


听起来像是命名元组的工作:

from collections import namedtuple

Body = namedtuple('Body', ['x', 'y', 'z', 'vx', 'vy', 'vz'])
b1 = Body(2,0,3,-2,0,3)

# now it is possible to access attributes both by name...
b1.x  # returns 2
# ... and as an array
np.array(b1)  # array([ 2,  0,  3, -2,  0,  3])

UPD:子类化和跟踪对象实例 PoC

_Body = namedtuple('Body', ['x', 'y', 'z', 'vx', 'vy', 'vz'])

class Body(_Body): 
    instances = set() 
 
    def __new__(cls, *args, **kwargs): 
        body = super(Body, cls).__new__(cls, *args, **kwargs) 
        Body.instances.add(body) 
        return body 
         
    def __del__(self): 
        Body.instances.remove(self) 
         
    def xy(self): 
        return self.x + self.y 

    @classmethod 
    def states(cls):
        # will return a 2D numpy array of all Body instances
        return np.array(list(cls.instances))

推荐阅读