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


当前回答

在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

太奇怪了。

其他回答

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

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作为结果。

看待差异的一种方式是:

在python中,对不可变对象的赋值可以被认为是深度拷贝, 而对可变对象的赋值是浅的

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

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

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

例如,你不会想到像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)这个解释当然是不科学的,它是为了帮助你掌握可变和不可变的区别。

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

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

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

例子:

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

查看这个表格: