numpy对象数组

jon*_*alm 23 python simulation numpy

我正在尝试在Python中实现格子模型(格子boltzmann)的模拟.格子的每个站点都具有许多属性,并根据某些规则与相邻站点交互.我认为创建一个包含所有属性的类并创建该类的实例网格可能很聪明.(由于我对Python缺乏经验,这可能不是一个好主意,所以请随意评论我的方法.)

这是我正在做的一个玩具示例

class site:
    def __init__(self,a,...):
        self.a = a
        .... other properties ...
    def set_a(self, new_a):
        self.a = new_a
Run Code Online (Sandbox Code Playgroud)

现在我想处理这些站点的2D/3D点阵(网格),所以我尝试执行以下操作(这里以2D 3x3网格为例,但在模拟中我需要> 1000x1000X1000的顺序)

lattice = np.empty( (3,3), dtype=object)
lattice[:,:] = site(3)
Run Code Online (Sandbox Code Playgroud)

现在,问题是例如每个格点指的是同一个实例

lattice[0,0].set_a(5)
Run Code Online (Sandbox Code Playgroud)

还会将lattice [0,2] .a的值设置为5.此行为是不需要的.为了避免这个问题,我可以遍历每个网格点并按元素分配对象,比如

for i in range(3):
    for j in range(3):
        lattice[i,j] = site(a)
Run Code Online (Sandbox Code Playgroud)

但是有没有更好的方法(不涉及循环)将对象分配给多维数组?

谢谢

Pau*_*aul 27

您可以向量化类的__init__功能:

import numpy as np

class Site:
    def __init__(self, a):
        self.a = a
    def set_a(self, new_a):
        self.a = new_a

vSite = np.vectorize(Site)

init_arry = np.arange(9).reshape((3,3))

lattice = np.empty((3,3), dtype=object)
lattice[:,:] = vSite(init_arry)
Run Code Online (Sandbox Code Playgroud)

这可能看起来更干净,但与循环解决方案相比没有性能优势.列表理解答案创建一个中间python列表,这将导致性能损失.

  • @Paul:是的,我理解你的观点。我以前从未见过将“向量化”应用于构造函数。这是解决这个问题的一个巧妙的解决方案,但它不是一个通用的解决方案,因为它使得向“Site”的构造函数添加参数变得很麻烦。 (2认同)

Set*_*son 6

缺少的部分是Python将所有内容都视为参考.(有一些"不可变"对象,字符串,数字和元组,更像是值.)当你这样做时

lattice[:,:] = site(3)
Run Code Online (Sandbox Code Playgroud)

你说的是"Python:创建一个新对象site,并告诉每个元素lattice指向该对象." 要查看确实如此,请打印数组以查看对象的内存地址是否完全相同:

array([[<__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>],
       [<__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>],
       [<__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>,
        <__main__.Site object at 0x1029d5610>]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

循环方式是一种正确的方法.使用numpy数组,这可能是您的最佳选择; 使用Python列表,您还可以使用列表推导:

lattice = [ [Site(i + j) for i in range(3)] for j in range(3) ]
Run Code Online (Sandbox Code Playgroud)

您可以将列表理解与numpy.array构造一起使用:

lattice = np.array( [ [Site(i + j) for i in range(3)] for j in range(3) ],
                    dtype=object)
Run Code Online (Sandbox Code Playgroud)

现在当你打印时lattice,它就是

array([[<__main__.Site object at 0x1029d53d0>,
        <__main__.Site object at 0x1029d50d0>,
        <__main__.Site object at 0x1029d5390>],
       [<__main__.Site object at 0x1029d5750>,
        <__main__.Site object at 0x1029d57d0>,
        <__main__.Site object at 0x1029d5990>],
       [<__main__.Site object at 0x1029d59d0>,
        <__main__.Site object at 0x1029d5a10>,
        <__main__.Site object at 0x1029d5a50>]], dtype=object)
Run Code Online (Sandbox Code Playgroud)

所以你可以看到那里的每个物体都是独一无二的.

您还应该注意"setter"和"getter"方法(例如set_a)是非Pythonic.最好直接设置和获取属性,然后使用@property装饰器,如果你真的需要阻止对属性的写访问.

另请注意,使用CamelCase编写Python类是标准的,而不是小写.


DSM*_*DSM 5

我不知道更好,但是作为一组明确的循环的替代方法,您可以编写

lattice = np.empty( (3,3), dtype=object)
lattice.flat = [site(3) for _ in lattice.flat]
Run Code Online (Sandbox Code Playgroud)

无论格子的形状如何,它都可以工作。