Python中一切皆对象,每个对象都有其唯一的id,对应的类型和值,其中id指的是对象在内存中的位置。根据对象的值是否可修改分为可变对象和不可变对象。其中,

不可对象包括:数字,字符串,tuple

可变对象包括:list,dict,set

Python中的变量可以指向任意对象,可以将变量都看成是指针,保存了所指向对象的内存地址(对象的引用)。

不可变对象

对于不可变对象,如果要更新变量引用的不可变对象的值,会创建新的对象,改变对象的引用,举个例子:

In [41]: x = 1

In [42]: y = x

In [43]: print(id(x))
140719461487648 In [44]: x = 2 In [45]: print(id(y))
140719461487648 In [46]: print(id(x))
140719461487680 In [47]: print(id(2))
140719461487680

上述是int类型的一个实例,可以看到:

  1. 想要变量的值,会在内存中创建一个新的对象,变量指向新的对象。
  2. 对于值为1或者2,不管几个引用指向它,内存中都只占用了一个地址,在Python内部会通过引用计数来记录指向该地址的引用个数,当引用个数为0时会进行垃圾回收。

所以,不可变对象的优点是对于相同的对象,无论多少个引用,在内存中只占用一个地址,缺点是更新需要创建新的对象,因此效率不高。

可变对象

对于可变对象,举个例子:

In [57]: a = [1, 2]

In [58]: b = a

In [59]: print(id(a), id(b))
1961088949320 1961088949320 In [60]: a.append(3) In [61]: print(a, b)
[1, 2, 3] [1, 2, 3] In [62]: print(id(a), id(b))
1961088949320 1961088949320 In [63]: a = [1, 2, 3] In [64]: print(id(a))
1961088989704

可以看到:

  1. 值的变化是在原有对象的基础上进行更新的,变量引用的地址没有变化。
  2. 对于一个变量的两次赋值操作,值相同,但是引用的地址是不同的,也就是同样值的对象,在内存中是保存了多份的,地址是不同的。

注意,我们研究可变对象的变化,研究的是同一对象,也就是可变指的是append, +=这种操作,而不包括新的赋值操作,赋值操作是会新建一个对象的。比如:

In [96]: a = [1, 2, 3]

In [97]: b = a

In [98]: a = [1]

In [99]: b
Out[99]: [1, 2, 3]

参数传递问题

因为可变对象和不可变对象的特性,因此在参数传递上需要注意,详情可参考 我的回答

深拷贝和浅拷贝

首先,举个例子:

In [69]: data = [{'name': 'a', 'deleted': True}, {'name' : 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]

In [70]: print(data)
[{'name': 'a', 'deleted': True}, {'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] In [71]: def add(data_list):
...: for item in data_list:
...: if item.get('deleted'):
...: data_list.remove(item)
...: return data_list
...: In [72]: add_result = add(data) In [73]: print(add_result)
[{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}] In [74]: print(data)
[{'name': 'b', 'deleted': False}, {'name': 'c', 'deleted': False}]

你会发现调用了add方法之后,data已经变了,在之后的代码中你已经无法再使用原来的data了,具体的原因在参数传递那个问题中我有说明。

但是,当你希望在add方法中并不会修改data的值,要怎么做呢?

这时候,你需要了解下深拷贝和浅拷贝:

深拷贝和浅拷贝的概念:

  1. 浅拷贝(shallow copy):构造一个新的对象并将原对象中的引用插入到新对象中,只拷贝了对象的地址,而不对对应地址所指向的具体内容进行拷贝,也就是依然使用原对象的引用。实现方式包括:工厂函数(list, set等)、切片,copy模块的copy方法。
  2. 深拷贝(deep copy):复制了对象的和引用,深拷贝得到的对象和原对象是相互独立的。实现方式:copy模块的deepcopy方法。

所以,上述代码可按需更新为:

def add(data_list):
ret_data_list = deepcopy(data_list)
for item in ret_data_list:
if item.get('deleted'):
ret_data_list.remove(item)
return ret_data_list

以上。

Python可变对象和不可变对象的更多相关文章

  1. 【Python】可变对象和不可变对象

    Python在heap中分配的对象分成两类:可变对象和不可变对象.所谓可变对象是指,对象的内容是可变的,例如list.而不可变的对象则相反,表示其内容不可变. 不可变对象:int,string,flo ...

  2. Python 可变对象和不可变对象

    具体可以看这里:http://thomaschen2011.iteye.com/blog/1441254 不可变对象:int,string,float,tuple 可变对象   :list,dicti ...

  3. Python中的可变对象和不可变对象

    Python中的可变对象和不可变对象 什么是可变/不可变对象 不可变对象,该对象所指向的内存中的值不能被改变.当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一 ...

  4. python,可变对象,不可变对象,深拷贝,浅拷贝。

    学习整理,若有问题,欢迎指正. python 可变对象,不可变对象 可变对象 该对象所指定的内存地址上面的值可以被改变,变量被改变后,其所指向的内存地址上面的值,直接被改变,没有发生复制行为,也没有发 ...

  5. python函数默认参数为可变对象的理解

    1.代码在执行的过程中,遇到函数定义,初始化函数生成存储函数名,默认参数初识值,函数地址的函数对象. 2.代码执行不在初始化函数,而是直接执行函数体. 代码实例 这要从函数的特性说起,在 Python ...

  6. python 中的可变对象与不可变对象

    近日辞职待工,没有实际的项目与大家分享.暂写写在实际运用python中遇到的关于可变对象和不可变对象的坑. 首先我们需要明确一个概念,在python中一且皆对象.我们一般定义一个变量a=0,其实质a是 ...

  7. python可变对象与不可变对象的差别

    一.可变对象和不可对象 Python在heap中分配的对象分成两类:可变对象和不可对象.所谓可变对象是指,对象的内容可变,而不可变对象是指内容不可变.   不可变对象:int.string.float ...

  8. python学习(28) 浅谈可变对象的单例模式设计

    python开发,有时候需要设计单例模式保证操作的唯一性和安全性.理论上python语言底层实现和C/C++不同,python采取的是引用模式,当一个对象是可变对象,对其修改不会更改引用的指向,当一个 ...

  9. Python入门之python可变对象与不可变对象

    本文分为如下几个部分 概念 地址问题 作为函数参数 可变参数在类中使用 函数默认参数 类的实现上的差异 概念 可变对象与不可变对象的区别在于对象本身是否可变. python内置的一些类型中 可变对象: ...

  10. python可变对象与不可变对象

    可变/不可变对象定义 不可变对象 该对象所指向的内存中的值不能被改变.当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址. 可 ...

随机推荐

  1. 【Linux常见问题】CentOS 6 root用户密码忘记,找回密码方法

    1.Linux的root密码修改不像Windows的密码修改找回,Windows的登录密码忘记需要介入工具进行解决.CentOS6和CentOS7的密码方法也是不一样的,具体如下: 2.centos ...

  2. JavaScript HTMlL DOM对象(上)

    Dom:document.相当于把所有的html文件,转换成了文档对象. 之前说过:html-裸体的人:css-穿上衣服:js-让人动起来. 让人动起来,就得先找到他,再修改它内容或属性. 找到标签 ...

  3. Vue移动端项目中下拉刷新和上拉加载

    Vue2.0中引入Mint-UI的下拉刷新和上拉加载.简单粗暴 安装Mint-UI npm i mint-ui -S 引入 打开项目的main.js入口文件,引入并使用.注意,为了方便,这里是全部引入 ...

  4. 简单的环绕散射 Simple Wrap Diffuse From GPU GEMS1

    简单的环绕漫反射光照,实现起来特别简单,在Shader中加入以下几行:  float diffuse = max(0,dot(L,N));  float wrap_diffuse = max(0, ( ...

  5. nginx日志、nginx日志切割、静态文件不记录日志和过期时间

    2019独角兽企业重金招聘Python工程师标准>>> 12.10 Nginx访问日志 日志格式 vim /usr/local/nginx/conf/nginx.conf //搜索l ...

  6. 网络流中的图像转化为OpenCV中的Mat类型

    1,从网络中读取到的图像流,不支持查找,不能直接转化为Mat类型 2,例子如下: string Url = "http://192.168.0.110/cgi-bin/camera?reso ...

  7. Java——Java实现生产者消费者

    1.生产/消费者模型 生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括"生产者"."消费者"."仓库"和"产品" ...

  8. jacoco 生成单测覆盖率报告

    一.jacoco 简介 jacoco 是一个开源的覆盖率工具,它针对的开发语言是 java.其使用方法很灵活,可以嵌入到 ant.maven 中:可以作为 Eclipse 插件:可以作为 javaAg ...

  9. 麦基数(p1045)

    描述: \(计算2^{P}−1的位数和最后500位数字(用十进制高精度数表示)\) Ⅰ.求位数 \(因为2^p最后一位必定不为0,求2^p-1的位数也就是求2^p位数\) \(2^p的位数确实很难求, ...

  10. 经典卷积神经网络算法(2):AlexNet

    .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...