Python 设计复杂的数据结构';依赖

Python 设计复杂的数据结构';依赖,python,design-patterns,Python,Design Patterns,我正在设计一个有限元库。对于给定的问题,使用的有限元网格可以有不同尺寸的元素(例如四面体和三角形),也可以组合相同尺寸的不同元素(例如四面体和六面体)。因此,我需要一个存储有限元信息的数据结构。最基本的信息是元素的连接性(定义元素的节点ID)。例如,我需要以某种方式存储三角形元素4连接到节点5、6和10 我的第一次尝试是创建一个索引为维度(0、1、2或3)并存储字典的列表。这些字典有字符串键(标识符),值是numpy数组(每行表示一个元素连接)。我需要这样做,因为给定维度的numpy数组根据字符

我正在设计一个有限元库。对于给定的问题,使用的有限元网格可以有不同尺寸的元素(例如四面体和三角形),也可以组合相同尺寸的不同元素(例如四面体和六面体)。因此,我需要一个存储有限元信息的数据结构。最基本的信息是元素的连接性(定义元素的节点ID)。例如,我需要以某种方式存储三角形元素4连接到节点5、6和10

我的第一次尝试是创建一个索引为维度(0、1、2或3)并存储字典的列表。这些字典有字符串键(标识符),值是numpy数组(每行表示一个元素连接)。我需要这样做,因为给定维度的numpy数组根据字符串标识符具有不同的形状

这是一节课:

import os
from collections import OrderedDict
import numpy.ma as ma

flatten = lambda l: [item for sublist in l for item in sublist]

class ElementData(list):

    def __init__(self, *args, **kwargs):

        self.reset()
        super(ElementData, self).__init__(*args, **kwargs)

    def __iter__(self):
        for k, v in self[self.idx].items():
            for i, e in enumerate(v):
                yield (k,i,e) if not ma.is_masked(e) else (k,i, None)
        self.reset()


    def __call__(self, idx):
        self.idx = idx-1
        return self

    def __getitem__(self, index):
        if index >= len(self):
            self.expand(index)
        return super(ElementData, self).__getitem__(index)

    def __setitem__(self, index, value):
        if index >= len(self):
            self.expand(index)
        list.__setitem__(self, index, value)

    def __str__(self):
        return "Element dimensions present: {}\n".format([i for i in range(len(self)) if self[i]]) + super(ElementData, self).__str__()

    def keys(self):
        return flatten([list(self[i].keys()) for i in range(len(self))])

    def reset(self):
        self.idx = -1
        self.d = -1

    def expand(self, index):
        self.d = max(index, self.d)
        for i in range(index + 1 - len(self)):
            self.append(OrderedDict())

    def strip(self, value=None):
        if not callable(value):
            saved_value, value = value, lambda k,v: saved_value
        return ElementData([OrderedDict({k:value(k, v) for k,v in i.items()}) for i in super(ElementData, self).__iter__()])


    def numElements(self, d):

        def elementsOfDimension(d):
            # loop over etypes
            nelems = 0
            for v in self[d].values():
                nelems += v.shape[0] if not isinstance(v, ma.MaskedArray) else v.shape[0] - v.mask.any(axis=1).sum()
            return nelems

        # compute the number of all elements
        if d == -1:
            nelems = 0
            for i in range(self.d+1):
                nelems += elementsOfDimension(i)
            return nelems
        else: # of specific dimension only
            return elementsOfDimension(d)
这个类工作得很好,它允许我无缝地遍历特定维度的所有项。但是,还有其他与单独存储的每个元素相关联的数据,例如其材质。因此,我决定使用相同的数据结构来引用其他属性。为此,我使用类的
strip
函数返回不带numpy数组的整个结构

我遇到的问题是,原始数据结构是动态的,如果我更改它,我必须修改依赖它的所有其他结构。我真的认为我在设计这门课时走错了方向。也许有更简单的方法来解决这个问题?我曾考虑将额外的信息存储在numpy数组旁边(例如元组),但我不知道这是否好。在设计软件时所做的选择真的会让我们以后的生活变得悲惨,我现在开始意识到这一点

更新

使用上面的类,一个示例如下:

Element dimensions present: [0, 1, 2]
[OrderedDict([('n1', array([[0],
       [1],
       [3]]))]), OrderedDict([('l2', array([[1, 2]]))]), OrderedDict([('q4', array([[0, 1, 5, 4],
       [5, 1, 2, 6],
       [6, 2, 3, 7],
       [7, 3, 0, 4],
       [4, 5, 6, 7]]))])]
数据结构用于存储0(节点)、1(直线)和2(四边形)维度的元素。

当提到“复杂数据结构的依赖关系”时,您可能会提到“图形”,您是否仔细检查了现有的元素是否符合您的需要?(如“路线问题”)

评论:该设计违背了程序的逻辑结构

我使用了给定的元素数据示例,并没有期望一下子得到全部信息

注释:每个元素都有一个唯一的维度 (三角形的尺寸始终为2, 作为一个四面体,它的维数始终为3 节点维度(0)

对不起,我误解了问题“…不同维度的元素…”的含义 “元素具有不同的维度”。我修改了我的建议

评论:。。。当我修改数据结构时,问题就出现了 (例如,通过添加元素)

只要您不显示此问题的数据示例, 我想不出一个解决办法


我的建议:

class Dimension(object):
    """
    Base class for all Dimensions
    Dimension has no knowledge to what the data belongs to
    """
    def __init__(self, data=None):
        pass

class Element(object):
    """
    Base class for all Elements
    Hold on class Dimension(object):
    """
    def __init__(self, dimension=None):
        pass

class Triangle(Element):
    def __init__(self, dimension):
        super().__init__(dimension=dimension)

class Tetrahedron(Element):
    def __init__(self, dimension):
        super().__init__(dimension=dimension)

class Node(Element):
    def __init__(self, dimension):
        super().__init__(dimension=dimension)
用法:构建示例元素

node = Node(dimension=[0,1,3])
line = Triangle(dimension=[0,2])
quad = Tetrahedron(dimension=[[0, 1, 5, 4], [5, 1, 2, 6], [6, 2, 3, 7], [7, 3, 0, 4], [4, 5, 6, 7]])
新的Future元素,仅显示
类元素
可以扩展:

# New future dimensions - No changes to class Element() required
class FutureNodTriTet(Element):
    def __init__(self, dimension):
        super().__init__(dimension=dimension)

future_NTT = FutureNodTriTet(dimension=[0, (1,3), (4,5,6)])

如果这更接近您的需要,请发表评论。

可能有助于理解您最初为什么设计这样的数据结构。你在解决什么问题?我正在创建一个有限元库。这些numpy数组中的每一行都是元素连接(定义元素的节点ID)。对于给定的问题,使用的有限元网格可以有不同尺寸的元素(例如四面体和三角形),也可以组合相同尺寸的不同元素(适用于四面体和六面体的示例。但第一个数据结构仅处理元素的连接性。每个元素还指定了材质属性(我需要另一个数据结构来存储材质),其他数据也一样。请为您的问题提供一些显示更改的数据,一个适用于给定的
,另一个不适用。我的第一次尝试失败,缺少
ma.MaskedArray
,它来自何处。@stovfl我不理解您的请求。但我发现您的尝试失败可能是因为这个类还可以处理屏蔽数组。所以你必须
将numpy.ma导入为ma
。我将编辑我的问题。请阅读我要求的内容:我非常熟悉图形,这些绝对不是我需要的数据结构。谢谢你的回答。虽然很有趣,但设计与逻辑结构相反每个元素都有一个唯一的维度(三角形总是维度2,四面体总是维度3,节点维度0)。我设计的结构工作得很好。但是,正如我在问题中所说的,当我修改数据结构(例如添加元素)时,会出现问题,因为所有数据都是分散的(材料保存在不同的数据结构中)。我想我需要的是将数据保存在一起。@aaragon:更新了我对你评论的回答