我对什么是不可变类型感到困惑。我知道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类,它没有标记错误。


当前回答

一个类是不可变的,如果该类的每个对象在实例化时都有一个固定的值,以后不能更改

换句话说,要么改变变量(名称)的整个值,要么就不管它。

例子:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

你希望这可以工作并打印hello world,但这将抛出以下错误:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

解释器说:我不能改变这个字符串的第一个字符

你将不得不改变整个字符串,以使其工作:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

查看这个表格:

其他回答

首先,一个类是否有方法或者它的类结构是什么与可变性无关。

int和float是不可变的。如果我这样做

a = 1
a += 5

它在内存的第一行将名称a指向1。在第二行,它查找1,加5,得到6,然后把a指向内存中的6——它没有以任何方式把1变成6。同样的逻辑适用于下面的例子,使用其他不可变类型:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

对于可变类型,我可以做一些事情来改变它在内存中存储的值。:

d = [1, 2, 3]

我已经在内存中创建了1、2和3的位置列表。如果我这样做

e = d

我只是把e指向d指向的同一个列表。然后我可以这样做:

e += [4, 5]

e和d所在的列表也会被更新到内存中的位置为4和5。

如果我回到一个不可变类型,用一个元组来做:

f = (1, 2, 3)
g = f
g += (4, 5)

那么f仍然只指向原来的元组——你已经把g指向了一个全新的元组。

现在,用你的例子

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

你经过的地方

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

(这是一个元组的元组)作为val,你会得到一个错误,因为元组没有.clear()方法-你必须传递dict(d)作为val为它工作,在这种情况下,你会得到一个空的SortedKeyDict作为结果。

可变对象必须至少有一个方法能够改变对象。例如,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

是否会创建一个由x引用的值5

X ->

>>>y = x

这个表述使y = 5 (x

X -------------> 5 <-----------y

>>>x = x + y

由于x是一个整数(不可变类型)已重新构建。

在语句中,RHS上的表达式将得到值10,当它被赋值给LHS (x)时,x将重新构造为10。所以现在

x ---------> 10

y ---------> 5

我没有阅读所有的答案,但所选的答案是不正确的,我认为作者有一个想法,即能够重新分配变量意味着任何数据类型都是可变的。事实并非如此。可变性与通过引用传递而不是通过值传递有关。

假设您创建了一个List

a = [1,2]

如果你说:

b = a
b[1] = 3

即使你在B上重新分配了一个值,它也会重新分配a上的值。这是因为当你分配“B = a”时。您传递给对象的是“Reference”,而不是值的副本。而字符串、浮点数等则不是这样。这使得列表、字典和类似的东西是可变的,但是布尔值、浮点数等是不可变的。

在Python中,有一种简单的方法可以知道:

不变的:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

可变的:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

And:

>>> s=abs
>>> s is abs
True

所以我认为内置函数在Python中也是不可变的。

但我真的不明白float是如何工作的:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

太奇怪了。