【问题标题】:Python assigning multiple variables to same value? list behaviorPython将多个变量分配给相同的值?列出行为
【发布时间】:2013-04-27 06:16:04
【问题描述】:

我尝试使用如下所示的多重赋值来初始化变量,但我对这种行为感到困惑,我希望单独重新分配值列表,我的意思是 b[0] 和 c[0] 像以前一样等于 0。

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

结果是: [1, 3, 5] [1, 3, 5] [1, 3, 5]

正确吗?我应该使用什么进行多重分配? 和这个有什么不同?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

结果: ('f:', 3) ('e:', 4)

【问题讨论】:

  • 你想让abc,都指向同一个值(在这种情况下是一个列表),还是你想要a=0b=3、和c=5。在这种情况下,您需要a,b,c = [0,3,5] 或只是a,b,c = 0,3,5

标签: python list


【解决方案1】:

如果您是从 C/Java/等语言开始使用 Python。家人,它可能会帮助您停止将a 视为“变量”,而开始将其视为“名称”。

abc 不是具有相同值的不同变量;它们是相同值的不同名称。变量有类型、身份、地址和各种类似的东西。

名字没有这些。 当然可以,而且你可以有很多相同的值的名称。

如果你给 Notorious B.I.G. 一个热狗,* Biggie SmallsChris Wallace 有一个热狗。如果将a 的第一个元素更改为1,则bc 的第一个元素为1。

如果您想知道两个名称是否命名同一个对象,请使用is 运算符:

>>> a=b=c=[0,3,5]
>>> a is b
True

然后你问:

与此有何不同?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

在这里,您将名称 e 重新绑定到值 4。这不会以任何方式影响名称df

在之前的版本中,您分配给a[0],而不是a。因此,从 a[0] 的角度来看,您正在重新绑定 a[0],但从 a 的角度来看,您正在就地更改它。

您可以使用id 函数,它为您提供一个代表对象身份的唯一数字,即使is 无能为力,也可以准确查看哪个对象:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

请注意,a[0] 已从 4297261120 更改为 4297261216——它现在是不同值的名称。 b[0] 现在也是相同新值的名称。那是因为ab 仍在命名同一个对象。


在幕后,a[0]=1 实际上是在调用列表对象的方法。 (等同于a.__setitem__(0, 1)。)所以,它真的根本没有重新绑定任何东西。这就像打电话给my_object.set_something(1)。当然,对象可能正在重新绑定实例属性以实现此方法,但这不是重要的;重要的是你没有分配任何东西,你只是在改变对象。 a[0]=1也是一样。


user570826 询问:

如果我们有,a = b = c = 10

这和a = b = c = [1, 2, 3]的情况完全一样:你有三个名字对应同一个值。

但在这种情况下,值是int,而ints 是不可变的。在任何一种情况下,您都可以将 a 重新绑定到不同的值(例如,a = "Now I'm a string!"),但不会影响原始值,bc 仍将是其名称。不同之处在于,使用列表,您可以将值 [1, 2, 3] 更改为 [1, 2, 3, 4],例如 a.append(4);因为这实际上改变了bc 的名称,所以b 现在将变为[1, 2, 3, 4]。无法将值 10 更改为其他值。 10 永远是 10,就像吸血鬼克劳迪娅永远是 5(至少在她被 Kirsten Dunst 取代之前)。


* 警告:不要给臭名昭著的 B.I.G.一个热狗。黑帮说唱僵尸不应该在午夜之后喂食。

【讨论】:

  • 如果我们have, a = b = c = 10; 并且当我们尝试更新 b 的值时,它会影响其他的吗?虽然我检查了他们的 id 是一样的。?
  • @user570826: 10 是不可变的——这意味着无法更新值,所以你的问题没有意义。您可以将b 指向不同的值,但这样做对ac 没有影响,它们仍指向原始值。列表的不同之处在于它们是可变的——例如,您可以将append 更改为列表,或lst[0] = 3,这将更新值,该值将通过该值的所有名称可见。
【解决方案2】:

咳咳

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 

【讨论】:

  • 恕我直言,这实际上回答了 OP 的第一个关键问题,即我应该使用什么来进行多重分配,而上面评分更高、更大脑的答案却没有。
  • a,b,c = 1,2,3 不带括号适用于 Python 2 或 3,如果你真的想要额外厘米的可读性。
【解决方案3】:

在 python 中,一切都是对象,也是“简单”的变量类型(int、float 等)。

当你改变一个变量值时,你实际上改变了它的指针,如果你在两个变量之间进行比较,它就会比较它们的指针。 (需要明确的是,指针是存储变量的物理计算机内存中的地址)。

因此,当您更改内部变量值时,您会更改它在内存中的值,并且会影响指向该地址的所有变量。

对于您的示例,当您这样做时:

a = b =  5 

这意味着 a 和 b 指向内存中包含值 5 的相同地址,但是当你这样做时:

a = 6

不影响 b,因为 a 现在指向另一个包含 6 的内存位置,而 b 仍然指向包含 5 的内存地址。

但是,当你这样做时:

a = b = [1,2,3]

a 和 b 再次指向同一个位置,但不同之处在于,如果您更改列表值之一:

a[0] = 2

它改变了a所指向的内存的值,但a仍然指向与b相同的地址,结果b也改变了。

