zoukankan      html  css  js  c++  java
  • Python引用拷贝赋值

    先安利一个网站,对学习编程很有帮助:
    http://www.pythontutor.com/

    可以逐行可视化执行代码,具体自行体验啦


    这个网站也是我在看别人的博文时候找到的,也先贴上别人的理解吧,我觉得写的都很好:

    REF:

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

    python 深入理解 赋值、引用、拷贝、作用域


    俗话说得好,师傅领进门,修行靠个人.学python也没多久,17年的时候走过一遍语法,应该没完.当时看的byte-of-python.

    我觉得这书还行,轻量化,如果学过一门编程语言上手应该挺快的.最近又开始看视频学习了,看了几天,然而并没有实际写什么程序出来.

    到视频后面感觉跟不上了,顿时明白自己应该叫停了.那些知识需要慢慢消化,不动手是不行的!

    在用python的时候总感觉迷迷糊糊的,这次是解决引用赋值的问题.

    上面两篇文章说的很清楚了,我就悄悄把他们剪切一下,方便自己理解:

    可变对象和不可变对象

    在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等。需要注意的是:这里说的不可变指的是值的不可变。对于不可变类型的变量,如果要更改变量,则会创建一个新值,把变量绑定到新值上,而旧值如果没有被引用就等待垃圾回收。另外,不可变的类型可以计算hash值,作为字典的key。可变类型数据对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的内存地址会保持不变,但区域会变长或者变短。

    示例:

     1 #immutable
     2 a = 12
     3 print(id(a))
     4 a=1
     5 print(id(a))
     6 st = "thisisastring"
     7 print(id(st))
     8 st = "string"
     9 print(id(st))
    10 
    11 #mutable
    12 li = [12,12,342,4]
    13 print(id(li))
    14 li.append(12)
    15 print(id(li))
    16 
    17 #a new list
    18 li = [12,123,213,123,123]
    19 print(id(li))
    10943360
    10943008
    140417189762736
    140417373204184
    140417198413960
    140417198413960
    140417198537224

    可以看出,当对象不可变时,重新赋值,地址已经改变了,也就是重新绑定"贴"到新的值上去了.

    当对象可变时,比如上例中的list,追加(append)一个元素后,地址并没变,只是在原来的基础上增加一个元素.

    下面那句

    li = [12,123,213,123,123]

    其实是重新创建了一个list,然后把li指向它,所以地址显然改变了.

    赋值和引用

    python中的变量名应该理解为标签,就是常说的引用.在赋值的时候总是把对象的引用值传给变量.

    a=12
    b=a
    print(id(a),id(b))
    b=13
    print(id(a),id(b))
    10943360 10943360
    10943360 10943392

    当对象不可变的时候,给已有变量赋值就会创建新对象,然后传新引用值给变量.而以往接触的程序语言总是地址不变,而改变其内的值.

    起初b=a,b获得了a的引用,所以ab的地址相同,而给b赋新值时,重新创建了对象,然后把b贴上去了,所以之后ab的地址不同.

    1 mylist= [12,13,1234]
    2 anotherlist = mylist
    3 anotherlist.append(999)
    4 print(mylist,id(mylist),id(anotherlist))
    5 del mylist[0]
    6 print(anotherlist,mylist)
    7 
    8 del anotherlist
    9 print(mylist,id(mylist))
    1 [12, 13, 1234, 999] 140417198414536 140417198414536
    2 [13, 1234, 999] [13, 1234, 999]
    3 [13, 1234, 999] 140417198414536

    当对象可变时,直接把一个list赋值给另一个list,传给那个新list的其实也是引用.

    所以mylist和anotherlist其实两者指向的是一个对象,

    然后给anotherlist追加一个元素,再删除首元素,由于指向一个对象,所以输出一样.

    最后删除anotherlist只是删除了这个标签而已,因为那个他们指向的对象还有mylist使用.(恩,这个网站确实有助于理解)

    拷贝

    那么究竟如何将一个对象像以前学的如c语言那样赋值是创建的独立的对(bian)象(liang)呢?

    在python中这中操作叫拷贝,拷贝又分两种,浅复制(copy),深复制(deepcopy).

    上面说了,直接赋值得到的是引用,要想拷贝就得使用别的操作,常见的有list的切片,list()方法,copy()方法.

    alist = [12,12,12,23,23213]
    blist = alist[:]
    clist = list(alist)
    from copy import copy
    dlist = copy(alist)
    print(alist==blist==clist)
    print(id(alist),id(blist),id(clist))
    alist.append(77)
    print(alist==blist==clist)
    print(id(alist),id(blist),id(clist))

    True
    140417198414216 140417198609096 140417189752712
    False
    140417198414216 140417198609096 140417189752712

    对象相等但是地址不同,说明确实是创建了新对象,之后给alist追加一个元素,三者不等.


    ==和is

    这里插播一下两者的区别,

    x = [1, 2, 3]
    y = [1, 2, 3]
    print(x == y)
    print(x is y)
    print(id(x))
    print(id(y))
    
    执行结果:
    True
    False
    2194144817928
    2194144817288

    ==比较的是对象是否相等,is 比较的是引用是否相等,x和y的对象都是1,2,3但是他们其实是不同的对象,只是值相同,然后x和y则是指向相等的不同的对象的不同的引用了.


    为什么说是浅拷贝呢?

    1 alist = [12,12,12,[12,23],23213]
    2 blist = alist[:]
    3 blist.append(777)
    4 blist[3][0]=21
    5 alist[3][1]=32
    6 print(alist,blist)
    [12, 12, 12, [21, 32], 23213] [12, 12, 12, [21, 32], 23213, 777]

    从输出的结果看到,无论是从alist中去更改alist[3]还是blist去更改blist[3],结果alist和blist的3号元素都改变了,这样的拷贝就叫浅拷贝,它只拷贝了最外面那层.

    当然看图片更容易懂了:

    从图片可以看到其它元素都是重新复制创建了一份,但是3号元素还是只是一个引用,和原来alist中那个引用相同.所以任意一个list去修改3号元素另一方会跟着改变.


    append和extend

    再插播一下这两者的区别:

    1 alist = [12,12,12,[12,23],23213]
    2 blist = list(alist)
    3 alist.append([12,777])
    4 blist.extend([12,777])
    5 print(alist)
    6 print(blist)
    [12, 12, 12, [12, 23], 23213, [12, 777]]
    [12, 12, 12, [12, 23], 23213, 12, 777]

    看输出就明白不同啦

    附:

    Help on method_descriptor:
    
    append(...)
        L.append(object) -> None -- append object to end
    
    Help on method_descriptor:
    
    extend(...)
        L.extend(iterable) -> None -- extend list by appending elements from the iterable

     再探深拷贝:

    1 alist = [12,12,[12,23],23213]
    2 blist = alist[:]
    3 from copy import deepcopy
    4 clist = deepcopy(alist)
    5 print(alist==blist==clist)
    6 print(id(alist),id(blist),id(clist))
    7 print(id(alist[2]),id(blist[2]),id(clist[2]))
    8 alist[2][0]=777
    9 print((alist),(blist),(clist))
    True
    140417189861576 140417189752008 140417189860744
    140417198413960 140417198413960 140417198612360
    [12, 12, [777, 23], 23213] [12, 12, [777, 23], 23213] [12, 12, [12, 23], 23213]

    blist由list()方法浅拷贝得来,clist由alist深拷贝而来,输出的第三行地址可见对于2号元素(一个子list)的地址,alist,blist相同,而clist和他们不同,是重新创建的.

    最后打印的结果也说明浅拷贝只是拷贝了外面那层,对于2号元素还是相同的引用.

    未完待续...... 

  • 相关阅读:
    设计模式课程 设计模式精讲 2-1 本章导航
    数字 日期 格式化方法
    jQuery事件委托之Safari失效的解决办法--摘抄
    css3鼠标点击穿透--摘抄
    字符串日期转换为周
    在echars上发布的半圆环形图
    一些框架源码中的代码
    webSQL 增删改查
    Android 根据版本号更新
    Android 永久保存简单数据
  • 原文地址:https://www.cnblogs.com/katachi/p/9571934.html
Copyright © 2011-2022 走看看