面试的时候,有没有被问到Python传参是传引用还是传值这种问题?有没有听到过Python传参既不是传值也不是传引用这种说法?一个小小的参数默认值也可能让代码出现难以查找的bug?

如果你也遇到过上面的问题,不妨我们来探究下Python函数传递的种种。

万物皆对象

Python中有一个非常重要的概念——万物皆对象,无论是一个数字、字符串,还是数组、字典,在Python中都会以一个对象的形式存在。

a = 123

对于上面这行代码,在Python看来就是创建一个PyObject对象,值为123,然后定义一个指针a,a指向这个PyObject对象。

可变对象和不可变对象

Python中的对象分为两种类型,可变对象和不可变对象,不可变对象指tuple、str、int等类型的对象,可变对象指的是dict、list、自定义对象等类型的对象,我们用一段代码说明他们的区别。

a = [1, 2, 3]
print(id(a)) # 2587116690248
a += [4]
print(id(a)) # 2587116690248 b = 1
print(id(b)) # 2006430784
b += 1
print(id(b)) # 2006430816

上面代码中我们分别定义了一个可变对象和一个不可变对象,并且对他们进行修改,打印修改前后的对象标识可以发现,对可变对象进行修改,变量对其引用不会发生变化,对不可变对象进行修改,变量引用发生了变化。

上图是一个可变对象,当修改对象时,例如删除数组中的一个元素,实际上把其中一个元素从对象中移除,对象本身的标识是不发生变化的。

改变一个不可变对象时,例如给一个int型加2,语法上看上去是直接修改了i这个对象,但是如前面所说,i只是一个指向对象73的一个变量,Python会将这个变量指向的对象加2后,生成一个新的对象,然后再让i指向这个新的对象。

参数传递时的表现

了解了对象的原理后,我们就可以来尝试理解一下参数传递时他们的不同表现了。

a = [1, 2, 3]
print(id(a)) # 1437494204232
def mutable(a):
print(id(a)) # 1437494204232
a += [4]
print(id(a)) # 1437494204232
mutable(a) b = 1
print(id(b)) # 2006430784
def immutable(b):
print(id(b)) # 2006430784
b += 1
print(id(b)) # 2006430816
immutable(b)

通过上面的代码可以看出,修改传进的可变参数时,会对外部对象产生影响,修改不可变参数时则不会影响。

概括地说,Python参数传递时,既不是传对象也不是传引用,之所以会有上述的区别,跟Python的对象机制有关,参数传递只是给对象绑定了一个新的变量(实际上是传递C中的指针)。

参数传递时的坑

理解了参数传递的逻辑,我们需要注意一下这种逻辑可能引发的问题。

def test(b=[]):
b += [1]
print(b) test() # [1]
test() # [1, 1]
test() # [1, 1, 1]

上面的代码的输出,按照可变对象传参的逻辑,应该每次调用都输出1才对,而实际输出看上去好像默认参数好像只生效了一次。原因在于Python的函数也是对象(万物皆对象),这个对象只初始化一次,加上参数又是不可变对象,所以每次调用实际上都修改的是一个对象。

解决这个问题,推荐再参数传递可变对象时,默认值设置为None,在函数内部对None进行判断后再赋予默认值。

def test(b=None):
b = b or []
b += [1]
print(b) test() # [1]
test() # [1]
test() # [1]

再看一段代码。

i = 1
def test(a=i):
print(a) i = 2
test() # 1

由于参数默认值是在函数定义时而不是函数执行时确定的,所以这段代码test方法的参数默认值时1而不是2。厦工叉车

