Python - 记学习对象可变性

作者: c37d344afd22 | 来源:发表于2017-07-20 22:49 被阅读174次

在刚学Python的时候就知道元组是不可变的,比如

In [1]: t = (1, 2, 3)

In [2]: id(t)
Out[2]: 4457650000

In [3]: t += (4, 5)

In [4]: id(t)
Out[4]: 4456501496

In [5]: t
Out[5]: (1, 2, 3, 4, 5)

当我们想要改变元组的时候他会新建一个对象来存储值,但是当我们的元组里有一个可变的对象呢

In [8]: t1 = ([1, 2, 3], )

In [9]: t1[0].append(4)

In [10]: t1
Out[10]: ([1, 2, 3, 4],)

其实元组保存的只是对象的引用,如果元组里的元素可变那么元素就是可变的。这里说的不可变只是元组保存的引用不变

在说一下深拷贝、浅拷贝

In [11]: l1 = [1, [2, 3, 4], (5, 6)]

In [12]: l2 = list(l1)

In [13]: l2
Out[13]: [1, [2, 3, 4], (5, 6)]

In [14]: l2[1].append(100)

In [15]: l2
Out[15]: [1, [2, 3, 4, 100], (5, 6)]

In [16]: l1
Out[16]: [1, [2, 3, 4, 100], (5, 6)]

In [18]: l1[-1] += (7, 8)

In [19]: l1
Out[19]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]

In [20]: l2
Out[20]: [1, [2, 3, 4, 100], (5, 6)]

list()方法默认做的是浅拷贝。浅拷贝就是只复制最外层的容器,复制出来容器里的元素仍为源容器中元素的引用。即l2的元素引用还是指向l1的,所以当l2[1]更改了的时候l1[1]也更改了。但是由于元组是不可变元素,所以当更改他的时候会新创建一个元组对象,所以l1和l2中的互不干扰

而深拷贝则不共享引用,全部重新创建新的引用

In [21]: import copy

In [22]: l3 = copy.deepcopy(l1)

In [23]: l3
Out[23]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]

In [24]: l1
Out[24]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]

In [25]: l1[1].append(200)

In [26]: l1
Out[26]: [1, [2, 3, 4, 100, 200], (5, 6, 7, 8)]

In [28]: l3
Out[28]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]

接下来说方法形参问题,都知道形参是一个实参的引用。那么这个时候传不可变对象还好。如果传可变参的话,那么方法做的更改就会关系到方法外

In [29]: def f(a, b):
    ...:     a += b
    ...:     return a
    ...:

In [30]: x = [1, 2]

In [31]: y = [3, 4]

In [32]: f(x, y)
Out[32]: [1, 2, 3, 4]

In [33]: x
Out[33]: [1, 2, 3, 4]

所以,尽量不要传递一个可变的对象给函数,除非你想这么做

同理,函数的默认参也不要是一个可变对象

class HauntedBus:
    def __init__(self, passengers=[]):
        self.passengers = passengers
    
    def pick(self, name):
        self.passengers.append(name)
    
    def drop(self, name):
        self.passengers.remove(name)

运行结果如下

In [32]: bus1 = HauntedBus(['Alice', 'Bill'])

In [33]: bus1.passengers
Out[33]: ['Alice', 'Bill']

In [34]: bus1.pick('Charlie')

In [35]: bus1.drop('Alice')

In [36]: bus1.passengers
Out[36]: ['Bill', 'Charlie']

In [37]: bus2 = HauntedBus()

In [38]: bus2.pick('Carrie')

In [39]: bus2.passengers
Out[39]: ['Carrie']

In [40]: bus3 = HauntedBus()

In [41]: bus3.passengers
Out[41]: ['Carrie']

In [42]: bus3.pick('Dave')

In [43]: bus3.passengers
Out[43]: ['Carrie', 'Dave']

In [44]: bus2.passengers
Out[44]: ['Carrie', 'Dave']

In [45]: bus2.passengers is bus3.passengers
Out[45]: True

In [46]: bus1.passengers
Out[46]: ['Bill', 'Charlie']

可以看到bus2和bus3共用了一组乘客,问题就是出在self.passengers = passengers,这里如果我们在创建对象时什么也不传则用了默认参passengers=[],而默认参是在加载模块的时候定义,不会每次都创建,所以默认参是这个函数的属性,而这个属性是个可变对象的时候,那么修改了之后后续的都会受到影响


最后

爱生活,爱小丽

相关文章

  • Python - 记学习对象可变性

    在刚学Python的时候就知道元组是不可变的,比如 当我们想要改变元组的时候他会新建一个对象来存储值,但是当我们的...

  • python的“不可变性”代表了什么?

    在python中,每一个对象都可以分为不可变性或者可变性。 不可变性可以用来保证在程序中保持一个对象的固定不变。 ...

  • Python进阶6

    对象引用、垃圾回收、可变性 Python中的变量是什么 引言 Python和java中的变量本质不一样,java的...

  • python学习笔记目录

    Python学习笔记目录(倒序) Python学习-异常 Python学习-面向对象 Python学习-文件系统 ...

  • python面向对象学习笔记-01

    学习笔记 # 0,OOP-Python面向对象 - Python的面向对象 - 面向对象编程 - 基础 -...

  • Python数据结构

    数据结构与可变性 可变性:创建后就不能改变,无法在原处修改,不可变对象的修改是返回一个新对象 利用类型转换函数进行...

  • Python对象引用、可变性和垃圾回收

    1. Python中的变量是什么 Python和Java中的变量不一样,Python的变量实质上是一个指针例如:a...

  • Python学习打call第七天:列表

    今天要学习的是Python的列表,主要学习一下什么是列表、列表的可变性、列表的访问、列表的增删改查等操作哦~ 1....

  • Python精简入门学习(十四)

    Python精简入门学习之面向对象(oop) -面向对象 -类和对象

  • 面向对象

    对象的特殊函数 构造函数,可重载 hashCode() equals() toString() 不可变性 Immu...

网友评论

    本文标题:Python - 记学习对象可变性

    本文链接:https://www.haomeiwen.com/subject/sfxskxtx.html