zoukankan      html  css  js  c++  java
  • Python FAQ1:传值,还是传引用?

    在C/C++中,传值和传引用是函数参数传递的两种方式。由于思维定式,从C/C++转过来的Python初学者也经常会感到疑惑:在Python中,函数参数传递是传值,还是传引用呢?

    看下面两段代码:

    def foo(arg):
    	arg = 5
    	print(arg)
    
    x = 1
    foo(x)    # 输出5
    print(x)  # 输出1
    def foo(arg):
    	arg.append(3)
    
    x = [1, 2]
    print(x)   # 输出[1, 2]
    foo(x)
    print(x)   # 输出[1, 2, 3]
    看完第一段代码,会有人说这是值传递,因为函数并没有改变x的值;看完第二段代码,又会有人说这是传引用,因为函数改变了x的内容。

    那么,Python中的函数到底是传值还是传引用呢?看下面的解释。


    一、变量和对象

    我们需要先知道Python中的“变量”与C/C++中“变量”是不同的。

    在C/C++中,当你初始化一个变量时,就是声明一块存储空间并写入值。相当于把一个值放入一个盒子里

    int a = 1;

    现在”a”盒子里放了一个整数1,当给变量a赋另外一个值时会替换盒子a里面的内容:

    a = 2;


    当你把变量a赋给另外一个变量时,会拷贝a盒子中的值并放入一个新的“盒子”里:

    int b = a;


    在Python中,一个变量可以说是内存中的一个对象的“标签”或“引用”

    a = 1


    现在变量a指向了内存中的一个int型的对象(a相当于对象的标签)。如果给a重新赋值,那么“标签”a将会移动并指向另一个对象:

    a = 2


    原来的值为1的int型对象仍然存在,但我们不能再通过a这个标识符去访问它了(当一个对象没有任何标签或引用指向它时,它就会被自动释放)。如果我们把变量a赋给另一个变量,我们只是给当前内存中对象增加一个“标签”而已:

    b = a

    综上所述,在Python中变量只是一个标签一个标识符,它指向内存中的对象。故变量并没有类型,类型是属于对象的,这也是Python中的变量可以被任何类型赋值的原因。


    二、可变对象与不可变对象

    Python的基本数据类型中,我们知道numbers、strings和tuples是不可更改的对象,而list、dict是可以修改的对象。那么可变与不可变有什么区别呢?看下面示例:

    a = 1     # a指向内存中一个int型对象
    a = 2     # 重新赋值
    当将a重新赋值时,因为原来值为1的对象是不能改变的,所以a会指向一个新的int对象,其值为2。(如上面的图示)

    lst = [1, 2]   # lst指向内存中一个list类型的对象
    lst[0] = 2     # 重新赋值lst中第一个元素

    因为list类型是可以改变的,所以第一个元素变更为2。更确切的说,lst的第一个元素是int型,重新赋值时一个新的int对象被指定给第一个元素,但是对于lst来说,它所指的列表型对象没有变,只是列表的内容(其中一个元素)改变了。

    好了,到这里我们就很容易解释本文开头的两段代码了:

    def foo(arg):
    	arg = 5
    	print(arg)
    
    x = 1
    foo(x)    # 输出5
    print(x)  # 输出1
    上面这段代码把x作为参数传递给函数,这时x和arg都指向内存中值为1的对象。然后在函数中arg = 5时,因为int对象不可改变,于是创建一个新的int对象(值为5)并且令arg指向它。而x仍然指向原来的值为1的int对象,所以函数没有改变x变量。

    def foo(arg):
    	arg.append(3)
    
    x = [1, 2]
    print(x)   # 输出[1, 2]
    foo(x)
    print(x)   # 输出[1, 2, 3]

    这段代码同样把x传递给函数foo,那么x和arg都会指向同一个list类型的对象。因为list对象是可以改变的,函数中使用append在其末尾添加了一个元素,list对象的内容发生了改变,但是x和arg仍然是指向这一个list对象,所以变量x的内容发生了改变。

    那么Python中参数传递是传值,还是传引用呢?准确的回答:都不是。之所以不是传值,因为没有产生复制,而且函数拥有与调用者同样的对象。而似乎更像是C++的传引用?但是有时却不能改变实参的值。只能这样说:对于不可变的对象,它看起来像C++中的传值方式;对于可变对象,它看起来像C++中的按引用传递。




    附: Everything is Object in Python

    Python使用对象模型来储存数据,任何类型的值都是一个对象。所有的python对象都有3个特征:身份、类型和值。

    • 身份:每一个对象都有自己的唯一的标识,可以使用内建函数id()来得到它。这个值可以被认为是该对象的内存地址。
    • 类型:对象的类型决定了该对象可以保存的什么类型的值,可以进行什么操作,以及遵循什么样的规则。type()函数来查看python 对象的类型。
    • :对象表示的数据项。

    >>> a = 1
    >>> id(a)
    140068196051520
    >>> b = 2
    >>> id(b)
    140068196051552
    >>> c = a
    >>> id(c)
    140068196051520
    >>> c is a
    True
    >>> c is not b
    True

    运算符isis not就是通过id()的返回值(即身份)来判定的,也就是看它们是不是同一个对象的“标签”。

    (全文完)







  • 相关阅读:
    懒加载——实现原理
    html5shiv.js和respond.min.js
    点击app分享链接,js判断手机是否安装某款app,有就尝试打开,没有就下载
    ajax获取后台数据渲染(整片文章不分段落)解决方案,要使用htmL方式输出
    +-下拉菜单
    html 中a标签的问题(无反应,跳转,调用方法)
    js中两种定时器,setTimeout和setInterval的区别
    chrome 调试进入 paused in debugger 状态解决办法
    mybatis-plus 获取新增id
    linux unzip和zip
  • 原文地址:https://www.cnblogs.com/songlee/p/5738119.html
Copyright © 2011-2022 走看看