【讨论】:

  • 这是高度误导。指针在 Python 级别肯定是不可见的,四个主要实现中至少有两个(PyPy 和 Jython)甚至在实现内部也不使用它们。
  • 欢迎您阅读和探索python内部,您会发现python中的每个变量实际上都是指针。
  • 没有。在 Python (CPython) 的一个实现中,每个变量都是指向 PyObject 的指针。在 PyPy 或 Jython 等其他实现中并非如此。 (事实上​​,甚至不清楚它怎么可能是真的,因为编写这些实现的语言甚至没有指针。)
  • 我认为在概念意义上使用“指针”是可以的(也许带有实现可能会有所不同的免责声明),尤其是如果目标是传达行为。
  • @abarnert 当人们说 Python 时,他们指的是 CPython,而不是其他很少使用的实现。就像人们说面巾纸时,他们指的是面巾纸。在这些 cmets 中玩语义游戏真的没有必要。至于他写的,他所描述的行为是错误的吗?
【解决方案4】:

是的,这是预期的行为。 a、b 和 c 都设置为同一列表的标签。如果您想要三个不同的列表,则需要单独分配它们。您可以重复显式列表,也可以使用多种方法之一来复制列表:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

Python 中的赋值语句不会复制对象 - 它们将名称绑定到对象,并且对象可以具有您设置的任意数量的标签。在您的第一次编辑中,更改 a[0],您正在更新 a、b 和 c 都引用的单个列表的一个元素。在第二次更改 e 时,您将 e 切换为不同对象的标签(4 而不是 3)。

【讨论】:

    【解决方案5】:

    您可以使用id(name) 来检查两个名称是否代表同一个对象:

    >>> a = b = c = [0, 3, 5]
    >>> print(id(a), id(b), id(c))
    46268488 46268488 46268488
    

    列表是可变的;这意味着您可以在不创建新对象的情况下就地更改值。但是,这取决于您如何更改值:

    >>> a[0] = 1
    >>> print(id(a), id(b), id(c))
    46268488 46268488 46268488
    >>> print(a, b, c)
    [1, 3, 5] [1, 3, 5] [1, 3, 5]
    

    如果你给a分配一个新列表,那么它的id会改变,所以不会影响bc的值:

    >>> a = [1, 8, 5]
    >>> print(id(a), id(b), id(c))
    139423880 46268488 46268488
    >>> print(a, b, c)
    [1, 8, 5] [1, 3, 5] [1, 3, 5]
    

    整数是不可变的,所以你不能在不创建新对象的情况下更改值:

    >>> x = y = z = 1
    >>> print(id(x), id(y), id(z))
    507081216 507081216 507081216
    >>> x = 2
    >>> print(id(x), id(y), id(z))
    507081248 507081216 507081216
    >>> print(x, y, z)
    2 1 1
    

    【讨论】:

    • id 不一定是内存位置。正如the docs 所说,这将返回“身份……一个整数……保证在其生命周期内对于该对象是唯一且恒定的。” CPython 恰好将内存地址用作id,但其他 Python 实现可能不会。例如,PyPy 就没有。并且说“两个变量指向同一个内存位置”对于任何理解它的 C 风格的人来说都是一种误导。 “同一个对象的两个名称”更准确,误导性更小。
    • @abarnert 感谢您的澄清,我更新了答案。
    【解决方案6】:

    在你的第一个例子中a = b = c = [1, 2, 3] 你真的是在说:

     'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]
    

    如果你想设置'a'等于1,'b'等于'2'和'c'等于3,试试这个:

    a, b, c = [1, 2, 3]
    
    print(a)
    --> 1
    print(b)
    --> 2
    print(c)
    --> 3
    

    希望这会有所帮助!

    【讨论】:

      【解决方案7】:

      简单地说,在第一种情况下,您将多个名称分配给list。在内存中只创建了一份 list 副本,所有名称都指向该位置。因此,使用任何名称更改列表实际上会修改内存中的列表。

      在第二种情况下,相同值的多个副本在内存中创建。所以每个副本都是相互独立的。

      【讨论】:

        【解决方案8】:

        你需要的是这个:

        a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
        a = 1             # `a` did equal 0, not [0,3,5]
        print(a)
        print(b)
        print(c)
        

        【讨论】:

          【解决方案9】:

          我需要的代码可能是这样的:

          # test
          
          aux=[[0 for n in range(3)] for i in range(4)]
          print('aux:',aux)
          
          # initialization
          
          a,b,c,d=[[0 for n in range(3)] for i in range(4)]
          
          # changing values
          
          a[0]=1
          d[2]=5
          print('a:',a)
          print('b:',b)
          print('c:',c)
          print('d:',d)
          

          结果:

          ('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
          ('a:', [1, 0, 0])
          ('b:', [0, 0, 0])
          ('c:', [0, 0, 0])
          ('d:', [0, 0, 5])
          

          【讨论】:

            【解决方案10】:

            行为是正确的。但是,所有变量都将共享相同的引用。请注意以下行为:

            >>> a = b = c = [0,1,2]
            >>> a
            [0, 1, 2]
            >>> b
            [0, 1, 2]
            >>> c 
            [0, 1, 2]
            >>> a[0]=1000
            >>> a
            [1000, 1, 2]
            >>> b
            [1000, 1, 2]
            >>> c
            [1000, 1, 2]
            

            所以,是的,如果您在单独的一行中以不同的方式分配 a、b 和 c,那么改变一个不会改变其他的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-06-03
              • 2017-09-16
              • 1970-01-01
              • 2019-01-27
              • 1970-01-01
              • 2012-07-23
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多