在 C/C++ 中,传值和传引用是函数参数传递的两种方式,在Python中参数是如何传递的?回答这个问题前,不如先来看两段代码。

代码段1:

def foo(arg):
    arg = 2
    print(arg)

a = 1
foo(a)  # 输出:2
print(a) # 输出:1

看了代码段1的同学可能会说参数是值传递。

代码段2:

def bar(args):
    args.append(1)

b = []
print(b)# 输出:[]
print(id(b)) # 输出:4324106952
bar(b)
print(b) # 输出:[1]
print(id(b))  # 输出:4324106952

看了代码段2,这时可能又有人会说,参数是传引用,那么问题来了,参数传递到底是传值还是传引用或者两者都不是?为了把这个问题弄清楚,先了解 Python 中变量与对象之间的关系。

变量与对象

Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。例如,[]是一个空列表对象,变量 a 是该对象的一个引用

a = []
a.append(1)

在 Python 中,「变量」更准确叫法是「名字」,赋值操作 = 就是把一个名字绑定到一个对象上。就像给对象添加一个标签。

a = 1

整数 1 赋值给变量 a 就相当于是在整数1上绑定了一个 a 标签。

a = 2

整数 2 赋值给变量 a,相当于把原来整数 1 身上的 a 标签撕掉,贴到整数 2 身上。

b = a

把变量 a 赋值给另外一个变量 b,相当于在对象 2 上贴了 a,b 两个标签,通过这两个变量都可以对对象 2 进行操作。

变量本身没有类型信息,类型信息存储在对象中,这和C/C++中的变量有非常大的出入(C中的变量是一段内存区域)

函数参数

Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,清楚了赋值和参数传递的本质之后,现在再来分析前面两段代码。

def foo(arg):
    arg = 2
    print(arg)

a = 1
foo(a)  # 输出:2
print(a) # 输出:1

在代码段1中,变量 a 绑定了 1,调用函数 foo(a) 时,相当于给参数 arg 赋值 arg=1,这时两个变量都绑定了 1。在函数里面 arg 重新赋值为 2 之后,相当于把 1 上的 arg 标签撕掉,贴到 2 身上,而 1 上的另外一个标签 a 一直存在。因此 print(a) 还是 1。

再来看一下代码段2

def bar(args):
    args.append(1)

b = []
print(b)# 输出:[]
print(id(b)) # 输出:4324106952
bar(b)
print(b) # 输出:[1]
print(id(b))  # 输出:4324106952

执行 append 方法前 b 和 arg 都指向(绑定)同一个对象,执行 append 方法时,并没有重新赋值操作,也就没有新的绑定过程,append 方法只是对列表对象插入一个元素,对象还是那个对象,只是对象里面的内容变了。因为 b 和 arg 都是绑定在同一个对象上,执行 b.append 或者 arg.append 方法本质上都是对同一个对象进行操作,因此 b 的内容在调用函数后发生了变化(但id没有变,还是原来那个对象)

最后,回到问题本身,究竟是是传值还是传引用呢?说传值或者传引用都不准确。非要安一个确切的叫法的话,叫传对象(call by object)。如果作为面试官,非要考察候选人对 Python 函数参数传递掌握与否,与其讨论字面上的意思,还不如来点实际代码。

show me the code

def bad_append(new_item, a_list=[]):
    a_list.append(new_item)
    return a_list

这段代码是初学者最容易犯的错误,用可变(mutable)对象作为参数的默认值。函数定义好之后,默认参数 a_list 就会指向(绑定)到一个空列表对象,每次调用函数时,都是对同一个对象进行 append 操作。因此这样写就会有潜在的bug,同样的调用方式返回了不一样的结果。

>>> print bad_append('one')
['one']
>>> print bad_append('one')
['one', 'one']

而正确的方式是,把参数默认值指定为None

def good_append(new_item, a_list=None):
    if a_list is None:
        a_list = []
    a_list.append(new_item)
    return a_list

同步于:https://foofish.net/python-function-args.html

公众号『Python之禅』(id:vttalk)