Python参数传递,既不是传值也不是传引用的更多相关文章

  1. C语言中传值和C++的传引用

    在C语言中,传址其实也时传值的一种,首先地址其实也时可以看做是一个值来进行传递的. 在C++中有一种说法叫传引用,就是&变量名. 比如: /* * 传值 int a = 3; void fun ...

  2. Python学习笔记之函数参数传递 传值还是传引用

      在学完Python函数那一章节时,很自然的的就会想到Python中函数传参时传值呢?还是传引用?或者都不是? 在回答上面的问题之前我们先来看看下面的代码: 代码1: def foo(var): v ...

  3. 传值与传引用(C++)

    reference(引用) 是C++对C的一个扩充 int a; int &b = a;//声明b是一个整形的引用变量 C语言,函数的参数传递有2种形式:传值方式调用和传引用方式调用 传值方式 ...

  4. java 函数形参传值和传引用的区别

    java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清. (一)基本数据类型:传值,方法不会改变实参的值. public class TestFun { public static v ...

  5. python函数的参数传递问题---传值还是传引用?

    摘要:在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象.不可更改对象的传递属于传值,可更改对象属于传引用.想要在函数中传递 ...

  6. Python参数传递(传值&传引用)

    # 测试参数是传值还是传引用def test(arg): print("test before") print(id(arg)) arg[1]=30 # 测试可变对象 # arg[ ...

  7. python 参数传递 传值还是传引用

    个人推测结论: 可变对象传引用,不可变对象传值 python里的变量不同于c中地址储值模型 a=100 b=100 print(id(a),id(b),a==b,a is b) #8790877986 ...

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

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

  9. Python中参数是传值,还是传引用?

    在 C/C++ 中,传值和传引用是函数参数传递的两种方式,在Python中参数是如何传递的?回答这个问题前,不如先来看两段代码. 代码段1: def foo(arg): arg = 2 print(a ...

随机推荐

  1. crontab定时执行

    一.crond简介 crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动cro ...

  2. Kafka设计解析(十二)Kafka 如何读取offset topic内容 (__consumer_offsets)

    转载自 huxihx,原文链接 Kafka 如何读取offset topic内容 (__consumer_offsets) 众所周知,由于Zookeeper并不适合大批量的频繁写入操作,新版Kafka ...

  3. OpenGL 混合功能

    一.概念:简言之,即在颜色缓存区和深度缓存区中,新旧颜色的覆盖和替换问题:已经存在于缓存区的为目标颜色,即将进入缓存区的为源颜色: 二.应用场景:在不透明的图形前绘制一个透明的图形: 三.主要代码实现 ...

  4. 一、用Delphi10.3模拟读取百度网页,并读取相关头部信息

    一.读取网页的如下: uses TxHttp, Classes, TxCommon, Frm_WebTool, SysUtils; var m_Url: string; m_Http: TTxHttp ...

  5. Hbase的安装和基本使用

    Hbase介绍 HBase是一个开源的非关系型分布式数据库(NoSQL),它参考了谷歌的BigTable建模,实现的编程语言为 Java.它是Apache软件基金会的Hadoop项目的一部分,运行于H ...

  6. Kali渗透测试2-抓包/DNS工具

    转载请注明出处. TCPDUMP:命令行网络抓包工具tcpdump -h tcpdump version 4.9.2 libpcap version 1.8.1 OpenSSL 1.1.0h 27 M ...

  7. Go语言连接Oracle(就我这个最全)

    综合参考了网上挺多的方案 倒腾了半天终于连接好了 Go都出来这么多年了 还没有个Oracle的官方驱动... 过程真的很蛋疼..一度想放弃直接连ODBC 首先交代一下运行环境和工具版本: WIN10 ...

  8. python基础之import模块导入和包的调用

    模块概念 在Python中,一个.py文件就称之为一个模块(Module).使用模块组织代码,最大的好处是大大提高了代码的可维护性 模块一共三种:python标准库.第三方模块.应用程序自定义模块. ...

  9. c++ 创建二叉树

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> ...

  10. Linux部署python django程序-apache

    1.安装Apache 先卸载自带的httpd rpm -e httpd --nodeps 在网上下载四个文件 1.apr-1.4.6.tar.gz 2.apr-util-1.5.1.tar.gz 3. ...