一、python中的变量及引用
1.1 python中的不可变类型:

数字(num)、字符串(str)、元组(tuple)、布尔值(bool)
接下来我们讲完后你就懂了为什么它们是不可变对象了。
都知道python中一切都是对象,而变量就是这些对象的引用,什么意思呢
综合表述:
变量是一个系统表的元素,拥有指向对象的连接的空间

对象是被分配的一块内存,存储其所代表的值

引用是自动形成的从变量到对象的指针

特别注意: 类型属于对象,不是变量

>>> c = 17 #1 数字17就是一个对象,实实在在存在计算机内存中
>>> d = c #2 c 和 d 都是对象17的一个引用,c指向17,d也是
>>> id(c) #3
1462698960
>>> id(d) #4
1462698960

在#1 处我们定义了各一个变量c,c指向了17(把17赋值给c),对象17的一个引用c



然后在#2处,又定义了一个变量d ,把c赋值给了d,接着#3、#4查看了c、d的 id 相同,

发现是同一个对象(17),对象17的引用+1



引用:

对象17的引用现在有两个了

变量:

在内部,变量事实上是到对象内存空间的一个指针

1.2 python中内存回收机制

1.2.1 python本身是一门动态语言
与c/c++ /java不同,不需要事先定义变量开辟内存空间,然后给变量赋值,存储到变量的内存空间中。使用结束,当然也不需要你去手动调用析构函数释放内存了。
python会预先申请一部分内存空间,在运行时定义了变量-对象,根据对象确认它的type,将对象放到申请的内存中,python每过一段时间就来检查一次,当有对象的引用为0时,就回收这块内存,返还回先申请的内存空间,而不是计算机。这样避免了内存碎片过多问题。

1.2.2 怎么减少对象的引用

  1. 将变量引用指向其他对象
>>> c = 17
>>> d = c
>>> id(c) #1
1462698960
>>> id(d) #2
1462698960
>>> c = "yue" #3
>>> id(c) #4
612496081896
>>> d #5
17

可以看到#1、#2处c、d都还是对象17的引用,当#3处把变量c 指向新对象字符串"yue" 时,#4处发现变量c指向的对象id变了,的确不是17了,所以对象17的引用 -1 如下图

注意:这儿改变了c的引用,可是#5处d却没有跟着c变,还是对象17

同理当你再把d指向其他对象时,对象17的引用就减为零,当Python来检查时,就会回收这块内存了

2.删除变量(引用)

>>> del d
>>> d
Traceback (most recent call last):
File "<stdin>", line 1, in <modul
NameError: name 'd' is not defined

不啰嗦,这样对象17就彻底被删除了,上图时对象17只剩下一个变量引用d。

同理对于函数,定义函数时,函数名就是一个引用,当其他地方调用函数时,引用+1,调用结束 -1 。在函数的命名空间中可以查到这些,详情看我这篇文章

python的内存回收就到这儿:总结:回收机制为判断对象 引用是否为0,如果为零就回收内存到自己申请的内存空间,不是计算机硬盘。

1.3 再谈不可变类型

通过上面的式子和图理解我们也知道了,当定义变量为数字、字符串、tuple、布尔值时,这些变量所对应的对象在内存空间的值是不可改变了,你重新赋值,也只是把变量引用指向了另一个对象,id变了,本身那个对象是不可变的。

>>> a = (1, 'one')
>>> id(a)
612494666568 #1
>>> a[0] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> a[0]
1
>>> a = (2, 'two')
>>> id(a)
612494666824 #2 #---------------------------------------------
>>> a = 'findxgo' #3
>>> id(a)
612496082848
>>> a.replace('x','--X--') #4
'find--X--go'
>>> id(a) #5
612496082848
>>> a = a.replace('x', '-X-') #6
>>> id(a)
612496086704

在#3出定义了字符串a,#4处替换x,得到一新字符串,但是原字符串还是#5id没变,当#6把替换的字符串赋值给变量a,a的引用指向了替换后新字符串

二、python中的深浅Copy
2.1 共享引用