Python中参数是传值,还是传引用?的更多相关文章

  1. JAVA方法传递参数:传值?传引用?

    先来看下面这三段代码: //Example1: public class Example1 { static void check(int a) { a++; } public static void ...

  2. Java:方法的参数是传值还是传引用

    Java中方法的参数总是采用传值的方式. 下列方法欲实现对象的交换,但实际上是不能实现的. public void swap(simpleClass a,simpleClass b){ simpleC ...

  3. java集合中的传值和传引用

    在学习java集合过程中发现了传值和传引用的区别: 我们来看下面两句话 ●java集合就像一种容器,我们可以把多个对象(实际上是对象的引用),丢进该容器.(来自疯狂java讲义) ●当使用Iterat ...

  4. python中给函数传参是传值还是传引用

    首先还是应该科普下函数参数传递机制,传值和传引用是什么意思? 函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题.基本的参数传递机制有两种:值传递和引用传 ...

  5. python传参是传值还是传引用

    在此之前先来看看变量和对象的关系:Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象.而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的.例 ...

  6. java中的传值与传引用

    java函数中的传值和传引用问题一直是个比较“邪门”的问题,其实java函数中的参数都是传递值的,所不同的是对于基本数据类型传递的是参数的一份拷贝,对于类类型传递的是该类参数的引用的拷贝,当在函数体中 ...

  7. python函数传参是传值还是传引用?

    首先还是应该科普下函数参数传递机制,传值和传引用是什么意思? 函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题.基本的参数传递机制有两种:值传递和引用传 ...

  8. c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)

       一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会 ...

  9. C++ 参数传值 与 传引用

    参数传值 在 C++ 中,函数参数的传递有两种方式:传值和传引用.在函数的形参不是引用的情况下,参数传递方式是传值的.传引用的方式要求函数的形参是引用.“传值”是指,函数的形参是实参的一个拷贝,在函数 ...

随机推荐

  1. 部署JForum 2.1.9遇到的问题及解决方法

    1. 主要问题是出在连接数据库和创建表阶段,当我们配置好MySQL的各种参数后,创建表的时候会报错: 原因:主要是由于建表的SQL语句和MySQL的版本不一致导致的. 解决办法:简单来说,在MYSQL ...

  2. Docker环境中部署OwnCloud 9.0

    整体思路: 1.官方获取mysql.php+apache镜像: 2.基于php+apache,创建OwnCloud镜像: 3.启动mysql镜像: 4.启动OwnCloud镜像,链接mysql镜像,访 ...

  3. (五)Hololens Unity 开发之 手势识别

    学习源于官方文档 Gestures in Unity 笔记一部分是直接翻译官方文档,部分各人理解不一致的和一些比较浅显的保留英文原文 (五)Hololens Unity 开发之 手势识别 HoloLe ...

  4. WinForm 更换主窗体的例子

    做一个登录窗口,登录成功时关闭form1,展示from2界面 1.主界面Login namespace WindowsFormsApplication1 { public partial class ...

  5. HDU 3782 xxx定律

    xxx定律 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  6. ArcGIS制图表达Representation-规则和几何效果

    ArcGIS制图表达Representation-规则和几何效果 by 李远祥 对制图表达运用是否纯熟,主要是看对制图表达规则的理解和巧妙运用.前面章节已经粗略介绍过制图表达的运用,通过一些简单的规则 ...

  7. Jenkins的插件

    Jenkins不仅自己为大家提供了很多功能,而且还支持插件.用户可以根据自己的需要安装插件,或者是自己开发插件. 这里说一下Jenkins的插件的安装方法: 首先,打开Jenkins首页,选择Jenk ...

  8. NSMutableAttributedString

    开发过程中,经常会遇到动态计算行高的问题, - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)op ...

  9. IIS7.0发布后关于"不能在此路径中使用此配置节”的解决办法

    在系统为window sever2008,iis7.0上安装后发布出现 IIS Web Core 通知 BeginRequest 处理程序 尚未确定 错误代码 0x80070021 配置错误 不能在此 ...

  10. 使用SBT编译Spark子项目

    前言 最近为了解决Spark2.1的Bug,对Spark的源码做了不少修改,需要对修改的代码做编译测试,如果编译整个Spark项目快的话,也得半小时左右,所以基本上是改了哪个子项目就单独对那个项目编译 ...