page contents

Python 的高级特性:容易忽略的不可变类型

Python 中有一些容易忽略的不可变类型 Str、Integer、None、Tuple # 错误演示In [45]: def demo(lst = []): ....: lst.append("hello") ....: return lst ....: In [46]: demo(...

Python 中有一些容易忽略的不可变类型 Str、Integer、None、Tuple

 # 错误演示

In [45]: def demo(lst = []):
....: lst.append("hello")
....: return lst
....:

In [46]: demo()
Out[46]: ['hello']

In [47]: demo()
Out[47]: ['hello', 'hello']

廖雪峰的 Python 教程有提到这一块,但并没有太细致。在这里,由于 lst 是一个可变参数,而 demo 在初始化时 lst 参数指向一个 [] 的内存空间,之后每一次调用,[] 这个内存空间都 append 一个「hello」,而由于 lst 依然指向这个内存空间,所以就会看到 demo 函数调用的奇怪现象,解决问题的办法就是引入不可变类型。

#正确演示

In [54]: def demo(lst = None):
....: lst = []
....: lst.append("hello")
....: return lst
....:

In [55]: demo()
Out[55]: ['hello']

In [56]: demo()
Out[56]: ['hello']

在正确演示中,将 lst 初始化为 None, 这样 lst 就是一个不可变参数,但是不能直接对 lst 直接使用 append,因为只有 list 才有 append 方法,因此需要将 lst 进行真正的初始化 lst = []

可变类型和不可变类型是一个很容易忽略的知识点,在这里深入进行研究,下面例举常见的不可变类型和可变类型。

  • 不可变「mutable」类型:int, long, float, string, tuple, frozenset
  • 可变类型「immutable」类型:list, dict

Python 中所有变量都是值的引用,也就说变量通过绑定的方式指向其值。 而这里说的不可变指的是值的不可变。 对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。下面用 int 和 list 分别作为代表进行讲解。

#不可变类型

In [31]: id(1),id(2)
Out[31]: (4477999936, 4477999968)

In [32]: a = 1

In [33]: id(a)
Out[33]: 4477999936

In [34]: # 当a赋一个新值时,变量a会绑定到新值上

In [35]: a = 3

In [36]: id(a)
Out[36]: 4478000000

#可变类型

In [38]: lst = [0]

In [39]: id(lst)
Out[39]: 4493976328

In [40]: lst = [0,1]

In [41]: id(lst)
Out[41]: 4499600328

表面上看可变类型,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()

在这段代码里,可以看到 Word 类继承自 str,str 是一个不可变类型,因此需要使用到 __new__这个魔术方法,在这里对 word 这个形参进行了预处理,然后预处理后的形参 word 会传递给 __init__ 由于此例此种情形中,a、b 指向的是不同的内存空间,即使不用 __new__ 也不会因为实参的传入导致上面例子出现不断追加的情况,但显然这会是一种更为安全的写法。

  • 发表于 2020-02-27 16:33
  • 阅读 ( 593 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1316 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章