我对什么是不可变类型感到困惑。我知道float对象被认为是不可变的,我的书中有这样的例子:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

因为类结构/层次结构,这被认为是不可变的吗?,这意味着float位于类的顶部,是它自己的方法调用。类似于这种类型的例子(即使我的书说dict是可变的):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

然而,可变的东西在类中有方法,例如:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

同样,对于最后一个类(SortedKeyDict_a),如果我将这种类型的set传递给它:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

不调用示例方法,它将返回一个字典。带有__new__的SortedKeyDict将其标记为错误。我尝试用__new__将整数传递给RoundFloat类,它没有标记错误。


当前回答

可变对象必须至少有一个方法能够改变对象。例如,list对象有append方法,它实际上会改变对象:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

但是float类没有改变float对象的方法。你可以:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

但是=操作数不是一个方法。它只是在变量和它右边的东西之间做了一个绑定,没有别的。它从不改变或创建对象。它声明了变量将指向什么,从现在开始。

当执行b = b + 0.1时,=操作数将变量绑定到一个新的浮点数,其创建结果为5 + 0.1。

当你将一个变量赋值给一个存在的对象时,不管是否是可变的,=操作数将变量绑定到该对象。没有别的事情发生

在任何一种情况下,=都只是进行绑定。它不改变或创建对象。

当执行= 1.0时,=操作数创建的不是浮点数,而是该行的1.0部分。实际上,当您编写1.0时,它是float(1.0)的简写,一个返回float对象的构造函数调用。(这就是为什么如果你输入1.0并按enter键,你会得到下面打印的“echo”1.0;这是你调用的构造函数的返回值)

现在,如果b是一个浮点数,你赋值a = b,两个变量都指向同一个对象,但实际上变量之间不能通信,因为对象是不可变的,如果你做b += 1,现在b指向一个新对象,而a仍然指向旧对象,不知道b指向什么。

但如果c是,假设,一个列表,你赋值a = c,现在a和c可以“通信”,因为list是可变的,如果你执行c.append('msg'),然后检查a,你得到消息。

(顺便说一下,每个对象都有一个唯一的关联id号,你可以通过id(x)获得。所以你可以检查一个对象是否相同,或者不检查它的唯一id是否改变了。)

其他回答

什么?浮点数是不可变的?但我做不到

x = 5.0
x += 7.0
print x # 12.0

那不是"mut" x吗?

你同意字符串是不可变的,对吧?但你可以做同样的事情。

s = 'foo'
s += 'bar'
print s # foobar

变量的值会改变,但改变的方式是改变变量所指向的对象。可变类型可以这样改变,也可以“就地”改变。

区别就在这里。

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

具体的例子

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

例如,对于不可变对象,赋值会创建一个新的值副本。

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

对于可变对象,赋值操作不会创建值的另一个副本。例如,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list

可变对象必须至少有一个方法能够改变对象。例如,list对象有append方法,它实际上会改变对象:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

但是float类没有改变float对象的方法。你可以:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

但是=操作数不是一个方法。它只是在变量和它右边的东西之间做了一个绑定,没有别的。它从不改变或创建对象。它声明了变量将指向什么,从现在开始。

当执行b = b + 0.1时,=操作数将变量绑定到一个新的浮点数,其创建结果为5 + 0.1。

当你将一个变量赋值给一个存在的对象时,不管是否是可变的,=操作数将变量绑定到该对象。没有别的事情发生

在任何一种情况下,=都只是进行绑定。它不改变或创建对象。

当执行= 1.0时,=操作数创建的不是浮点数,而是该行的1.0部分。实际上,当您编写1.0时,它是float(1.0)的简写,一个返回float对象的构造函数调用。(这就是为什么如果你输入1.0并按enter键,你会得到下面打印的“echo”1.0;这是你调用的构造函数的返回值)

现在,如果b是一个浮点数,你赋值a = b,两个变量都指向同一个对象,但实际上变量之间不能通信,因为对象是不可变的,如果你做b += 1,现在b指向一个新对象,而a仍然指向旧对象,不知道b指向什么。

