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


当前回答

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

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

其他回答

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

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

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

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]

常见的不可变类型:

数字:int(), float(), complex() 不可变序列:str(), tuple(), frozenset(), bytes()

通用可变类型(几乎所有其他类型):

可变序列:list(), bytearray() 设置类型:Set () 映射类型:dict() 类,类实例 等。

快速测试类型是否可变的一个技巧是使用id()内置函数。

例如,使用on integer,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

使用on list,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

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

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()要快得多,因为它是字符串对象的一部分。