查看原文
其他

给妹子讲python-S01E20函数参数的传递与修改

给妹子讲python Python爱好者社区 2019-04-07

作者:酱油哥/ 清华程序猿      
微信公众号: python数据科学家
知乎专栏: 《给妹子讲python》
https://zhuanlan.zhihu.com/c_147297848


前文传送门:

给妹子讲python-S01E01好用的列表

给妹子讲python-S01E02学会用字典

给妹子讲python-S01E03元组的使用

给妹子讲python-S01E04容器遍历和列表解析式

给妹子讲python-S01E05字符串的基本用法

给妹子讲python-S01E06字符串用法进阶

给妹子讲python-S01E07字符编码历史观:从ASCII到Unicode

给妹子讲python-S01E08理清python中的字符编码方法

给妹子讲python-S01E09文件操作小意思

给妹子讲python-S01E10动态类型与共享引用

给妹子讲python-S01E11赋值与对象拷贝

给妹子讲python-S01E12循环迭代初体验

给妹子讲python-S01E13循环迭代高级技巧

给妹子讲python-S01E14可迭代对象和迭代器

给妹子讲python-S01E15迭代环境

给妹子讲python-S01E16生成器的使用

给妹子讲python-S01E17函数的基本特征

给妹子讲python-S01E18初探函数作用域

给妹子讲python-S01E19内嵌作用域与函数闭包

【要点抢先看】

1.函数参数传递的实现过程
2.可变对象和不可变对象参数传递、修改的区别
3.如何避免不可变对象参数传递的本地修改

今天我们再来说说函数中的参数传递问题

【妹子说】这个看上去自然而然的过程里有什么讲究么?

有很多需要注意的地方,从这一节开始就来仔细的聊聊。

参数的传递是通过自动将对象赋值给本地变量名来实现的。在函数运行时,函数头部的参数名是一个新的、本地的变量名,这个变量名是在函数的本地作用域内存在。参数的传递本质上就是python赋值的另一个实例而已。

那么,这个问题分为可变对象和不可变对象两种情况进行讨论:

在原处改变函数的可变对象参数的值会对调用者有影响。函数能够就地改变传入的可变对象,因此其结果会影响调用者,这其实和前面介绍过的对象赋值原理是一样的;

而不可变对象的引用重新赋值会指向新的对象,因此不会改变调用者。

先看一个不可变对象的例子

def f(a):
    a = 99
    print(a)

b = 88
f(b)
print(b)

99
88

在函数中修改a对于调用函数的地方没有任何影响,因为他在函数内部直接把本地变量a重置为了一个完全不同的新对象,所以他不会影响最初的变量b

而当参数传递像列表和字典这样的可修改对象的时候,我们还需要注意,对这样的可变对象的原处修改可能在函数退出后依然有效,并由此影响到调用者。

def change(a,b):
    a = 2
    b[0] = 'spam'

x = 1
l = [1,2]
change(x,l)
print(x,l)

1 ['spam', 2]

再次对比可以看出,调用者的不可变变量x没有受到影响,而可变变量L在函数内部进行本地修改,并影响到了自身

可以看出,对于参数a,仅仅把本地变量a修改为引用一个完全不同的对象,并没有改变调用者作用域中的名称x的绑定。而参数b被传给了一个可变对象(在调用者作用域中叫做L的列表),因为第二个赋值是一个在原处发生的对象改变,对函数中b[0]进行赋值的结果会在函数返回后影响L的值,他修改了b所引用的对象的一部分,因为引用共享对象的缘故,L也被同时改变。

再强调一次,其实参数传递后的本地修改过程和简单对象赋值后的对象修改,实质上是一回事,换句话说就等于下面这个例子所描述的程序过程

L = [1,2]
b = L
b[0] = 'spam'
print(L)

['spam', 2]

【妹子说】那如何避免对可变参数的修改呢?

实际上,可变参数的原处修改行为并不是一个bug,它只是参数传递在python中工作的方式。在python中,默认通过引用进行函数的参数传递,是因为这通常是我们所想要的:这意味着不需要创建多个拷贝就可以在我们的程序中传递很大的对象。如果不想要函数内部在原处的修改影响传递给它的对象,那么,我们可以简单的创建一个明确的可变对象的拷贝

def change(a,b):
    a = 2
    b[0] = 'spam'

x = 1
l = [1,2]
change(x,l[:])
print(x,l)

1 [1, 2]

或者在函数内部进行拷贝,这样可以不改变传入的对象,函数调用看上去没有变化

def change(a,b):
    b = b[:]
    a = 2
    b[0] = 'spam'

x = 1
l = [1,2]
change(x,l)
print(x,l)

1 [1, 2]

今天就说这么多,看上去不是很复杂,不过在程序中可是经常会碰到的,可不能掉以轻心。

Python爱好者社区历史文章大合集

Python爱好者社区历史文章列表(每周append更新一次)

福利:文末扫码立刻关注公众号,“Python爱好者社区”,开始学习Python课程:

关注后在公众号内回复“课程”即可获取:

小编的Python入门免费视频课程!!!

【最新免费微课】小编的Python快速上手matplotlib可视化库!!!

崔老师爬虫实战案例免费学习视频。

陈老师数据分析报告制作免费学习视频。

玩转大数据分析!Spark2.X+Python 精华实战课程免费学习视频。


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存