Python中有一些容易忽略的不可变类型包括: str
, integer
, tuple
, None
。
def demo(lst=[]):
lst.append("hello")
return lst
demo()
['hello']
demo()
['hello', 'hello']
在这里,由于 lst
是一个可变参数,而 demo
在初始化时 lst
参数指向一个 []
的内存空间,
之后每一次调用, []
这个内存空间都 append
一个 "hello"
,
而由于 lst
依然指向这个内存空间,所以就会看到demo函数调用的奇怪现象,解决问题的办法就是引入不可变类型。
def demo(lst=None):
lst=[]
lst.append("hello")
return lst
demo()
['hello']
demo()
['hello']
不可变(mutable)类型: int
, long
, float
, string
, tuple
, frozenset
,
可变类型(immutable)类型: list
, dict
。
Python中所有变量都是值的引用,也就说变量通过绑定的方式指向其值。 而这里说的不可变指的是值的不可变。 对于不可变类型的变量,如果要更改变量,则会创建一个新值, 把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。
下面用 int
和 list
分别作为代表进行讲解。
id(1),id(2)
(94089742363208, 94089742363240)
a = 1
id(a)
94089742363208
a = 3
id(a)
94089742363272
lst = [0]
id(lst)
139737324473024
lst = [0,1]
id(lst)
139737324478720
表面上看可变类型,python似乎实现了不同类型的管理方式,其实不是的。
其实 lst
代表地址,它引用的 lst[0]
, lst[1]
的内存地址其实是变了的,
因为 lst[i]
就是 int(此处)
,而 int
就是不可变类型。
另外,还想延伸一下关于__new__
的用法。
为什么要放在这里说,待会看了这个例子就会明白。
class Word(str):
def __new__(cls, word):
word = word.replace(" ","")
return str.__new__(cls,word)
def __init__(self,word):
self.word = word
def __eq__(self, other):
return len(self)==len(other)
def main():
a=Word("foorrrdd ")
b=Word("sswwss ")
print(a==b)
if __name__ == '__main__':
main()
False
在这段代码里,可以看到Word类继承自 str
, str
是一个不可变类型,
因此需要使用到__new__
这个魔术方法,在这里对word这个形参进行了预处理,
然后预处理后的形参word会传递给__init__
。由于此例此种情形中,
a,b指向的是不同的内存空间,
即使不用__new__
也不会因为实参的传入导致上面例子出现不断追加的情况,
但显然这会是一种更为安全的写法。