这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的。本文结合数据与内存形态讲解python中的数据,内容包括:

  • 引用与对象
  • 可变数据类型与不可变数据类型
  • 引用传递与值传递
  • 深拷贝与浅拷贝

(id函数:你可以通过python的内置函数 id() 来查看对象的身份(identity),这个所谓的身份其实就是 对象 的内存地址)

一、引用与对象:引用与对象的关系: 

  1.  
    #创建两个对象
  2.  
    name1='wupeiqi'
  3.  
    name2='alex'
  • 1
  • 2
  • 3

对象:当创建数据对象时,在内存中会保存对象的值,这个值就是对象自己;(字符串对象:”wupeiqi”) 
引用:对象保存在内存空间,外部想要使用对象的值,需要使用引用,就是‘name1’,’name2’。内存会保存对象的引用数量,当某个对象的引用数量为0时,对象会被回收。

二、可变数据类型与不可变数据类型 
1,数据分类:

  • 可变数据类型:列表list和字典dict
  • 不可变数据类型:整型int、浮点型float、字符串型string和元组tuple

这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域不可变)。如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。

(1)python中的不可变数据类型,不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象; 
(2)可变数据类型,允许变量的值发生变化,即如果对变量进行append、+=等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。”

2,不可变数据类型:不可变是指对象本身的值是不可变的(当你创建a=1整型对象,用a去引用它,内存中的对象1是不变得,当执行a=2时,只是重新创建了对象2,用a引用,如果1对象没有其他引用会被回收)

  1.  
    >>> x = 1
  2.  
    >>> id(x)
  3.  
    31106520
  4.  
    >>> y = 1
  5.  
    >>> id(y)
  6.  
    31106520
  7.  
    >>> x = 2
  8.  
    >>> id(x)
  9.  
    31106508
  10.  
    >>> y = 2
  11.  
    >>> id(y)
  12.  
    31106508
  13.  
    >>> z = y
  14.  
    >>> id(z)
  15.  
    31106508
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

解释:这里的不可变大家可以理解为x引用的地址处的值是不能被改变的,也就是31106520地址处的值在没被垃圾回收之前一直都是1,不能改变,如果要把x赋值为2,那么只能将x引用的地址从31106520变为31106508,相当于x = 2这个赋值又创建了一个对象,即2这个对象,然后x、y、z都引用了这个对象,所以int这个数据类型是不可变的,如果想对int类型的变量再次赋值,在内存中相当于又创建了一个新的对象,而不再是之前的对象。从下图中就可以看到上面程序的过程。 

3,可变对象:可变是指对象本身的值是可变的(list,dict对象的值其实是引用了其他对象,当改变对象的值时,其实是引用了不同的对象)

  1.  
    >>> a = [1, 2, 3]
  2.  
    >>> id(a)
  3.  
    41568816
  4.  
    >>> a = [1, 2, 3]
  5.  
    >>> id(a)
  6.  
    41575088
  7.  
    >>> a.append(4)
  8.  
    >>> id(a)
  9.  
    41575088
  10.  
    >>> a += [2]
  11.  
    >>> id(a)
  12.  
    41575088
  13.  
    >>> a
  14.  
    [1, 2, 3, 4, 2]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

解释:(1)进行两次a = [1, 2, 3]操作,两次a引用的地址值是不同的,也就是说其实创建了两个不同的对象,这一点明显不同于不可变数据类型,所以对于可变数据类型来说,具有同样值的对象是不同的对象,即在内存中保存了多个同样值的对象,地址值不同。 
(2)我们对列表进行添加操作,分别a.append(4)和a += [2],发现这两个操作使得a引用的对象值变成了上面的最终结果,但是a引用的地址依旧是41575088,也就是说对a进行的操作不会改变a引用的地址值,只是在地址后面又扩充了新的地址,改变了地址里面存放的值,所以可变数据类型的意思就是说对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的内容变化了或者地址得到了扩充。下图对这一过程进行了图示,可以很清晰地看到这一过程。 

三、引用传递与值传递:可变对象为引用传递,不可变对象为值传递。(函数传值) 
1,引用传递:当传递列表或者字典时,如果改变引用的值,就修改了原始的对象。

  1.  
    # 添加了一个string类型的元素添加到末尾
  2.  
     
  3.  
    def ChangeList(lis):
  4.  
    lis.append('hello i am the addone')
  5.  
    print lis
  6.  
    return
  7.  
     
  8.  
    lis = [1, 2, 3]
  9.  
    ChangeList(lis)
  10.  
    print lis
  11.  
     
  12.  
    输出:
  13.  
    [1,2,3, 'hello i am the addone']
  14.  
     
  15.  
    [1,2, 3,'hello i am the addone']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2,值传递:当传递不可变对象时,如果改变引用的值,只是创建了不同的对象,原始对象并没有改变。

  1.  
    def ChangeString(string):
  2.  
    string = 'i changed as this'
  3.  
    print string
  4.  
    return
  5.  
     
  6.  
    string = 'hello world'
  7.  
    ChangeString(string)
  8.  
    print string
  9.  
     
  10.  
    输出:
  11.  
    i changed as this
  12.  
     
  13.  
    hello world
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

四、深拷贝与浅拷贝: 
copy.copy() 浅拷贝;copy.deepcopy() 深拷贝。浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。拷贝序列类型对象(列表\元组)时,默认是浅拷贝。

1,赋值拷贝: 
赋值,只是创建一个变量,该变量指向原来内存地址:n4 = n3 = n2 = n1 = “123/’Wu’” 

2,浅拷贝:在内存中只额外创建第一层数据

  1.  
    import copy
  2.  
    n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
  3.  
    n3 = copy.copy(n1)
  • 1
  • 2
  • 3

  1.  
    import copy
  2.  
    a = [1,[[2,3],5],3]
  3.  
    b = a.copy() #copy.copy(a)
  4.  
     
  5.  
    print(id(a[1]))
  6.  
    print(id(b[1]))
  7.  
     
  8.  
    c = copy.deepcopy(a)
  9.  
    print(id(c[1]))
  10.  
     
  11.  
    输出:
  12.  
    3021497843400
  13.  
    3021497843400
  14.  
    3021497854728
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3,深拷贝:在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)

  1.  
    import copy
  2.  
    n1 = {"k1": "wu", "k2": 123, "k3": ["alex", 456]}
  3.  
    n4 = copy.deepcopy(n1)
  • 1
  • 2
  • 3

参考文献: 
http://blog.csdn.net/dan15188387481/article/details/49864613 
https://www.cnblogs.com/lfpython/p/7207747.html 
https://www.cnblogs.com/huamingao/p/5809936.html 
https://www.cnblogs.com/jiangzhaowei/p/5740913.html

详解Python变量在内存中的存储的更多相关文章

  1. Java变量在内存中的存储

    目录 Java变量在内存中的存储 成员变量 局部变量 总结 Java变量在内存中的存储 以下探究成员变量和局部变量在内存中的存储情况. package com.my.pac04; /** * @aut ...

  2. python中变量在内存中的存储与地址关系解析、浅度/深度copy、值传递、引用传递

    ---恢复内容开始--- 1.变量.地址 变量的实现方式有:引用语义.值语义 python语言中变量的实现方式就是引用语义,在变量里面保存的是值(对象)的引用(值所在处内存空间的地址).采用这种方式, ...

  3. PHP变量在内存中的存储方式

    原文:http://www.phppan.com/tag/refcount/ 每门计算机语言都需要一些容器来保存变量数据.在一些语言当中,变量都有特定的类型,如字符串,数组,对象等等.比如C和Pasc ...

  4. 详解Python中内置的NotImplemented类型的用法

    它是什么? ? 1 2 >>> type(NotImplemented) <type 'NotImplementedType'> NotImplemented 是Pyth ...

  5. 详解Python编程中基本的数学计算使用

    详解Python编程中基本的数学计算使用 在Python中,对数的规定比较简单,基本在小学数学水平即可理解. 那么,做为零基础学习这,也就从计算小学数学题目开始吧.因为从这里开始,数学的基础知识列位肯 ...

  6. 详解js变量、作用域及内存

    详解js变量.作用域及内存 来源:伯乐在线 作者:trigkit4       原文出处: trigkit4    基本类型值有:undefined,NUll,Boolean,Number和Strin ...

  7. 举例详解Python中的split()函数的使用方法

    这篇文章主要介绍了举例详解Python中的split()函数的使用方法,split()函数的使用是Python学习当中的基础知识,通常用于将字符串切片并转换为列表,需要的朋友可以参考下   函数:sp ...

  8. 详解Python中re.sub--转载

    [背景] Python中的正则表达式方面的功能,很强大. 其中就包括re.sub,实现正则的替换. 功能很强大,所以导致用法稍微有点复杂. 所以当遇到稍微复杂的用法时候,就容易犯错. 所以此处,总结一 ...

  9. Python之路-变量和基本数据类型详解(变量、数据类型、)

    一.注释 注释的作用: 增加程序的可读性 作为调试用 提高团队的合作效率 注释的分类 1.单行注释 以井号(#)开头,右边的所有内容当做说明 2.多行注释 以三对单引号(’’’注释内容’’’)将注释包 ...

随机推荐

  1. 使用Docker快速创建.Net Core2.0 Nginx负载均衡节点

    本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws 一.Self-Host Kestrel 1. 在vs2017中新建dotnet core2. ...

  2. 使用 ES2015 编写 Gulp 构建

    Gulp 自 v3.9.0 版本增加对 Babel 的支持,也就是说可以使用 ES2015 语法来编写 gulp 任务. 检查 gulp 版本 $ gulp -v 确保 gulp-cli 和 gulp ...

  3. MSVCP110.DLL没有被指定在WINDOWS上运行

    要重新安装C++ 运行库 为msvcp110.dll是VC++2012的文件 数字代表版本msvcp120是VC++2013的 110是2012的 100是2010的 90是2008的 71是2005 ...

  4. Elasticsearch学习之有用博客

    推荐阅读:1.阿里:https://elasticsearch.cn/article/61712.滴滴:http://t.cn/EUNLkNU3.腾讯:http://t.cn/E4y9ylL4.携程: ...

  5. Golang 的 协程调度机制 与 GOMAXPROCS 性能调优

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  6. linux 远程连接ssh提示IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY解决

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HA ...

  7. Spring-Boot 访问Controller时报错可能会是这个坑

    报错信息: 代码: @Controller("/index") public class IndexController extends BaseController{ @GetM ...

  8. 剑指offer——python【第34题】第一个只出现一次的字符

    题目描述 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写) 思路 遍历字符串,找到那个第 ...

  9. php 检测url

    if (!preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@ ...

  10. SpringBoot:四种读取properties文件的方式

    前言 在项目开发中经常会用到配置文件,配置文件的存在解决了很大一份重复的工作.今天就分享四种在Springboot中获取配置文件的方式. 注:前三种测试配置文件为springboot默认的applic ...