初识Python内存机制

id, is, ==

  • id 是 内存地址,每创建一个对象就会在内存中开辟一块空间,将这个临时数据存放在内存中,内存地址是这个空间的唯一标识,也就是这个对象的ID

    a = '123'
    print(id(a))    # 1492379299488
  • is 是 判断两边内存地址是否相等,如果内存地址相等,那么两个其实指向同一个内存地址

  • == 是 比较两边数值是否相等

    >>> a = 100
    >>> b = 100
    >>> a is b
    True
    >>> a == b
    True
    >>> a = 1000
    >>> b = 1000
    >>> a is b
    False
    >>> a == b
    True

可以说内存地址相同,那么值肯定相同,但是值相同,内存地址不一定相同

代码块

Python程序是由代码块构造的。块是一个python程序的文本,他是作为一个单元执行的。

  • 代码块:一个模块,一个函数,一个类,一个文件等都是一个代码块。

  • 而作为交互方式输入的每个命令都是一个代码块。

    def func1():
        pass      # 代码块
    ​
    def func2():
        pass      # 代码块

代码块的缓存机制

  • Python在执行同一个代码块的初始化对象的命令时,会检查是否其值是否已经存在,如果存在,会将其重用

    • 执行同一个代码块时,遇到初始化对象的命令时,他会将初始化的这个变量与值存储在一个字典中,在遇到新的变量时,会先在字典中查询记录,如果有同样的记录那么它会重复使用这个字典中的之前的这个值。所以在你给出的例子中,文件执行时(同一个代码块)会把i1、i2两个变量指向同一个对象,满足缓存机制则他们在内存中只存在一个,即:id相同

    • 适用对象:int (float) str bool

      • int(float):任何数字在同一个代码块下都会复用

      • bool:True和False在字典中会以0,1方式存在,并且复用

      • str:几乎所有的字符串都符合缓存机制,具体如下

        • 非乘法得到的字符串都满足代码块的缓存机制,只含有大小写字母,数字,下划线时

          s1 = "holle"
          s2 = "holle"
          s1 is s2
          True
        • 乘法得到的字符串分两种情况

          • 乘数为1时,任何字符串满足代码块的缓存机制

          • 乘数>=2时:仅含大小写字母,数字,下划线,总长度<=20,满足代码块的缓存机制

  • 能够提高一些字符串,整数处理任务在时间和空间上的性能,需要相同字符串,整数的时候,直接从‘字典’取出来复用,避免频繁的创建和销毁,提升效率,节约内存

小数据池

小数据池,不同代码块的缓存机制,也称为小整数缓存机制,或者称为驻留机制等等

Python自动将-5~256的整数进行了缓存,当你将这些整数赋值给变量时,并不会重新创建对象,而是使用已经创建好的缓存对象。

python会将一定规则的字符串在字符串驻留池中,创建一份,当你将这些字符串赋值给变量时,并不会重新创建对象, 而是使用在字符串驻留池中创建好的对象。

  其实,无论是缓存还是字符串驻留池,都是python做的一个优化,就是将~5-256的整数,和一定规则的字符串,放在一个‘池’(容器,或者字典)中,无论程序中那些变量指向这些范围内的整数或者字符串,那么他直接在这个‘池’中引用,言外之意,就是内存中之创建一个。

  • 适用对象:int(float),str,bool

    • int:对于整数来说,小数据池的范围是-5~256,如果多个变量指向同一个范围内的数字,他们指向同一个内存地址

      a = 100
      b = 100
      c = 100
      print(id(a),id(b),id(c))
      # 1639937888 1639937888 1639937888
    • str:字符串要从下面几个大方向分析

      • 字符串长度为0或者1,默认采用了驻留机制(小数据池)

        a = '@'
        b = '@'
        a is b
        True
      • 字符串的长度>1,且子只含有大小写字母,数字,下划线时,才会默认驻留

        a = "nayue_jintian_meiqian"
        b = "nayue_jintian_meiqian"
        a is b
        True
      • 用乘法得到的字符串,分两种情况

        • 乘数为1时

          • 仅包含大小写字母,数字,下划线,默认驻留

            a = "nayue_jintian_meiqian"
            b = a * 1
            a is b
            True
          • 含其他字符,长度 <= 1,默认驻留

          • 含其他字符,长度 > 1,默认驻留

        • 乘数>=2时

          仅包含大小写字母,数字,下划线,总长度 <= 20 ,默认驻留

          a = 'na_y'*4
          b = 'na_y'*4
          a is b
          True
      • 指定驻留

        from sys import intern
        a = intern('hello!@'*20)
        b = intern('hello!@'*20)
        print(a is b)
        #指定驻留是你可以指定任意的字符串加入到小数据池中,让其只在内存中创建一个对象,多个变量都是指向这一个字符串。
    • bool值就是True,False,五六你创建多少个变量指向True,False,它在内存中只存在一个

