简体   繁体   English

是否可以同时让 numpy 数组值和对象属性指向内存中的相同位置?

[英]Is it possible to have both numpy array values and object atribute pointing to the same position in memory?

In a project I am developing I was wondering if it is possible to do something like:在我正在开发的项目中,我想知道是否可以执行以下操作:

class P:
    def __init__(self, x):
        self.x = x

    def __str__(self):
        return str(self.x)

    def __repr__(self):
        return self.__str__()

obj_lst = [P(x=2), P(x=3), P(x=4), P(x=5)]

np_x = array([p.x for p in obj_lst])

obj_lst[0].x = 10
print(np_x)

The expected result would be,预期的结果是,

array([10, 3, 4, 5])

Also,还,

np_x[2] = 20
print(obj_lst)

I would get,我会得到,

[10, 3, 20, 5]

So the both the object's attribute and the values in the array are pointing to the same position in memory.所以对象的属性和数组中的值都指向内存中的相同位置。

This way I could use the OOP abstraction by one side and the numpy speed for the complex algebraic operations.通过这种方式,我可以一方面使用 OOP 抽象,并在复杂的代数运算中使用 numpy 速度。

You can do it if you think a bit outside the box (and possibly tweak your requirements just slightly).如果您在框外思考(并且可能稍微调整您的要求),您可以做到这一点。 Let's reverse the way things are set up and create a buffer that holds the data first:让我们颠倒事物的设置方式并首先创建一个保存数据的缓冲区:

np_x = np.array([2, 3, 4, 5])

Now define your class a bit differently.现在定义你的类有点不同。 Instead of recording the value of x , we will record a pointer to it as an array and index (later you can do some interesting things with raw memory locations, but let's not do that for now).我们不会记录x,而是将指向它的指针记录为数组和索引(稍后您可以使用原始内存位置做一些有趣的事情,但我们现在不要这样做)。 You can keep pretty much the exact same interface by making the x attribute a property in the class, and stashing the data for it in an instance attribute of the same name:通过将x属性设为类中的property ,并将其数据存储在同名的实例属性中,您可以保持几乎完全相同的接口:

class P:
    def __init__(self, buffer, offset):
        self.__dict__['x'] = (buffer, offset)

    @property
    def x(self):
        buf, off = self.__dict__['x']
        return buf[off]

    @x.setter
    def x(self, value):
        buf, off = self.__dict__['x']
        buf[off] = value

    def __str__(self):
        return str(self.x)

    def __repr__(self):
        return self.__str__()

Now you can make the list of objects.现在您可以制作对象列表。 This is the only part of your code that changes outside the class definition:这是您的代码中唯一在类定义之外更改的部分:

obj_lst = [P(np_x, 0), P(np_x, 1), P(np_x, 2), P(np_x, 3)]

All your changes are now mutually transparent because you share a buffer:您的所有更改现在都是相互透明的,因为您共享一个缓冲区:

>>> obj_lst[0].x = 10
>>> np_x
array([10,  3,  4,  5])
>>> np_x[-2] = 20
>>> obj_lst
[10, 3, 20, 5]

The neat thing about this is that P will work with essentially any type that supports __getitem__ and __setitem__ , regardless of how it is indexed.关于这一点的巧妙之处在于P基本上可以与支持__getitem____setitem__任何类型一起使用,而不管它是如何索引的。 For example, you can apply it to a dict :例如,您可以将其应用于dict

>>> d_x = {'a': 2, 'b': 3, 'c': 4, 'd': 5}
>>> obj_lst = [P(d_x, 'a'), P(d_x, 'b'), P(d_x, 'c'), P(d_x, 'd')]
>>> obj_lst[0].x = 10
>>> d_x
{'a': 10, 'b': 3, 'c': 20, 'd': 5}
>>> d_x['c'] = 20
>>> obj_lst
[10, 3, 20, 5]

You can also supply complex indices to numpy arrays:您还可以为 numpy 数组提供复杂的索引:

>>> np_x = np.arange(10)
>>> obj_lst = [P(np_x, 0), P(np_x, slice(1, None, 2)), P(np_x, [1, 2, 6, 8])]
>>> obj_lst
[0, [1 3 5 7 9], [1 2 6 8]]
>>> obj_lst[-1].x = 100
>>> np_x
array([  0, 100, 100,   3,   4,   5, 100,   7, 100,   9])
>>> np_x[5:] = 20
>>> obj_lst
[0, [100   3  20  20  20], [100 100  20  20]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM