Python 元组赋值在什么时候表现得像一个";互换;所涉及的VAR,以及它在什么时候的行为类似于;单个顺序赋值“;?

Python 元组赋值在什么时候表现得像一个";互换;所涉及的VAR,以及它在什么时候的行为类似于;单个顺序赋值“;?,python,Python,下面,我用三个例子说明“交换”和“赋值语句的顺序执行”的含义 示例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) 该示例向我们展示了元

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


示例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__()
现在,我们可能想通过元组赋值来摆脱tmp_uu变量,以交换变量的内容:

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
然而,一旦我们到达最后一个元素,这个实现就会给出一个错误。这里的问题是元组赋值的行为类似于顺序赋值

  • 分配:当前前->当前
  • 分配:当前->当前.next(列表末尾无)
  • 分配:current.next->pre_current:产生错误,因为current是None
    如果您举一个简单的例子(三个整数)并查看字节码,您会看到:

    ...
    LOAD_NAME                2 (z)
    LOAD_NAME                1 (y)
    LOAD_NAME                0 (x)
    ROT_THREE
    ROT_TWO
    STORE_NAME               0 (x)
    STORE_NAME               1 (y)
    STORE_NAME               2 (z)
    ...
    

    也就是说,答案是首先得到右边的值(简单情况下只得到变量引用的值,但在复杂情况下,这可能会产生更广泛的影响),然后,然后从堆栈到左侧相应变量的值。

    下面是另一个答案,用伪代码总结了元组赋值的工作原理:

    以下元组分配:

    a, b, c = e1, e2, e3
    
    翻译成:

    e1_ = eval(e1)
    e2_ = eval(e2)
    e3_ = eval(e3)
    
    a = e1_
    b = e2_
    c = e3_
    
    因此,请注意,如果
    e1
    更改了
    e2
    访问的某些共享状态,则表达式
    e1
    的计算可能会影响表达式
    e2
    的计算

    类似地,如果
    b
    依赖于a(例如,
    a=c
    b=c.next
    ),则对
    a
    的赋值可能会影响
    b
    的赋值

    因此,元组赋值不仅仅是一种“交换”


    例子: 类别编号:

    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_和_increment(),c.get_和_increment()

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

    如果您提前停止一次迭代,如果不是当前迭代,该怎么办。下一步:?@BlackBear可能有效,也可能无效。我试图研究这个问题的原因是,在这种情况下,元组赋值不进行交换,而是顺序执行赋值语句。在任何赋值之前,RHS都会被完全评估,但LHS会从左到右执行,因此,当您到达current.next时,对current的赋值已经发生。@BlackBear另一个例子,如果您将元组赋值更改为:current,pre_current,current.next=current.next,current,pre_current,那么您将进入一个无限循环。因为(next element).next=(next_element)的指针在所有这些情况下都做相同的事情。没有两种行为,没有决定;请参阅示例。感谢您在我的示例中展示堆栈上发生的情况。请参阅我的答案,从编程语言的角度看会发生什么。
    def __init__(self):
        self.val = Num(1)
    
    def get_and_increment(self):
        return_val = self.val
        self.val += 1
        return return_val