为什么Python变量每次使用';重新修改?
只是想知道这背后的逻辑是什么?从表面上看,它似乎有点低效,每次你做一些简单的事情,比如“x=x+1”,它就必须接受一个新地址并丢弃旧地址。因为基本类型是不可变的,所以每次你修改它时,它都需要再次实例化 …这非常好,特别是对于函数而言Python变量(在Python中称为标识符或名称)是对值的引用。为什么Python变量每次使用';重新修改?,python,Python,只是想知道这背后的逻辑是什么?从表面上看,它似乎有点低效,每次你做一些简单的事情,比如“x=x+1”,它就必须接受一个新地址并丢弃旧地址。因为基本类型是不可变的,所以每次你修改它时,它都需要再次实例化 …这非常好,特别是对于函数而言Python变量(在Python中称为标识符或名称)是对值的引用。id()函数为该值而不是名称说了些什么 许多值是不可变的;整数、字符串和浮点数都不会发生变化。将1添加到另一个整数时,将返回一个新的整数,然后替换对旧值的引用 您可以将Python名称视为标签,与值绑定
id()
函数为该值而不是名称说了些什么
许多值是不可变的;整数、字符串和浮点数都不会发生变化。将1
添加到另一个整数时,将返回一个新的整数,然后替换对旧值的引用
您可以将Python名称视为标签,与值绑定。如果将值想象为引出序号,则每次指定给该名称时,都会将标签还原为一个新引出序号。如果气球上再也没有其他标签,它就会随风飘走,再也看不见了。id()
在这里,我将更多地讨论价值观作为气球的概念
这似乎效率低下。对于许多经常使用的小值,Python实际上使用了一个称为interning的过程,在这个过程中,它将缓存这些值的存储以供重用None
是这样的值,小整数和空元组(()
)也是这样的值。您可以使用该函数对希望经常使用的字符串执行相同的操作
但请注意,只有当值的引用计数(“标签”的数量)降至0时,才会清除这些值。值的负载一直在各地重复使用,特别是那些内部整数和单例。操作符不修改对象,它将名称分配给一个完全不同的对象,该对象可能有id,也可能没有id
例如,整数是不可变的;没有办法在一个文件中添加一些内容并保持相同的id
事实上,小整数至少在cPython中是固定的,所以如果你这样做:
x = 1
y = 2
x = x + 1
那么x
和y
可能具有相同的id。在python中,int和string等“基本”类型是不可变的,这意味着它们不能修改
Python实际上相当有效,因为正如@Wooble所评论的那样,«非常短的字符串和小的整数被插入。»:如果两个变量引用相同(小)的不可变值,它们的id是相同的(减少重复的不可变值)
使用不可变类型背后的原因是对这些值进行并发访问的安全方法
归根结底,这取决于设计的选择
根据您的需要,您可以更多地利用一种实现而不是另一种实现。
例如,在一种类似的语言Ruby中可以找到一种不同的原理,在Ruby中,Python中的那些类型是不可变的,而不是。准确地说,赋值x=x+1
不会修改x
正在引用的对象,它只是让x指向另一个值为x+1
的对象
要理解背后的逻辑,需要理解和引用语义之间的区别
具有值语义的对象意味着只有它的值才重要,而不是它的标识。虽然具有引用语义的对象关注其标识(在Python中,标识可以从id(obj)
返回)
通常,值语义意味着对象的不变性。或者相反,如果一个对象是可变的(即就地更改),这意味着它具有引用语义
让我们简要解释一下这种不变性背后的基本原理
具有引用语义的对象可以就地更改,而不会丢失其原始地址/标识。这是有意义的,因为具有引用语义的对象的标识使其自身区别于其他对象
相比之下,具有值语义的对象永远不应该改变自身
首先,这在理论上是可能和合理的。因为只有值(不是它的标识)是重要的,所以当需要更改时,可以安全地将其替换为具有不同值的另一个标识。这就是所谓的。请注意,对于具有引用语义的对象,这是不可能的
第二,这在实践中是有益的。正如OP所想,每次更改旧对象时丢弃旧对象似乎效率低下,但大多数情况下效率更高。首先,Python(或任何其他语言)有intern/cache方案,以减少要创建的对象。更重要的是,如果值语义的对象被设计为可变的,那么在大多数情况下它将占用更多的空间
例如,Date具有值语义。如果设计为可变的,则任何从内部字段返回日期的方法都会将句柄暴露给外部世界,这是有风险的(例如,外部可以直接修改此内部字段,而无需诉诸公共接口)。类似地,如果通过引用某个函数/方法传递任何日期对象,则该对象可能会在该函数/方法中被修改,这可能与预期不同。为了避免这些副作用,我们必须这样做:不是直接返回内部日期字段,而是返回它的克隆;他不是通过引用传递,而是通过值传递,这意味着会制作额外的副本。正如人们可以想象的那样,有更多的机会创造出更多的物体。更糟糕的是,这些额外的克隆使代码变得更加复杂
总之,不变性强化了值语义,它通常涉及较少的对象创建,具有较少的副作用和麻烦,并且更易于测试。此外,不可变对象本质上是线程安全的,这意味着在多线程中锁更少,效率更高
>>> a = 42
>>> b = 5
>>> id(a) == id(b)
False
>>> b += 37
>>> id(a) == id(b)
True