首先还是应该科普下函数参数传递机制传值和传引用是什么意思?

   函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递机制有两种:值传递和引用传递。

  值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

  引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

在python中实际又是怎么样的呢?

先看一个简单的例子:

from ctypes import *
import os.path
import sys def test(c):
print "test before "
print id(c)
c+=2
print "test after +"
print id(c)
return c def printIt(t):
for i in range(len(t)):
print t[i] if __name__=="__main__":
a=2
print "main before invoke test"
print id(a)
n=test(a)
print "main afterf invoke test"
print a
print id(a)

运行后结果如下:

>>>
main before invoke test
39601564
test before
39601564
test after +
39601540
main afterf invoke test
2
39601564

id函数可以获得对象的内存地址.很明显从上面例子可以看出,将a变量作为参数传递给了test函数,传递了a的一个引用,把a的地址传递过去了,所以在函数内获取的变量C的地址跟变量a的地址是一样的,但是在函数内,对C进行赋值运算,C的值从2变成了4,实际上2和4所占的内存空间都还是存在的,赋值运算后,C指向4所在的内存。而a仍然指向2所在的内存,所以后面打印a,其值还是2.

如果还不能理解,先看下面例子

>>> a=1
>>> b=1
>>> id(a)
40650152
>>> id(b)
40650152
>>> a=2
>>> id(a)
40650140

a和b都是int类型的值,值都是1,而且内存地址都是一样的,这已经表明了在python中,可以有多个引用指向同一个内存(画了一个很挫的图,见谅),在给a赋值为2后,再次查看a的内存地址,都已经变化了

而基于最前面的例子,大概可以这样描述:

那python函数传参就是传引用?然后传参的值在被调函数内被修改也不影响主调函数的实参变量的值?再来看个例子。

from ctypes import *
import os.path
import sys def test(list2):
print "test before "
print id(list2)
list2[1]=30
print "test after +"
print id(list2)
return list2 def printIt(t):
for i in range(len(t)):
print t[i] if __name__=="__main__":
list1=["loleina",25,'female']
print "main before invoke test"
print id(list1)
list3=test(list1)
print "main afterf invoke test"
print list1
print id(list1)

实际值为:

>>>
main before invoke test
64129944
test before
64129944
test after +
64129944
main afterf invoke test
['loleina', 30, 'female']
64129944

发现一样的传值,而第二个变量居然变化,为啥呢?

实际上是因为python中的序列:列表是一个可变的对象,就基于list1=[1,2] list1[0]=[0]这样前后的查看list1的内存地址,是一样的。

>>> list1=[1,2]
>>> id(list1)
64185208
>>> list1[0]=[0]
>>> list1
[[0], 2]
>>> id(list1)
64185208

结论:python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值'来传递对象。

原文链接:https://www.cnblogs.com/loleina/p/5276918.html

附:https://foofish.net/python-function-args.html

再附:https://www.jb51.net/article/127667.htm这篇文章的结论不完全对,文章中的不可变对象使用了整数,得出了不可变对象共享这个结论,是错误的,他忽略了小整数池的概念。

python中给函数传参是传值还是传引用的更多相关文章

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

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

  2. Python中的传参是传值还是传址?

    传值:在C++中,传值就是把一个参数的值给这个函数,其中的更改不会影响原来的值. 传址:即传引用,直接把这个参数的内存地址传递进去,直接去这个内存地址上进行修改. 但是这些在Python中都没有,Py ...

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

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

  4. java方法中,传参是传值还是传址问题(对比C语言、C#和C++)

    问题引出: 编写一个简单的交换值的小程序,如果我们只是简单地定义一个交换函数接收两个数,在函数内部定义一个中间变量完成交换.那么当我们把a,b两个实参传给这个函数时,往往得不到预期的结果.这是为什么呢 ...

  5. Python中匿名函数与内置高阶函数详解

    大家好,从今天起早起Python将持续更新由小甜同学从 初学者的角度 学习Python的笔记,其特点就是全文大多由 新手易理解 的 代码与注释及动态演示 .刚入门的读者千万不要错过! 很多人学习pyt ...

  6. Python中利用函数装饰器实现备忘功能

    Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下   " ...

  7. [19/10/13-星期日] Python中的函数

    一.函数 # 第五章 函数 ## 函数简介(function) - 函数也是一个对象 - 对象是内存中专门用来存储数据的一块区域 - 函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行 ...

  8. 『无为则无心』Python函数 — 25、Python中的函数

    目录 1.函数的使用 (1)定义函数 (2)调用函数 (3)使用函数的注意事项 2.函数的参数 3.实参的类型 Python函数的说明: Python中函数的应用非常广泛,前面章节中我们已经接触过多个 ...

  9. Python中split()函数的用法及实际使用示例

    Python中split()函数,通常用于将字符串切片并转换为列表. 一.函数说明: split():语法:str.split(str="",num=string.count(st ...

随机推荐

  1. ArrayList、LinkedList区别(jdk8)

    /** * jdk8 * ArrayList:底层动态数组实现(未初始化指定数组长度) * add():添加元素时,才初始化数组长度为10.容量不够时,动态扩容策略为: 原容量 + 原容量*0.5 * ...

  2. 遇到的基础php函数、方法

    0x01 PHP file() 函数 file() 函数把整个文件读入一个数组中. 数组中的每个元素都是文件中相应的一行,包括换行符在内. 实例: <?php print_r(file(&quo ...

  3. java中拦截器与过滤器

    注:文摘自网络,仅供自己参考 1.首先要明确什么是拦截器.什么是过滤器 1.1 什么是拦截器: 拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之 ...

  4. 深入浅出聊一聊Docker

    网易云信IM私有化部分用到Docker技术,今天我们就深入浅出来聊聊Docker. Docker是什么? Docker是一个工具,能把应用打包部署于container里,这里可以把container看 ...

  5. eclipse如何查看源码

    方式一: Source not found The JAR file X:\xxxx\xxxx\xxxx\xx has no source attachment. 没有源附件. You can att ...

  6. MySQL 8.0.18 在 Windows Server 2019 上的安装(ZIP)公开

    AskScuti MySQL : Windows Server 2019 安装 MySQL 8.0 温馨提示:为了展现我最“魅力”的一面,请用谷歌浏览器撩我. 一切就绪,点我开撩

  7. Response与ServletContext对象

    HTTP协议: 请求消息:客户端发送给服务器端的数据 数据格式: 请求行: 格式: 请求方式 请求url 请求协议/版本 请求头:告诉服务器,当前访问的浏览器自身的一些信息 格式: 请求头名称: 请求 ...

  8. SpringBoot框架---配置

    1.更改tomcat的端口号: application.properties 配置文件中, server.port=9090 2.设置上下文路径: server.servlet.context-pat ...

  9. STM 32 内部功能回顾

    EXTI   外部中断 NVIC 嵌套的向量式中断控制器 AHB 是高级高性能内部总线,主要是用在CPU.DMA.DSP(数字信号处理) APB 是外围总线,I2C. 串口 APB 分为高速APB2( ...

  10. asp.net mvc获取http body中的json

    今天遇到一个对方直接post 一共json对象字符串过来的场景.按照以前的旧方案方法可以直接在request.inputstream里面读取,然后反序列化即可.(当然估计ASP.NET MVC应该有更 ...