如图:指两个或多个变量指向同一个内存空间
![](https://images2018.cnblogs.com/blog/1226829/201808/1226829-20180804124229776-277665676.png)

如果删掉c后, 不会影响d

拷贝概念的引入就是针对:可变对象的共享引用潜在的副作用而提出的。

2.2 可变对象

2.2.1 指python中,存储在内存可以被修改的对象:列表、字典等
上面说的数字、字符串、元组等不可变类型,在你复制时也就是增加了一个引用,无法去改变内存的值。对对象的其中一个引用变量操作不会影响其他引用。
但是对于列表、字典:

>>> list_1 = [5, 2, 1]
>>> L2 = list_1 #1 将list_1赋值给L2
>>> list_1,L2
([5, 2, 1], [5, 2, 1])
>>> list_1[2] = '01314' #2 修改list_1 索引2处的值
>>> list_1,L2
([5, 2, '01314'], [5, 2, '01314'])

可以看到#1 上面定义一个列表,赋值给L2后,L2、list_1对应完全一样的值(列表)事实上,他两的确对应着一块内存,你可以自己去查id,是那块内存(列表)的两个引用

当你去在list_1,或者L2进行操作时,改变了对应内存的值,所以#2下面两个值都变了。python中同一块内存(对象)的不同引用改变对象所以引用都会被影响。

同理字典:通过自己哈希表将key计算后得到的内存地址就是存放value的地方,当你用如上同样的方式改变哪儿的值,所有引用都会被影响。

2.3 浅copy

上述的情况如果想避免,有两种方式,原理都一样:copy一份放到另一个内存,变成同样(value)的两个对象,当你修改其中一个时,另一个不会影响。
1、切片复制:完全切片

>>> list_1 = [5, 2, 1]
>>> L2 = list_1[:] #1 此处完全切片,复制
>>> list_1,L2
([5, 2, 1], [5, 2, 1])
>>> id(list_1) #2 查看id
612496051784
>>> id(L2) #3
612496051720

如上:#1处完全切片也可以L2 = list_1[0: -1]是一样的,#2,#3处可以看见,id不同,就不是同一个对象,只是里面的value相同而已

同理copy模块的copy方法,这是浅拷贝。

2.4 深copy

1. 深浅拷贝,即可用于序列,也可用于字典

>>> import copy

>>> dict_1 = {'copy': '浅拷贝', 'deepcopy': ['deep', '第二层', ' 深拷贝']}

>>> D2 = copy.copy(dict_1)      #浅拷贝:只拷贝顶级的对象,也说:父级对象

>>> D3 = copy.deepcopy(dict_1)  #深拷贝:拷贝所有对象,顶级对象及其嵌套对象。或者说:父级对象及其子对象

>>> print("源:{0: ^18}\n浅拷贝:{1}\n深拷贝:{2}".format(id(dict_1),id(D2),id(D3)))
源: 37811303432
浅拷贝:37813197256
深拷贝:37813160264

2.改变源顶级对象,深浅拷贝不会变

>>> dict_1['copy'] = 'n_copy'
>>> dict_1;D2;D3
{'copy': 'n_copy', 'deepcopy': ['deep', '第二层', ' 深拷贝']}
{'copy': '浅拷贝', 'deepcopy': ['deep', '第二层', ' 深拷贝']}
{'copy': '浅拷贝', 'deepcopy': ['deep', '第二层', ' 深拷贝']}

3.改变源嵌套对象,浅拷贝变了,深拷贝不变

>>> dict_1['deepcopy'][1] = '嵌套层'
>>> dict_1;D2;D3
{'copy': 'n_copy', 'deepcopy': ['deep', '嵌套层', ' 深拷贝']}
{'copy': '浅拷贝', 'deepcopy': ['deep', '嵌套层', ' 深拷贝']}
{'copy': '浅拷贝', 'deepcopy': ['deep', '第二层', ' 深拷贝']}

这儿的浅拷贝,只拷贝了父级对象,在'deepcopy'对应的哪儿就是只拷贝了内存地址,而深拷贝还要去内存地址拷贝内容回来赋值

原理看到这儿,差不多也懂了,就不罗嗦了!

三、总结
  • 深浅拷贝都是对源对象的复制,占用不同的内存空间
  • 如果源对象只有一级目录的话,源做任何改动,不影响深浅拷贝对象
  • 如果源对象不止一级目录的话,源做任何改动,都要影响浅拷贝,但不影响深拷贝
  • 序列对象的切片其实是浅拷贝,即只拷贝顶级的对象

一个有意思的练习题

import copy
a = [1,2,3,[4,5],6]
b=a
c=copy.copy(a)
d=copy.deepcopy(a)
b.append(10)
c[3].append(11)
d[3].append(12)

a,b,c,d分别为什么?

答案我放评论

python 的内存回收,及深浅Copy详解的更多相关文章

  1. Python的深浅copy详解

    Python的深浅copy详解 目录 Python的深浅copy详解 一.浅copy的原理 1.1 浅copy的定义 1.2 浅copy的方法 二.深copy的原理 2.1 深copy的定义 2.2 ...

  2. 深浅copy详解

    一. 前言 在python中,对象的赋值和深浅copy,是有差异的.最终得的值也不同,下面我们就通过几个例子,来看下它们之间的区别. 二. 赋值 list2 = ["jack",2 ...

  3. JVM垃圾回收算法及回收器详解

    引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...

  4. 深拷贝与浅拷贝(mutableCopy与Copy)详解 iOS

    深拷贝与浅拷贝(mutableCopy与Copy)详解 iOS ios中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying 协议的类可以发送copy消息,遵守NSMutab ...

  5. 【python】redis基本命令和基本用法详解

    [python]redis基本命令和基本用法详解 来自http://www.cnblogs.com/wangtp/p/5636872.html 1.redis连接 redis-py提供两个类Redis ...

  6. Python中操作mysql的pymysql模块详解

    Python中操作mysql的pymysql模块详解 前言 pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同.但目前pymysql支持python3.x而后者不支持 ...

  7. [转载]windows任务管理器中的工作设置内存,内存专用工作集,提交大小详解

    windows任务管理器中的工作设置内存,内存专用工作集,提交大小详解 http://shashanzhao.com/archives/832.html 虽然是中文字,但是理解起来还是很困难,什么叫工 ...

  8. 七牛云存储Python SDK使用教程 - 上传策略详解

    文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k  ...

  9. Python调用C/C++动态链接库的方法详解

    Python调用C/C++动态链接库的方法详解 投稿:shichen2014 这篇文章主要介绍了Python调用C/C++动态链接库的方法,需要的朋友可以参考下 本文以实例讲解了Python调用C/C ...

随机推荐

  1. CSipSimple通话记录分组

    为了便于查看通话记录,通常要对通话记录进行分组.本质上来说这没什么难度,只需要用ContentResolver去读数据库,剩下的就是策略问题.代码在com/csipsimple/ui/calllog/ ...

  2. laravel 指定 版本安装

    composer create-project laravel/laravel=5.0.* --prefer-dist composer create-project laravel/laravel= ...

  3. 一步一步搭建客服系统 (3) js 实现“截图粘贴”及“生成网页缩略图”

    最近在做一个客服系统的demo,在聊天过程中,我们经常要发一些图片,而且需要用其它工具截图后,直接在聊天窗口里粘贴,就可以发送:另外用户输入一个网址后,把这个网址先转到可以直接点击的link,并马上显 ...

  4. 关于java读取和写入properties配置文件的内容

    一般通过使用流的方式进行读取 代码示例如下: package com.zznode.transmit.util; import java.io.FileInputStream; import java ...

  5. PAT (Advanced Level) 1024. Palindromic Number (25)

    手动模拟加法高精度. 注意:如果输入数字的就是回文,这个时候输出0步. #include<iostream> #include<cstring> #include<cma ...

  6. js扩展父类方法

    在网上找了很多一直没找到关于JS扩展父类的方法,让我很是郁闷啊~要是真的开发组遇到了该咋整,于是乎自己手写了一些测试代码,没想到通过了……(难道是人品太好了?)废话不多说了直接上代码看看~ <s ...

  7. 双向循环链表(C语言描述)(二)

    链表的基本操作基于对链表的遍历:计算链表的长度就是对链表进行一次遍历: int linkedlist_length(const LinkedList list) { assert(list); ; L ...

  8. 什么是IO多路复用

    先百度或者知乎,找到这篇文章 [1] IO 多路复用是什么意思? 文中提到: 第一种好理解,就是来一个请求,fork一个进程,第二种提到I/O多路复用使用单个线程实现的,作者肯定没有写错,因为后面的文 ...

  9. 前端开发【第一篇: HTML】

    HTML初识  1.什么是HTML? HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).  2.网页的组成 我们平时 ...

  10. python第二十三天-----作业中

    #!usr/bin/env python #-*-coding:utf-8-*- # Author calmyan import os ,sys,time from core import trans ...