首页 > 解决方案 > 元组赋值什么时候表现得像所涉及的变量的“交换”,什么时候表现得像“单独的顺序赋值”?

问题描述

下面,我用三个例子来说明“交换”和“赋值语句的顺序执行”的含义。


示例 1(交换)

元组赋值可以非常方便地交换变量的内容。

下面的例子展示了我们如何在不需要临时变量的情况下以清晰简洁的方式交换数组中两个元素的内容:

a = [1,2]

# prints [1, 2]
print(a)

a[0], a[1] = a[1], a[0]

# prints: [2,1] and not: [2, 2] as with a sequential assignment!
print(a)

该示例向我们展示了元组赋值如何表现得像交换,而不是顺序执行第一个赋值,然后进行第三个赋值。


示例 2(交换)

这是另一个交换三个整数的示例:

x, y, z = 1, 2, 3

# prints: 1 2 3
print(x, y, z)

# swap contents in variables:
x, y, z = z, y, x

# prints: 3 2 1 and not: 3 2 3 as with a sequential assignment!
print(x, y, z)

示例 3(顺序赋值语句)

然而,一旦事情变得比简单数据类型更复杂,元组赋值也可能表现得像顺序赋值。

让我们考虑以下链表实现:

class ListNode:

    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

    def __repr__(self):
        is_first = True
        current = self
        l = []
        safety_count = 0
        while current and safety_count < 10:
            if not is_first:
                l.append(' -> ')
            is_first = False
            l.append(str(current.data))
            current = current.next
            safety_count += 1
        return ''.join(l)

    def __str__(self):
        return self.__repr__()

这个函数颠倒了链表的顺序(并且工作得很好):

def reverse_list_working(L):
    if not L:
        return L
    pre_current = None
    current = L
    while True:
        tmp_next = current.next
        current.next = pre_current
        pre_current = current
        current = tmp_next
        if not current:
            break
    return pre_current

现在,我们可能会想通过元组赋值来摆脱 tmp_ 变量,以交换变量的内容:

def reverse_list_not_working(L):
    pre_current = None
    current = L
    while True:
        pre_current, current, current.next = current, current.next, pre_current
        if not current:
            break
    return pre_current

但是,一旦我们到达最后一个元素,这种实现就会出错。这里的问题是元组赋值的行为类似于顺序赋值。

  1. 分配:pre_current -> 当前
  2. 分配:current -> current.next(在列表末尾是 None )
  3. assign: current.next -> pre_current : 产生错误,因为 current 是 None!

标签: python

解决方案


这是另一个用伪代码总结元组分配如何工作的答案:

以下元组赋值:

a, b, c = e1, e2, e3

被翻译成:

e1_ = eval(e1)
e2_ = eval(e2)
e3_ = eval(e3)

a = e1_
b = e2_
c = e3_

因此,请注意,如果更改访问的某些共享状态,e1则表达式的评估可能会对表达式的评估产生影响。e2e1e2

类似地,分配 toa可能会影响b, if的分配b取决于 a (例如, a = c, b = c.next)。

因此,元组分配不仅仅是“交换”。


例子:

班级编号:

def __init__(self, val):
    self.val = val

def __add__(self, other):
    return Num(self.val + other)

def __str__(self):
    return str(self.val)

类计数器:

def __init__(self):
    self.val = Num(1)

def get_and_increment(self):
    return_val = self.val
    self.val += 1
    return return_val

c = 计数器()

a = Num(1) a.val = Num(2) b = a

a.val, b.val = c.get_and_increment(), c.get_and_increment()

print(str(a)) # -> 打印 2 print(str(b)) # -> 打印 2


推荐阅读