如何在Python中实现具有节点特定计算的图?

发布于 2025-01-10 10:00:23 字数 2543 浏览 0 评论 0原文

我正在尝试实现类似于树的东西。有基本节点(数据输入)和计算节点(这些节点可以同时基于基本节点和计算节点)。 由于一个节点可以有多个父节点,因此它不再是一棵树。

基本上,计算节点的值是其子节点的总和或乘积。但它也可以是更复杂的计算(根、幂等以及各种组合)。 问题一:对于特殊计算,必须在每种情况下调整类的代码,并且必须调整计算函数。

我尝试了以下方法:

import numpy as np

class Node:
    def __init__(self, node_id, calc = 'sum'):
        
        self.id = node_id
        self.calc = calc
        self.children = set([])
        self.val = np.nan
        
    def add_child(self, c_id): self.children.add(c_id)
    def get_val(self): return self.val
    def set_val(self, value): self.val = value
    
    def calc_node(self):
        if bool(self.children):
            if self.calc == 'sum':
                val = 0
                for child in self.children:
                    val += child.get_val()
            elif self.calc == 'prod':
                val = 1
                for child in self.children:
                    val *= child.get_val()
            elif self.calc == 'calc_3':
                val = all_nodes['raw_1'].get_val() * \
                            all_nodes['raw_3'].get_val() + \
                            all_nodes['calc_1'].get_val()
            self.val = val

all_nodes = {'raw_1': Node('raw_1'),'raw_2': Node('raw_2'),
            'raw_3': Node('raw_3'),
            'calc_1': Node('calc_1', 'prod'),'calc_2': Node('calc_2'),
            'calc_3': Node('calc_3','calc_3')}

all_nodes['raw_1'].set_val(10.0)
all_nodes['raw_2'].set_val(5.0)
all_nodes['raw_3'].set_val(2.5)

all_nodes['calc_1'].add_child(all_nodes['raw_1'])
all_nodes['calc_1'].add_child(all_nodes['raw_2'])
all_nodes['calc_1'].calc_node()

all_nodes['calc_2'].add_child(all_nodes['raw_1'])
all_nodes['calc_2'].add_child(all_nodes['raw_2'])
all_nodes['calc_2'].calc_node()

#children of calc_3 are only relevant to determine children
all_nodes['calc_3'].add_child(all_nodes['raw_3'])
all_nodes['calc_3'].add_child(all_nodes['calc_1'])
all_nodes['calc_3'].add_child(all_nodes['calc_2'])
all_nodes['calc_3'].calc_node()

print(all_nodes['calc_1'].get_val())
print(all_nodes['calc_2'].get_val())
print(all_nodes['calc_3'].get_val())

问题二:该类使用全局变量all_nodes。这可以通过将 self.children 定义为有序集来解决。或者有更好的解决方案吗?

第一个问题可以通过简单地合并基本算术运算(包括幂、根等)并添加一个额外的节点(计算 calc_2 中使用的乘积的中间节点)来解决。但如果计算复杂,这很快就会变得令人困惑。这里还有其他的可能性吗?或者使用字符串作为 self.calc 并使用 eval() 来计算值是否有意义?我认为这不是一个好的编码风格,如果有大量节点,可能会很慢。

从角度来看,我想将计算集成到 GUI(Tkinter?PyQt5?)中,我可以自己定义节点,而无需自定义代码。如果您说这没有意义,而仅代码解决方案更有意义,那么我将重点关注仅代码解决方案。

由于我是面向对象编程的新手,因此我将不胜感激任何建议或类似的示例。非常感谢!

I am trying to implement something similar to a tree. There are base nodes (data input) and calculated nodes (these can be based on both base nodes and calculated nodes).
As a node can have multiple parents, it's not a tree anymore.

Basically, the value of a computed node is the sum or product of its children. But it can also be a much more complex calculation (root, power, etc. and various combinations).
Problem one: for special computations the code of the class must be adapted in each case and the computation function must be adapted.

I had tried the following:

import numpy as np

class Node:
    def __init__(self, node_id, calc = 'sum'):
        
        self.id = node_id
        self.calc = calc
        self.children = set([])
        self.val = np.nan
        
    def add_child(self, c_id): self.children.add(c_id)
    def get_val(self): return self.val
    def set_val(self, value): self.val = value
    
    def calc_node(self):
        if bool(self.children):
            if self.calc == 'sum':
                val = 0
                for child in self.children:
                    val += child.get_val()
            elif self.calc == 'prod':
                val = 1
                for child in self.children:
                    val *= child.get_val()
            elif self.calc == 'calc_3':
                val = all_nodes['raw_1'].get_val() * \
                            all_nodes['raw_3'].get_val() + \
                            all_nodes['calc_1'].get_val()
            self.val = val

all_nodes = {'raw_1': Node('raw_1'),'raw_2': Node('raw_2'),
            'raw_3': Node('raw_3'),
            'calc_1': Node('calc_1', 'prod'),'calc_2': Node('calc_2'),
            'calc_3': Node('calc_3','calc_3')}