能够提高一些字符串,整数处理人物在时间和空间上的性能;需要值相同的字符串,整数的时候,直接从‘池’里拿来用,避免频繁的创建和销毁,提升效率,节约内存

深浅copy

深浅copy就是完全复制一份,和部分复制一份

  • 赋值运算

    ls1 = ['na','yue',['cat','xin']]
    ls2 = ls1
    ​
    ls1[0] = 'dd'
    ls1    # ['dd', 'yue', ['cat', 'xin']]
    ls2    # ['dd', 'yue', ['cat', 'xin']]
    ​
    ls1[2][1] = 'dog'
    ls1    # ['dd', 'yue', ['cat', 'dog']]
    ls2    # ['dd', 'yue', ['cat', 'dog']]

    对于赋值运算来说,ls1与ls2指向的是同一个内存地址,所以他们是完全一样的,任何一个变量对列表进行改变,剩下那个变量在使用列表之后,这个列表是发生了改变之后的列表

  • 浅拷贝copy

    # 同一代码块下
    ls1 = [1,'那月',True,(1,2,3),[22,33]]
    ls2 = ls1.copy()
    print(id(ls1),id(ls2))
    # 2685384972616 2685384972360
    print(id(ls1[-2]),id(ls2[-2]))
    # 2685384946744 2685384946744
    print(id(ls1[-1]),id(ls2[-1]))  
    # 2685384956360 2685384956360
    print(id(ls1[-1][0]),id(ls2[-1][0]))
    # 1630301600 1630301600
    ​
    # 不同代码块下
    >>> ls1 = [1,'那月',True,(1,2,3),[22,33]]
    >>> ls2 = ls1.copy()
    >>> print(id(ls1),id(ls2))
    # 2092396391432 2092396391496
    >>> print(id(ls1[-2]),id(ls2[-2]))
    # 2092396269432 2092396269432
    >>> print(id(ls1[-1]),id(ls2[-1]))
    # 2092396390472 2092396390472
    >>> print(id(ls1[-1][0]),id(ls2[-1][0]))
    # 1630301600 1630301600
    • 对于浅copy来说,只是在内存中创建了一个空间存放一个新列表,但新列表中的元素与愿列表中的元素是公用的

  • 深拷贝deepcopy

    # 同一代码块下
    import copy
    ​
    ls1 = [1,'那月',True,(1,2,3),[22,33]]
    ls2 = copy.deepcopy(ls1)
    print(id(ls1),id(ls2))
    # 2087560861704 2087560882952
    print(id(ls1[-2]),id(ls2[-2]))
    # 2087560836152 2087560836152
    print(id(ls1[-1]),id(ls2[-1]))
    # 2087560861768 2087560881928
    print(id(ls1[-1][0]),id(ls2[-1][0]))
    # 1630301600 1630301600
    ​
    # 不同代码块下
    >>> import copy
    >>> ls1 = [1,'那月',True,(1,2,3),[22,33]]
    >>> ls2 = copy.deepcopy(ls1)
    >>> print(id(ls1),id(ls2))
    # 2092396391560 2092396391432
    >>> print(id(ls1[-2]),id(ls2[-2]))
    # 2092396306504 2092396306504
    >>> print(id(ls1[-1]),id(ls2[-1]))
    # 2092396395976 2092396403528
    >>> print(id(ls1[-1][0]),id(ls2[-1][0]))
    # 1630301600 1630301600
    •   对于deepcopy来说,列表是在内存中重新创建的,列表中可变的数据类型是重新创建的,列表中不可变数据类型是公用的

    

思考:

ls1 = [1, 2, ['cat']]
ls2 = ls1[::]
ls1.append('yue')
print(ls2)

 

posted @ 2018-12-21 20:07  CatdeXin  阅读(225)  评论(0)    收藏  举报