python 传值 传引用函数的参数传递是传值还是传引用

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
标签:至少1个,最多5个
最近遇到个有趣的问题:“JS中的值是按值传递,还是按引用传递呢?”
在分析这个问题之前,我们需了解什么是按值传递(call by value),什么是按引用传递(call by reference)。在计算机科学里,这个部分叫求值策略(Evaluation Strategy)。它决定变量之间、函数调用时实参和形参之间值是如何传递的。
按值传递 VS. 按引用传递
按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。修改形参的值并不会影响实参。
按引用传递(call by reference)时,函数的形参接收实参的隐式引用,而不再是副本。这意味着函数形参的值如果被修改,实参也会被修改。同时两者指向相同的值。
按引用传递会使函数调用的追踪更加困难,有时也会引起一些微妙的BUG。
按值传递由于每次都需要克隆副本,对一些复杂类型,性能较低。两种传值方式都有各自的问题。
探究JS值的传递方式
JS的基本类型,是按值传递的。
var a = 1;
function foo(x) {
console.log(a); // 仍为1, 未受x = 2赋值所影响
再来看对象:
var obj = {x : 1};
function foo(o) {
console.log(obj.x); // 3, 被修改了!
说明o和obj是同一个对象,o不是obj的副本。所以不是按值传递。 但这样是否说明JS的对象是按引用传递的呢?
我们再看下面的例子:
var obj = {x : 1};
function foo(o) {
console.log(obj.x); // 仍然是1, obj并未被修改为100.
如果是按引用传递,修改形参o的值,应该影响到实参才对。但这里修改o的值并未影响obj。 因此JS中的对象并不是按引用传递。那么究竟对象的值在JS中如何传递的呢?
按共享传递 call by sharing
准确的说,JS中的基本类型按值传递,对象类型按共享传递的(call by sharing,也叫按对象传递、按对象共享传递)。最早由Barbara Liskov. 在1974年的GLU语言中提出。该求值策略被用于Python、Java、Ruby、JS等多种语言。
该策略的重点是:调用函数传参时,函数接受对象实参引用的副本(既不是按值传递的对象副本,也不是按引用传递的隐式引用)。 它和按引用传递的不同在于:在共享传递中对函数形参的赋值,不会影响实参的值。如下面例子中,不可以通过修改形参o的值,来修改obj的值。
var obj = {x : 1};
function foo(o) {
console.log(obj.x); // 仍然是1, obj并未被修改为100.
然而,虽然引用是副本,引用的对象是相同的。它们共享相同的对象,所以修改形参对象的属性值,也会影响到实参的属性值。
var obj = {x : 1};
function foo(o) {
console.log(obj.x); // 3, 被修改了!
对于对象类型,由于对象是可变(mutable)的,修改对象本身会影响到共享这个对象的引用和引用副本。而对于基本类型,由于它们都是不可变的(immutable),按共享传递与按值传递(call by value)没有任何区别,所以说JS基本类型既符合按值传递,也符合按共享传递。
var a = 1; // 1是number类型,不可变 var b = b = 6;
据按共享传递的求值策略,a和b是两个不同的引用(b是a的引用副本),但引用相同的值。由于这里的基本类型数字1不可变,所以这里说按值传递、按共享传递没有任何区别。
基本类型的不可变(immutable)性质
基本类型是不可变的(immutable),只有对象是可变的(mutable). 例如数字值100, 布尔值true, false,修改这些值(例如把1变成3, 把true变成100)并没有什么意义。比较容易误解的,是JS中的string。有时我们会尝试“改变”字符串的内容,但在JS中,任何看似对string值的”修改”操作,实际都是创建新的string值。
var str = "abc";
str[0]; // "a"
str[0] = "d";
// 仍然是"abc";赋值是无效的。没有任何办法修改字符串的内容
而对象就不一样了,对象是可变的。
var obj = {x : 0};
obj.x = 100;
obj.x; // 1, 被修改
obj.x; // 1, 不会因o = true改变
这里定义变量obj,值是object,然后设置obj.x属性的值为100。而后定义另一个变量o,值仍然是这个object对象,此时obj和o两个变量的值指向同一个对象(共享同一个对象的引用)。所以修改对象的内容,对obj和o都有影响。但对象并非按引用传递,通过o = true修改了o的值,不会影响obj。
7 收藏&&|&&38
你可能感兴趣的文章
1 收藏,1.8k
3 收藏,1k
8 收藏,4.8k
本作品采用 署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
js种,参数传递只有一种规则:按值传递,基于值的复制。原始类型复制的是值本身,所以这两份数据互不影响;引用类型复制的是引用值,所以形参和实参指向同一个对象,通过一个饮用修改了对象,那么通过另外一个引用访问的对象就是修改后的对象
js种,参数传递只有一种规则:按值传递,基于值的复制。原始类型复制的是值本身,所以这两份数据互不影响;引用类型复制的是引用值,所以形参和实参指向同一个对象,通过一个饮用修改了对象,那么通过另外一个引用访问的对象就是修改后的对象
这么总结更好记了,都tm按值传递,引用类型等于复制指针。哈哈
这么总结更好记了,都tm按值传递,引用类型等于复制指针。哈哈
博主,按共享传递可否理解为按对象的地址传递,函数的形参等于把对象的地址复制了一份给了形参?
博主,按共享传递可否理解为按对象的地址传递,函数的形参等于把对象的地址复制了一份给了形参?
分享到微博?
我要该,理由是:python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变 - CSDN博客
python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变
函数参数传递本质上和变量整体复制一样,只是两个变量分别为形参a和实参b。那么,a=b后,a变了,b值是否跟着变呢?这取决于对象内容可变不可变
首先解释一下,什么是python对象的内容可变不可变?
python的变量是无类型的,如n=1&& #变量n无类型(n相当于指针),其指向int数据类型的值,这个值是int类型。
所以,python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。
& & & &举个列子,
& & 不可变如,a=5后,a=10,这里实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
& & & & 而可变如,la=[1,2,3,4]后,la[2]=5则是将list la的第二个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
那么,python函数的参数传递:
&&&&&&& 可变类型,则类似c++的引用,如list、dict。如fun(la),则是将la真正的传过去,修改后fun外部的la也会受影响
& & & & 而不可变类型,则类似c++的值传递,如int。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
讲的很细:
同样的道理,python变量复制也是一样,a=b:
变量间复制,可变对象是引用,不可变是值copy(新生成值空间,不是变量对象空间)
样例代码如下:
& & & & &a={1:'a',2:'b',3:'c'}
& & & & &b=a
& & & & &a[4]='d'
& & & & &print a,b
#输出:{1: 'a', 2: 'b', 3: 'c', 4: 'd'} {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
& & & & a=10
& & & & b=a
& & & & a=6
& & & & print a,b
#输出:&6 10
本文已收录于以下专栏:
相关文章推荐
一直对python函数中的参数关系
和其他语言不一样,传递参数的时候,python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。
实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是...
一: 如何在两个类A和B中传递Value??
 (1)方法一:
 两个类中都有这个要传递的value成员变量。
 均有getValue(),setValue() 函数。
 从一个类...
从python中copy与deepcopy的区别看python引用
撰写于  分类: python 标签: python copy
概要:昨天遇到一个关于copy与de...
新手的困惑,python函数参数到底是值引用还是地址引用?为什么函数内部修改一个变量外部输入却没改变?
1. 命名规范:
    1) 类:由多个单词连缀而成,单词之间不要分隔符,每个单词首字母大写,其余小写;
    2) 数据成员:应该是名词,多个单词连缀而成,首字母小写,其余单词首字母大写,其余小...
python3 不允许我们选择参数传递方式,但是他已经全部采用了引用传递。
相关测试代码与解释贴在文章里了。
我们先来看两个例子例子1例子2分析刚刚进入函数体内是,外部x以及内部x引用同一个对象。当对内部x引用的对象(尾部x也在引用这个对象)进行修改时差别就出现了:
- 对于例子1,执行x = 100。在此...
#_*_coding:UTF-8_*_
# 1.默认参数
# 函数的参数支持默认。当某个参数没有传递实际值时,函数将使用默认参数计算。例如,可以向login()函数的username参数和passwo...
他的最新文章
讲师:宋宝华
讲师:何宇健
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
四 传参传递类型#默认参数(其默认值可以是变量,但此变量必须之前有定义)#但在PHP中默认值必须是常量表达式,不能是诸如变量,类成员,或者函数调用等表达式。a = 10def test(t = a):& & return ta = 5print test()&#这里输出为10&#可变长参数(数量可变)#最后一位参数数量不定,加上*号就可接受任意数量的参数#数量不定参数都作为一个元组放入args变量中,然后传递给函数&#PHP中可在函数内使用func_get_args()来获取不定数量的参数&def test2(a,b,*args):& & print args #这里输出(3, 4, 5, 6, 7, 8)test2(1,2,3,4,5,6,7,8)& &&#关键字参数#这时候参数的位置与顺序就无关紧要了。def test3(a,b,c):& & print '%s,%s,%s'%(a,b,c) #输出aa,bb,cctest3(c='cc',b='bb',a='aa')&#多个不定关键字参数#最后一个参数以**开头,可以把所有额外的关键字参数都入到一个#字典中并将此字典传递给函数def test4(**agrs):& & print agrstest4(c='cc',b='bb',a='aa',d='dd',e='ee')#输出{'a': 'aa', 'c': 'cc', 'b': 'bb', 'e': 'ee', 'd': 'dd'}&#可变长参数与不定关键字参数def test5(*args,**kargs):& & print args #输出(1, 2, 3, 4, 5)& & print kargs #输出{'a': 'aa', 'c': 'cc', 'b': 'bb', 'e': 'ee', 'd': 'dd'}test5(1,2,3,4,5,c='cc',b='bb',a='aa',d='dd',e='ee')五 函数其它& &#闭包&函数在python中是第一类对象,可以把它们当作参数传递给其他函数,放在数据结构中,以及作为函数结果返回。& &将组成函数的语句和这些语句的执行环境打包在一起时,得到的对象称为闭包。def test(i):
def go(n):
print locals() #使用locals()函数可打印出当前的局部变量 {'i': 1, 'n': 1}
return i+n
return go#只创建和返回函数go(),并且隐式地携带创建go()函数时定义的外部变量的值one = test(1)print one #&function go at 0x011F5430&two = test(2)#执行真正的功能函数print one(1) #2print two(2) #4#更多时候结合 lambda 使用def test(x):
return lambda y:x*y #lambda 参数列表:运算主体one = test(1)print one #&function &lambda& at 0x011F53F0&two = test(2)print one(2) #2print two(3) #6另外注意:在python2中闭包只能读外部函数的变量,而不能修改。在python3中增加了一个nonlocal 关键字来实现这功能。def test(n):
def one():
return onet = test(2)当我们调用时t(),就会报错UnboundLocalError: local variable 'n' referenced before assignment& #yield 生成器对象&生成器是一个函数,它生成一个值的序列,以便在迭代中使用。&#coding:utf-8'''yield 生成器对象:生成器是一个函数,它生成一个值的序列,以便在迭代中使用。'''def test(n):
while n&0:
return#返回一个生成器对象c = test(10)#使用next()执行函数(python3中为__next__())c.next()'''&&& 10&&& c.next()9&&& c.next()8&&& c.next()7&&&'''调用next()时,生成器函数将不断执行语句,直到遇到yield语句停止,yield语句在函数执行停止的地方生成一个结果,直到再次调用next(),然后继续执行yield之后的语句。更多参考:手册。附#计算阶乘 n*n-1*n-2...*1#方法一def fact1(n):& & if n==0:& & & & return 1& & return n*fact1(n-1)#方法二()def fact2(n):& & result = 1& & for i in xrange(n,1,-1):& & & & result *= i& & return resultn = 6print fact1(n)print fact2(n)&?php//PHP简单无级分类(注意:在设计数据库时,为了性能的提升,可以记录其完整的路径)$a = array( array(
'chil'=&array(
'chil'=&null
) ), array(
'chil'=&array(
'chil'=&null
) ), array(
'chil'=&array(
'chil'=&array(
'chil'=&null
'chil'=&array(
'chil'=&null
) ), );/**** @author: fc_lamp* @blog:fc-lamp.* @param $array:数组 $i:当前循环的第几层***/function test($array,$i=0){ $str = ''; foreach($array as $one) {
$str .= str_repeat('&',$i).$one['n'].'&br/&';
if(is_array($one['chil']))
$str .=test($one['chil'],$i);
//一层循环完后,减一层
$i--; } return $}echo test($a);
fc_lamp推荐阅读:
& 16:22:00
阅读(7097)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'简单说说python中函数传值方式(引用传值,按值传递,默认参数、关键字参数)',
blogAbstract:' 简单说说python中函数传值方式(引用传值,按值传递,默认参数、关键字参数)(注:在python中,一切对象皆引用,在Python中,无论你把对象做为参数传递,做为函数返回值,都是引用传递的)eg:import copya =[\'a\',\'b\',\'c\']b = ac = copy.copy(a)b.append(\'d\')print \'a=\',a #a= [\'a\', \'b\', \'c\', \'d\']print \'b=\',b #b= [\'a\', \'b\', \'c\', \'d\']print \'c=\',c #c= [\'a\', \'b\', \'c\']',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:1,
publishTime:9,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}Python 函数中,参数是传值,还是传引用? - Python - 伯乐在线
& Python 函数中,参数是传值,还是传引用?
在 C/C++ 中,传值和传引用是函数参数传递的两种方式,在Python中参数是如何传递的?回答这个问题前,不如先来看两段代码。
def foo(arg):
print(arg)
print(a) # 输出:1
def foo(arg):&&&&arg = 2&&&&print(arg)&a = 1foo(a)&&# 输出:2print(a) # 输出:1
看了代码段1的同学可能会说参数是值传递。
def bar(args):
args.append(1)
print(b)# 输出:[]
print(id(b)) # 输出:
print(b) # 输出:[1]
print(id(b))
def bar(args):&&&&args.append(1)&b = []print(b)# 输出:[]print(id(b)) # 输出:bar(b)print(b) # 输出:[1]print(id(b))&&# 输出:
看了代码段2,这时可能又有人会说,参数是传引用,那么问题来了,参数传递到底是传值还是传引用或者两者都不是?为了把这个问题弄清楚,先了解 Python 中变量与对象之间的关系。
变量与对象
Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标签),对象的操作都是通过引用来完成的。例如,[]是一个空列表对象,变量 a 是该对象的一个引用
a.append(1)
a = []a.append(1)
在 Python 中,「变量」更准确叫法是「名字」,赋值操作 = 就是把一个名字绑定到一个对象上。就像给对象添加一个标签。
整数 1 赋值给变量 a 就相当于是在整数1上绑定了一个 a 标签。
整数 2 赋值给变量 a,相当于把原来整数 1 身上的 a 标签撕掉,贴到整数 2 身上。
把变量 a 赋值给另外一个变量 b,相当于在对象 2 上贴了 a,b 两个标签,通过这两个变量都可以对对象 2 进行操作。
变量本身没有类型信息,类型信息存储在对象中,这和C/C++中的变量有非常大的出入(C中的变量是一段内存区域)
Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,清楚了赋值和参数传递的本质之后,现在再来分析前面两段代码。
def foo(arg):
print(arg)
print(a) # 输出:1
def foo(arg):&&&&arg = 2&&&&print(arg)&a = 1foo(a)&&# 输出:2print(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)
print(b)# 输出:[]
print(id(b)) # 输出:
print(b) # 输出:[1]
print(id(b))
def bar(args):&&&&args.append(1)&b = []print(b)# 输出:[]print(id(b)) # 输出:bar(b)print(b) # 输出:[1]print(id(b))&&# 输出:
执行 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
def bad_append(new_item, a_list=[]):&&&&a_list.append(new_item)&&&&return a_list
这段代码是初学者最容易犯的错误,用可变(mutable)对象作为参数的默认值。函数定义好之后,默认参数 a_list 就会指向(绑定)到一个空列表对象,每次调用函数时,都是对同一个对象进行 append 操作。因此这样写就会有潜在的bug,同样的调用方式返回了不一样的结果。
&&& print bad_append('one')
&&& print bad_append('one')
['one', 'one']
&&& 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
def good_append(new_item, a_list=None):&&&&if a_list is None:&&&&&&&&a_list = []&&&&a_list.append(new_item)&&&&return a_list
参考:http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
可能感兴趣的话题
看完之后感觉又有了一点点小小的进步
o 256 回复
关于 Python 频道
Python频道分享 Python 开发技术、相关的行业动态。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线

我要回帖

更多关于 python 引用上级函数 的文章

 

随机推荐