all_nodes['raw_1'].set_val(10.0)
all_nodes['raw_2'].set_val(5.0)
all_nodes['raw_3'].set_val(2.5)

all_nodes['calc_1'].add_child(all_nodes['raw_1'])
all_nodes['calc_1'].add_child(all_nodes['raw_2'])
all_nodes['calc_1'].calc_node()

all_nodes['calc_2'].add_child(all_nodes['raw_1'])
all_nodes['calc_2'].add_child(all_nodes['raw_2'])
all_nodes['calc_2'].calc_node()

#children of calc_3 are only relevant to determine children
all_nodes['calc_3'].add_child(all_nodes['raw_3'])
all_nodes['calc_3'].add_child(all_nodes['calc_1'])
all_nodes['calc_3'].add_child(all_nodes['calc_2'])
all_nodes['calc_3'].calc_node()

print(all_nodes['calc_1'].get_val())
print(all_nodes['calc_2'].get_val())
print(all_nodes['calc_3'].get_val())

Problem two: the class uses the global variable all_nodes. This could be solved by defining self.children as an ordered set. Or are there better solutions?

The first problem can be solved by simply incorporating the basic arithmetic operations including power, root, etc. and adding an extra node (intermediate node which calculates the product used in calc_2). But with a complex calculation, this can quickly become confusing. Are there other possibilities here? Or does it make sense to use a string as self.calc and use eval() to calculate the value? I assume this is not a good coding style and might be slow if there are huge numbers of nodes.

In perspective, I want to integrate the calculation into a GUI (Tkinter? PyQt5?) where I can define nodes myself without having to customize the code. If you say this doesn't make sense and a code-only solution makes more sense, then I'll focus on the code-only solution.

Since I am new to object-orientated programming, I would appreciate any advice or similar examples. Thx a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嘿看小鸭子会跑 2025-01-17 10:00:23

问题 1 可以通过将函数作为参数传递给构造函数来解决。由于 python 中的所有内容都是对象,因此函数可以传递到对象中并设置为属性,而不会出现问题。

问题2,这实际上取决于你想如何使用这个类。如果您希望能够通过 ID 在任何点访问任何节点,那么您需要一个字典。如果只需要计算树底部的值,那么您实际上不需要任何系统,您可以将较高的节点留在较低节点的子节点中的某个位置。

另外,我建议使用一个系统,其中调用 get_val 尝试递归地评估其子节点的值,这样您就不需要手动评估每个节点。

import numpy as np

def sum_child(children): return sum([x.get_val() for x in children])

class Node:
   def __init__(self, calc=sum_child):
      self.calc_node = calc
      self.children = set([])

   def get_val(self):
      try :
         return self.val
      except AttributeError:
         return self.calc_node(self.children)

   def add_child(self, child):
      self.children.add(child)

raw_1 = Node(lambda self: 10)
raw_2 = Node(lambda self: 5)
raw_3 = Node(lambda self: 2.5)

def product(children):
   val = 1
   for child in children :
      val *= child.get_val()
   return val
      
calc_1 = Node(product)
calc_2 = Node()

calc_1.add_child(raw_1)
calc_1.add_child(raw_2)

calc_2.add_child(raw_1)
calc_2.add_child(raw_2)

print(calc_1.get_val())
print(calc_2.get_val())

这是我对这个系统的尝试。我不太确定你想用 calc_3 做什么,因为它实际上并不是基于子节点并且总是解析为一个常量,所以我把它排除在外。

Problem 1 can be solved via passing a function as an argument to the constructor. Since everything in python is an object, functions can passed into an object and set as an attribute without issue.

Problem 2, It really depends on how you want to use the class. If you want to be able to access any node at any point from the ID, then you would want a dictionary. If only need to calculate the value of the bottom of the tree then you don't need any system really and you can just leave the higher nodes somewhere in the children of the lower nodes.

Also I would recommend using a system where calling get_val attempts to evaluate the value of its children recursively so that you don't need to manually evaluate each Node.

import numpy as np

def sum_child(children): return sum([x.get_val() for x in children])

class Node:
   def __init__(self, calc=sum_child):
      self.calc_node = calc
      self.children = set([])

   def get_val(self):
      try :
         return self.val
      except AttributeError:
         return self.calc_node(self.children)

   def add_child(self, child):
      self.children.add(child)

raw_1 = Node(lambda self: 10)
raw_2 = Node(lambda self: 5)
raw_3 = Node(lambda self: 2.5)

def product(children):
   val = 1
   for child in children :
      val *= child.get_val()
   return val
      
calc_1 = Node(product)
calc_2 = Node()

calc_1.add_child(raw_1)
calc_1.add_child(raw_2)

calc_2.add_child(raw_1)
calc_2.add_child(raw_2)

print(calc_1.get_val())
print(calc_2.get_val())

Here is my attempt at this system. I wasn't really sure what you were trying to do with calc_3 since it wasn't actually based on the child nodes and always resolved to a constant so I just left it out.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文