但如果c是,假设,一个列表,你赋值a = c,现在a和c可以“通信”,因为list是可变的,如果你执行c.append('msg'),然后检查a,你得到消息。

(顺便说一下,每个对象都有一个唯一的关联id号,你可以通过id(x)获得。所以你可以检查一个对象是否相同,或者不检查它的唯一id是否改变了。)

在我看来,你似乎在争论可变/不可变到底意味着什么。下面是一个简单的解释:

首先,我们需要一个解释的基础。

把你编程的任何东西都想象成一个虚拟对象,一个以二进制数字序列的形式保存在计算机内存中的东西。(不过,不要试图把这个想象得太困难。^^)现在在大多数计算机语言中,你不会直接处理这些二进制数,而是更多地使用二进制数的解释。

例如,你不会想到像0x110, 0xaf0278297319或类似的数字,而是你会想到像6这样的数字或像“Hello, world”这样的字符串。然而,这些数字或字符串是计算机内存中二进制数的一种解释。对于变量的任何值都是如此。

简而言之:我们不是用实际值编程,而是用实际二进制值的解释编程。

Now we do have interpretations that must not be changed for the sake of logic and other "neat stuff" while there are interpretations that may well be changed. For example think of the simulation of a city, in other words a program where there are many virtual objects and some of these are houses. Now may these virtual objects (the houses) be changed and can they still be considered to be the same houses? Well of course they can. Thus they are mutable: They can be changed without becoming a "completely" different object.

现在想想整数:它们也是虚拟对象(计算机内存中的二进制数序列)。如果我们改变其中一个,比如把6的值加1,它还是6吗?当然不是。因此任何整数都是不可变的。

所以:如果虚拟对象的任何变化意味着它实际上变成了另一个虚拟对象,那么它就被称为不可变的。

最后的话:

(1)永远不要把你在现实世界中可变和不可变的经验与某种语言的编程混淆在一起:

每种编程语言都有自己的定义,哪些对象可以静音,哪些对象不可以静音。

因此,虽然您现在可能理解了含义上的差异,但仍然需要学习每种编程语言的实际实现. ...的确,一种语言可能有一个目的,即6可能被削弱为7。然后,这将是相当疯狂或有趣的东西,就像平行宇宙的模拟

(2)这个解释当然是不科学的,它是为了帮助你掌握可变和不可变的区别。

Mutable意味着它可以改变/变异。相反,不可改变。

有些Python数据类型是可变的,有些则不是。

让我们来看看哪些类型适合每个类别,并看一些例子。


可变的

在Python中有各种可变类型:

列表 dict 集

让我们看看下面关于列表的例子。

list = [1, 2, 3, 4, 5]

如果我执行以下操作来更改第一个元素

list[0] = '!'
#['!', '2', '3', '4', '5']

它工作得很好,因为列表是可变的。

如果我们考虑这个列表,它被改变了,然后给它赋值一个变量

y = list

如果我们改变列表中的一个元素,比如

list[0] = 'Hello'
#['Hello', '2', '3', '4', '5']

如果输出y,它就会给出

['Hello', '2', '3', '4', '5']

因为list和y都指向同一个列表,我们改变了列表。


不可变的

在一些编程语言中,可以定义一个常量,如下所示

const a = 10

如果调用,它会给出一个错误

a = 20

然而,这在Python中不存在。

然而,在Python中,有各种不可变类型:

没有一个 保龄球 int 浮动 str 元组

让我们看看下面关于字符串的例子。

取字符串a

a = 'abcd'

我们可以得到第一个元素

a[0]
#'a'

如果试图给第一个位置的元素赋一个新值

a[0] = '!'

它会给出一个错误

“str”对象不支持项赋值

当对一个字符串使用+=时,例如

a += 'e'
#'abcde'

它不会给出一个错误,因为它把a指向了一个不同的字符串。

这和下面一样

a = a + 'f'

不改变字符串。

不可变的优点和缺点

•内存中的空间从一开始就知道。它不需要额外的空间。

•通常,它会让事情更有效率。例如,查找字符串的len()要快得多,因为它是字符串对象的一部分。