专栏首页JetpropelledSnakePython学习笔记之函数参数传递 传值还是传引用

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

在学完Python函数那一章节时,很自然的的就会想到Python中函数传参时传值呢?还是传引用?或者都不是? 

在回答上面的问题之前我们先来看看下面的代码:

代码1:

def foo(var):
    var = 2
    print(var)  #output: 2
a = 1
foo(a)
print(a)       #output: 1

恩,看似是值传递 代码2:

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

b = []
print(b) #output:[]
bar(b)
print(b) #output:[1]

应该是引用传递?有点奇怪吧,为了弄清楚这个问题,我们先来了解一下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

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

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

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

a = 1     # a指向内存中一个int型对象  
a = 2     # 重新赋值  

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

示例2

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

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

现在我们再来看看开始那两段代码:

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

a = 1  
foo(a)    
print(a)   

上面这段代码把a作为参数传递给函数,这时avar都指向内存中值为1的对象。然后在函数中var = 2时,因为int对象不可改变,于是创建一个新的int对象(值为2)并且令var指向它。而a仍然指向原来的值为1int对象,所以函数没有改变变量a。 如下图:

代码2;

def Bar(var):  
   var.append(1)  

b = []  
print(b)   
Bar(b)  
print(b)   

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

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

参考

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 前端学习笔记之JavaScript

    尽管 ECMAScript 是一个重要的标准,但它并不是 JavaScript 唯一的部分,当然,也不是唯一被标准化的部分。实际上,一个完整的 JavaScri...

    Jetpropelledsnake21
  • 前端学习笔记之ES6快速入门

    ES6新增了let命令,用于声明变量。其用法类似var,但是声明的变量只在let命令所在的代码块内有效。

    Jetpropelledsnake21
  • SQL学习笔记之MySQL中真假“utf8” 问题

    最近我遇到了一个 bug,我试着通过 Rails 在以“utf8”编码的 MariaDB 中保存一个 UTF-8 字符串,然后出现了一个离奇的错误:

    Jetpropelledsnake21
  • AS3性能优化

    本篇文章用来总结本人对AS3性能优化方面的认识及经验,可能会有一些错误,敬请不吝赐教.如果想了解更多,请参考ADOBE方面的相关只是介绍.

    py3study
  • hexo的图片和视频显示

    之前一番因为买了阿里云和域名,而且备了案。于是在阿里云上重新部署了博客,同样用的hexo。

    efonfighting
  • 当Kotlin遇见数据结构丨使用哈夫曼编码压缩文件

    哈夫曼编码是一种编码格式,属于可变字长编码的一种,该方法依照字符出现的概率来构建异字头的平均长度最短的码字,最终实现根据使用频率来最大化节省码字(字符)的存储空...

    码脑
  • ManagementClass类解析和C#如何获取硬件的相关信息

           在.NET的项目中,有时候需要获取计算机的硬件的相关信息,在C#语言中需要利用ManagementClass这个类来进行相关操作。     ...

    彭泽0902
  • 我要脱单!在微信这样表白,谁都不会拒绝你

    当你发自内心地喜欢 ta,从眼神笑意,到肢体语言,浑身上下每一个细胞,无一不散发着爱的讯息,想藏也藏不住。可是由于种种原因,生活中的你我,往往爱在心头口难开。

    知晓君
  • 教你如何推广网站、增加流量的4大方法

    流量是做网站成功的关键。自从有了智能企业建站系统后(如新万云企业美站等),做网站不再难,然而如何在众多同类网站中脱颖而出,获得更多流量呢?下面,新万网络给大家分...

    新万网络
  • CVPR 2019 | 亮风台推出全球最大单目标跟踪数据集 LaSOT

    包含超过 352 万帧手工标注的图片和 1400 个视频,也是目前为止最大的拥有密集标注的单目标跟踪数据集。

    AI科技评论

扫码关注云+社区

领取腾讯云代金券