【问题标题】:Python Simple Swap FunctionPython 简单交换函数
【发布时间】:2012-10-31 20:57:22
【问题描述】:

我在尝试学习 python 时遇到了这个问题。考虑以下函数:

def swap0(s1, s2):
    assert type(s1) == list and type(s2) == list
    tmp = s1[:]
    s1 = s2[:]
    s2 = tmp
return

s1 = [1]
s2 = [2]
swap0(s1, s2)
print s1, s2

s1 和 s2 会打印什么?

运行问题后发现print语句会打印1 2。从swap0函数看来s1和s2的值并没有变化。我能想到的唯一解释是因为这条线。

tmp = s1[:]

由于 s1[:] 是一个副本,因此 s1 的值在函数调用中不会改变是有道理的。但是因为swap0的参数是(s1, s2),所以我不确定是否在做tmp = s1[:]之后。任何时候都可以

s1 = something...

它将是对 s1 副本的引用,而不是 s1 本身。有人可以提供更好的解释吗?谢谢。

【问题讨论】:

    标签: python swap


    【解决方案1】:

    这是因为它在swap0 函数中为s1s2 分配了新值。这些分配不会传播到函数之外。如果您只是将函数体复制并粘贴到函数调用的位置,您会发现它是有效的。

    您可以通过修改参数引用的对象而不是参数本身来解决此问题:

    def swap0(s1, s2):
        assert type(s1) == list and type(s2) == list
        tmp = s1[:]
        s1[:] = s2
        s2[:] = tmp
    

    但是,在 Python 中进行交换更简单、更好的方法是:

    s1, s2 = s2, s1
    

    这也只会交换那些对列表的特定引用,而不是列表内容本身。

    【讨论】:

    • 请注意,如果您对列表 s1 和/或 s2 有其他引用,swap0 所做的工作与 s1,s2 = s2,s1 所做的工作可能会有显着差异。跨度>
    【解决方案2】:

    实际上,您的最终print 将打印出s1s2 的原始值。这是因为您只是在函数范围内交换它们。这样做不会影响它们在函数外的值(即在调用函数之后它们的值之后)

    如果它们是可变类型(listsetdict 等),那么您可以在 swap 中就地修改它们。但是,这限制了 swap 仅适用于可变类型。

    因此,您最好以相反的顺序返回输入:

    def swap(s1, s2):
        return s2, s1
    
    s1 = 'a'
    s2 = 'b'
    s1, s2 = swap(s1, s2)
    print s1, s2 # prints 'b a'
    

    当然,您可以在一行中完成所有操作,如下所示:

    s1, s2 = s2, s1
    

    干杯!

    【讨论】:

    • 这种形式的交换可能并不完全是 OP 的目的。在尝试交换之前,请考虑您是否创建了引用 s3 = s1。在这种情况下,s3 现在引用与s2 相同的列表。
    • 好点!我没有想到这一点。 s1, s2 = copy.deepcopy(s2), copy.deepcopy(s1) 会解决这个问题吗?
    • 嗯...不。那也行不通。 s3 指向一个列表。只要您对 RHS 上的两件事都执行deepcopys3 就不再指向与其他两个相同的列表。
    • 在这种情况下,s3 将指向 s1,即 __eq__ 指向交换后的 s2。当然,它指向不同的内存地址,但我认为这是这里的权衡。其他指针要么必须指向 __eq__ (但存储在其他地方),要么也必须更改。我看不到直接的解决方法。你(我的意思不是听起来很有挑战性,我真诚地问......)?
    【解决方案3】:

    其他答案解释了出了什么问题。这是一个可以满足您需求的版本:

    def swap(s1, s2):
        assert isinstance(s1, list) and isinstance(s2, list)
        s1[:], s2[:] = s2[:], s1[:]
    

    另请参阅:isinstance vs. type

    【讨论】:

      【解决方案4】:

      在函数内部,您将 local 变量 s1s2 与右侧的值重新绑定(这也是本地的,因为您使用切片进行复制) .即使您更改了这些局部变量的内容,也不会更改调用范围内列表的内容,因为它们不再引用相同的列表。

      【讨论】:

        【解决方案5】:

        这是一个可以实现目标的单行函数:

        swap = lambda x: (x[1], x[0])
        

        【讨论】:

        • 这根本不是一个解决方案。您认为您的解决方案如何运作?假设我有两个列表:a=[1,2,3,4]b=[5,6,7,8]。我把你的函数称为swap(a, b)吗???你试过了吗?请举例说明它的工作原理!
        • @AGNGazer a, b = swap((a, b))。仅供参考,更好的方法是swap = lambda x, y: (y, x)
        【解决方案6】:

        如果两个列表的长度相同,您也可以通过使用索引和循环的旧交换方法来做到这一点。这是一种老派,但有助于理解索引

        a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
        b = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
        
        for i in range(0, len(a)):
            a[i] = a[i] + b[i]
            b[i] = a[i] - b[i]
            a[i] = a[i] - b[i]
        
        print(a)
        print(b)
        

        这将使输出为:

         [0,9,8,7,6,5,4,3,2,1]
         [1,2,3,4,5,6,7,8,9,0]
        

        或者也可以使用异或来完成。 Xor 运算符是按位运算符,例如在操作数之间进行 Xor 运算。

        a = 5 #0b101
        b = 4 #0b100
        c = a ^ b #0b001
        

        这里 0b101 是 5 的二进制表示,0b100 是 4 的二进制表示,当您对这些进行异或运算时,您将输出为 0b001 即 1 。如果一个且只有一个输入为 1,则异或返回 1 个输出结果。如果两个输入为 0 或两者均为 1,则输出 0 个结果。 我们可以使用 Xor 交换两个变量,例如:

        a = 5        # 0b0101
        b = 9        # 0b1001
        a = a ^ b    # Xor (0b0101, 0b1001) = 0b1100 (12)
        b = a ^ b    # Xor (0b1100, 0b1001) = 0b0101 (5)
        a = a ^ b    # Xor (0b1100, 0b0101) = 0b1001 (9)
        print("a = {} and b = {}".format(a, b))
        

        输出将是a = 9 and b = 5

        同样,我们也可以通过对其中的项目进行异或操作来交换两个列表,例如:

        a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
        b = [ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ] 
        
        for i in range(0, len(a)) :
             a[i] = a[i] ^ b[i] 
             b[i] = a[i] ^ b[i] 
             a[i] = a[i] ^ b[i] 
        
        print(a)
        print(b)
        

        输出:

        [0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
        [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
        

        让我们看另一个场景,如果我们需要交换列表中的项目,例如: 我们有一个像这样x = [ 13, 3, 7, 5, 11, 1 ] 的列表,我们需要像这样交换它的项目x = [ 1, 3, 5, 7 , 11, 13 ] 所以我们可以通过使用两个按位运算符 Xor ^ 和 Compliments ~ 来做到这一点

        代码:

        # List of items 
        a = [ 13, 3, 7, 5, 11, 1 ]
        
        # Calculated the length of list using len() and
        # then calulated the middle index of that list a 
        
        half = len(a) // 2
        
        # Loop from 0 to middle index
        for i in range(0, half) :
        
        # This is to prevent index 1 and index 4 values to get swap 
        # because they are in their right place.
        if (i+1) % 2 is not 0 :
        
            #Here ~i means the compliment of i and ^ is Xor,
            # if i = 0 then ~i will be -1 
            # As we know -ve values index the list from right to left 
            # so a [-1] = 1 
        
            a[i] = a[i] ^ a[~i] 
            a[~i] = a[i] ^ a[~i] 
            a[i] = a[i] ^ a[~i]
        
        print(a)
        

        所以输出将是[1, 3, 5, 7, 11, 13]

        【讨论】:

        • 这段代码没有具体说明“交换”。另外,请修正你的缩进。
        • 你是对的,虽然我不建议这样做,因为错误的积累。
        • 还有一件事:请在print() 语句之后添加一些输出或完全删除它们。
        【解决方案7】:

        你可以有这个:

        def swap(x , y):
          x , y = y , x
          return x , y
        
        x  = 5
        y = 10
        
        print ('x is {0} and y is {1}'.format(x,y))    # x is 5 and y is 10
        x , y = swap(x,y)                              # doing swap 
        print ('x is {0} and y is {1}'.format(x,y))    # x is 10 and y is 5
        

        【讨论】:

          【解决方案8】:

          根本不需要函数。 a,b=b,a 可以解决问题。

              >>> a,b=1,2
              >>> print (a,b)
              (1, 2)
              >>> a,b=b,a
              >>> print (a,b)
              (2, 1)
              >>>
          

          它也适用于数组。 但是,如果您非常想要这里的功能,那就是

              def swap(a,b)
                 return b,a
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-11-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-11-28
            • 2013-11-15
            • 1970-01-01
            • 2013-12-28
            相关资源
            最近更新 更多