代码之家  ›  专栏  ›  技术社区  ›  BeepBoop

链表在传递到函数中时的具体行为是什么?

  •  1
  • BeepBoop  · 技术社区  · 7 年前

    这是我的节点类:

    class Node:
    
     def __init__(self, value):
       self.value = value
       self.next = None
    
     def __repr__(self):
       if self.next == None:
         return 'Node({})'.format(repr(self.value))
       else:
         return 'Node({}, {})'.format(
           repr(self.value), repr(self.next))
    

    这是我的方法m:

    def m(lnk):
       while lnk and lnk.next:
           lnk.next = lnk.next.next
           lnk = lnk.next
       print(lnk)
    

    然后运行以下代码:

    lnk = Node(1)
    lnk.next = Node(2)
    lnk.next.next = Node(3)
    m(lnk)
    print(lnk)
    

    第一个输出是节点(3),第二个输出是节点(1,节点(3))。我理解第一个输出,但第二个输出我没有真正理解。为什么节点(1)仍然存在?我认为lnk会作为引用传入,因此会发生变化,并具有与函数中相同的值。然而,这并没有发生,我不明白为什么。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Scott Mermelstein    7 年前

    你现在的问题是,你的m函数没有做它应该做的。通过删除该行进行更改 lnk.next = lnk.next.next :

    def m(lnk):
       while lnk and lnk.next:
           lnk = lnk.next
       print(lnk)
    

    然后,您将得到以下结果:

    >>> m(lnk)
    Node(3)
    >>> print(lnk)
    Node(1, Node(2, Node(3)))
    

    那么,你在做什么?

    __repr__ 表示如果您是列表的最后一部分,请返回“Node(#)”,其中#是值。如果您不是列表的最后一部分,请返回您下面的节点列表。

    修改后的 m 函数现在只需打印列表中的最后一个节点。

    详细了解您当前的设置以及 m级 函数执行以下操作:

    当前:lnk={'value':1,“next':{'value':2,“next':{'value':3,“next':None}}}}

    第一次循环通过 m级 获取传入的对象,调用 lnk ,并将其更改为lnk={'value':1,“next':{'value':3,“next”:None}(因为lnk.next=lnk.next.next)。在你的第二次循环中 lnk公司 变量已更改。它不再指向同一个对象;它指向对象{'value':3,“next':None},循环停止,因为“next”是None。

    请注意,这修改了您的链接列表,我不认为这是您的意图。

    埃文给你指的参考资料很好。在阅读相关内容时,您将看到函数中的变量名是本地的。它首先引用您创建的节点(1)对象,但当您将其指定给其他对象时,它不再引用该对象。但它不是按值传递的,因为您在该对象内部所做的更改(例如设置lnk.next.next)仍然存在。

    创建初始链表时,需要使用 .next.next 避免为每个节点创建临时变量的语法。您也可以这样构建您的列表:

    node1 = Node(1)
    node2 = Node(2)
    node3 = Node(3)
    
    node1.next = node2
    node2.next = node3
    linked_list = node1
    

    现在,如果你想更新 lnk公司 要指向列表的尾部,需要返回局部变量。 m级 它的用法应该是这样的:

    def m(node):
        while node and node.next:
            node = node.next
        return node
    
    tail = m(lnk)
    

    注意,我重命名了的参数 m级 未来 node 。这将帮助您避免混淆 lnk公司 正在设置或修改。要意识到,如果你不断修改列表,你的结果还是一样的。即使它的名字 节点 ,该行 node.next = node.next.next 仍然会修改名为 lnk公司 在更高的范围内。

    在风格方面,我强烈建议不要命名函数 m级 。给它起个名字,比如 get_tail get_leaf ,因此很清楚函数的作用。至少你的一些困惑来自于对m应该做什么没有一个明确的定义。

    如果你 必须 修改 lnk公司 函数内部的变量(变量本身,而不是内容),可以使用全局变量来完成,但这几乎总是一个坏主意,不应该这样做。(因为人们习惯了变量的行为方式,而改变行为会使代码更难维护。在这种情况下,大多数人希望链表上的操作不会具有破坏性,除非从语义上很明显它们是有意的,例如。 pop .)

    def m():
        global lnk
        while lnk and lnk.next:
            lnk = lnk.next
    
        2
  •  2
  •   Evan    7 年前

    Variables in Python are not passed by reference, but by assignment. 它实际上与在C或C++中传递指针变量是一样的:如果更改函数中可变对象上的字段,则更改将在调用范围中可见,但如果更改变量本身的标识,则不会可见。