Python列表不反映变量更改

27

当我写下这段代码时:

polly = "alive"
palin = ["parrot", polly]
print(palin)
polly = "dead"
print(palin)

我原本以为它会输出以下内容:

"['parrot', 'alive']"
"['parrot', 'dead']"

然而,它并没有。它输出的是:

['parrot', 'alive']
['parrot', 'alive']

如何使其输出前者?


2
请参考以下链接:https://dev59.com/E2bWa4cB1Zd3GeqPYp44 - mgilson
6个回答

58

Python变量持有对的引用。因此,当您定义palin列表时,传递的是由polly引用的值,而不是变量本身。

您可以将值想象成气球,变量是系着气球的线程。 "alive"是一个气球,polly只是连接到该气球的线程,palin列表有一个连接到同一气球的不同线程。在Python中,列表只是一系列编号从0开始的线程。

接下来,您将polly字符串连接到一个新的气球"dead",但列表仍然保留着连接到"alive"气球的旧线程。

您可以通过按索引重新分配列表以引用每个线程来替换列表所持有的"alive"线程。在您的示例中,这是线程1

>>> palin[1] = polly
>>> palin
['parrot', 'dead']

这里我简单地把 palin[1] 线程和 polly 绑在了一起,无论它们绑的是什么。

请注意,Python 中的任何集合,如dictsettuple等,都只是线程的集合。其中一些可以将其线程替换为不同的线程,例如列表和字典,这就是使 Python 中的某些东西“可变”的原因。

另一方面,字符串是不可变的。一旦你定义了一个字符串,比如说:"dead"或者"alive",那么它就是一个气球。你可以用一个线程(变量、列表或其他)将其系住,但是你不能更改其中的字母。你只能将该线程与全新的字符串绑定。

Python 中的大多数事物都可以像气球一样。整数、字符串、列表、函数、实例、类,所有这些都可以被绑定到一个变量上,或者被绑定到一个容器中。

您可能还想阅读Ned Batchelder对Python名称的探讨


13
我喜欢气球的比喻,特别是当它们是用氦气充的,当最后一根绳子被放开时,它们就会飘走... (+1)。 - mgilson
4
这是否意味着垃圾回收策略与天气对应? - DSM
9
@DSM: 正是如此 :-) GC 就像吹走没有系好的气球的风。 :-) - Martijn Pieters
1
@user1404053:那么你需要一个可变的气球。例如,一个用户定义的类,而不是一个字符串。 - Martijn Pieters
我用整数替换了字符串("alive" 变成了 3,"dead" 变成了 4),但即使使用 palin[1] = polly,仍然没有变化。 - Flexo1515
显示剩余7条评论

4
在第二个打印语句之前,将你的新值存储到palin中:
palin = ["parrot", polly]

1
有没有不恢复新值的方法来实现它? - Flexo1515
3
在Python中,字符串是不可变的。因此,不能直接修改polly的值来在其他地方产生更改。polly = "dead"这一行是重新绑定操作,而不是突变操作。 - David Robinson

4
当您将字符串放入列表中时,列表会保存该字符串的副本。无论该字符串最初是变量、字面值、函数调用的结果还是其他内容,当列表看到它时,它只是一个字符串值。稍后更改生成字符串的任何内容都不会影响列表。
如果您想存储对值的引用,并在该值发生更改时注意到更改,通常的机制是使用包含“引用”值的列表。将其应用于您的示例,您将得到一个嵌套的列表。例如:
polly = ["alive"]
palin = ["parrot", polly]
print(palin)
polly[0] = "dead"
print(palin)

2
该列表仅包含值,而不是您所希望的变量引用。但是,您可以将lambda存储在列表中,并使lambda查找您变量的值。
>>> a = 'a'
>>> list = ['a',lambda: a]
>>> list[1]
<function <lambda> at 0x7feff71dc500>
>>> list[1]()
'a'
>>> a = 'b'
>>> list[1]()
'b'

更多解释:这里的 lambda: a 实际上是一个函数,它只返回存储在 a 中的值。这就是为什么你可以使用命令 list[1]();它意味着你调用存储在 list[1] 中的函数。(另外,适当的 gravatar,@Jonatan) - Matthew Adams
非常正确,感谢解释。我还完全忘记了我的头像,哈哈! - Jonatan

1

不行。在Python中,对裸名称的赋值操作总是只重新绑定名称,您无法自定义或监视此操作。

您可以将polly变成可变对象而不是字符串,并改变其值而不是重新绑定名称。以下是一个简单的示例:

>>> polly = ['alive']
>>> items = ['parrot', polly]
>>> items
['parrot', ['alive']]
>>> polly[0] = 'dead'
>>> items
['parrot', ['dead']]

0
其他答案已经很好地解释了正在发生的事情。
这是激励使用对象的(几个)问题之一。例如,一个人可能会这样做:
class Animal:
    def __init__(self, aniType, name):
        self.aniType = aniType
        self.name = name
        self.isAlive = True

    def kill(self):
        self.isAlive = False

    def getName(self):
        return self.name

    def getType(self):
        return self.aniType

    def isLiving(self):
        return self.isAlive


polly = Animal("parrot", "polly")

print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())

polly.kill()

print(polly.getName()+' the '+polly.getType()+' is alive?')
print(polly.isLiving())

对于一个简单的任务来说,一开始可能会觉得代码量很大,但是对象通常是处理这种事情的方式,因为它们有助于保持所有内容的组织。

以下是该程序的输出:

polly the parrot is alive?
True
polly the parrot is alive?
False

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接