类方法引用类实例但不会通过self导致circ依赖,以及如何对抗它?

问题描述 投票:-1回答:1

如果B类是A的子代,下面的代码会导致循环依赖吗? (为简单起见,我在A,B和A(),B()之间没有区别)

def random_function(B):
    # get parent
    par = B.A

    do_stuff(par, B)   



class A:

    def method(self):
        # get B
        b = A.B
        random_function(b)
        # random_function will reference A now, but not via self, is this a problem?

我知道,这很容易解决

def random_function(A,B):
    do_stuff(A, B)   



class A:

    def method(self):
        # get B
        b = A.B
        random_function(self, b)

...但我想知道我是否仍然可以只用一个参数保留签名。

如果这太抽象了,这就是我要问的原因:在Blender中,有一个套接字类和一个节点类,其中套接字是节点的子节点。我有一个节点的子类和一个如下函数:

def func(socket):
    # get parent
    node = socket.node
    do_stuff(node, socket)

class sub_class(Node):

    def method(self):
        func(self.Socket)

有没有办法让函数签名只有一个参数,而不是在这里遇到循环依赖?

编辑:提供一些实际代码和上下文的编辑,遵循评论中的建议

我将从类的描述开始,我认为这些类正在引起循环引用。

ShaderNodeCustomGroup代表一个节点 - 来自可视化编程的东西。它有套接字,它正在监听这些套接字上发生的事情。用户将输入连接到输入插座,可以从输出插座获得输出。基本上是非编程用户的功能的可视化表示。虚幻也有这个,但我的是来自Blender。

上面已经提到过类Socket - 这是可以输入的内容以及输出的内容。

我不知道,它们究竟是如何交互的 - 这是在源代码中定义的,我只是使用API​​但是如果我执行以下操作,我会以某种方式崩溃整个应用程序:

导致崩溃的功能:

def get_socket_neighbor(socket, offset):
    """
        get the neighbor of this socket, whose position is specified by the offset
        RETURNS: a socket or OUT_OF_RANGE
        NOTE: offset is not for the Blender socket order, it's acual movement by 'offset' socket up or down in world coordinates
    """
    sock_ind = get_socket_index(socket, socket.node)
    desired_position = sock_ind - offset # <--- the actual position in 'blender coordinates'
    if desired_position > (len(socket.node.inputs)-1) or desired_position < 0:
        return OUT_OF_RANGE
    else: 
        return socket.node.inputs[desired_position]

对于节点中的套接字,这一个查找其邻居:例如,一个位置向上的套接字,或一个向下的位置(参见插图中的节点和套接字,套接字是小圆圈)。它做什么,它需要一个套接字,获取它所属的节点,推断出套接字的位置(sock_ind = get_socket_index(socket, socket.node)),并返回一个邻居套接字。

现在,我构造了ShaderNodeCustomGroup的子类(在下面的SUBCLASS中)。这个子类可以生成它的UI并具有套接字并完成节点可以执行的所有操作。它只是有几个我可以定义的附加功能。从这个类的方法,调用上面的函数。 SUBCLASS将其中一个套接字作为此函数的输入。然后该函数获取套接字的节点(这只是SUBCLASS本身)。这会使应用程序崩溃。

class LayerStackNode(bpy.types.ShaderNodeCustomGroup):
            bl_label = "Layer Stack"

       .
       .
       .

        def insert_link(self, link):
    # runs on the insert of a link,
    # this is a preferred method when
    # it gets repaired by blender devs

        # check if the user is connecting mask
        if link.to_socket.name == "mask_layer":
            # simply draw a link from this socket to its mixer
            my_index = mf.get_socket_index(link.to_socket, self)
            relevant_layer = mf.get_socket_neighbor(link.to_socket, 1)
            relevant_mixer = mf.get_node_by_type(self.node_tree.nodes, 'GROUP_INPUT').outputs[my_index - 1].links[0].to_node
            self.node_tree.links.new(relevant_mixer.inputs[0], mf.get_node_by_type(self.node_tree.nodes, 'GROUP_INPUT').outputs[my_index])
            return

        def is_insert_input(self, link): 
        # determines whether the iserted link is input
            if link.to_socket.name == 'layer':
                return True
            else:
                return False

        if not is_insert_input(self, link):
            return  

        # if trying to insert on top of an empty layer, cancel and return
        if mf.get_socket_neighbor(link.to_socket, -1) != mf.OUT_OF_RANGE and \
            mf.get_socket_neighbor(link.to_socket, -1).is_NULL_layer:
            # TODO: find a way to undo the operation
            return

        if self.is_NULL_layer(link.to_socket): # proceed only if it's empty
            if not mf.get_socket_index(link.to_socket, self) == (len(self.inputs)-1): # <--- private case for the lowest layer in the stack
                if not self.is_NULL_layer(mf.get_socket_neighbor(link.to_socket, -1)):
                    self.initialize_layer(link.to_socket)
                else:
                    print("CANNOT MIX ONTO NULL LAYER")
            else:
                # when base layer, create a single link from input to output
                sock_index = mf.get_socket_index(link.to_socket, self)
                out = mf.get_node_by_type(self.node_tree.nodes, 'GROUP_INPUT').outputs[sock_index]
                inp = mf.get_node_by_type(self.node_tree.nodes, 'GROUP_OUTPUT').inputs[0]
                self.node_tree.links.new(inp, out)
        else:
            print("no action needed, the layer is not NULL") 

       .
       .
       .

与你相关的是mf.get_socket_neighbor(link.to_socket, -1).is_NULL_layer。这里节点的类给出一个如果它的套接字到函数,然后它试图找到这个套接字的节点,这是调用类本身。

总而言之,SUBCLASS的方法为一个函数提供了SUBCLASS的套接字,然后该函数试图访问套接字节点,即SUBCLASS本身。如果我重新构建代码,以便函数只需要自己,并且不必访问socket.node,一切正常。

所以,我的问题是:这是循环参考吗?如果是的话,有没有办法克服它?我关心因为这使得使用类不太合理:例如,我无法定义访问其节点的套接字的属性。因为如果我在节点方法中使用此属性,这也会使应用程序崩溃。

python circular-dependency
1个回答
1
投票

这里没有明确显示或暗示的父/子关系。 B.A和A.B不建议父母/子女关系。他们只是说B包含属性名称'A'而A包含名为'B'的属性。这根本没有任何问题(除了命名约定)。这两个名字彼此无关,也与父母/子女关系无关。在B.A的情况下,'A'与名为'A'的类无关。

关于:

# random_function will reference A now, but not via self, is this a problem?

不,random_function不会引用A. random_function将引用A类的B属性包含的任何内容。

© www.soinside.com 2019 - 2024. All rights reserved.