Python中的参数传递问题
今天学习了一个小时的朴素贝叶斯,数学原理到是好理解,一旦使用它来解决实际的问题,比如使用朴素贝叶斯实现一个自动敏感词过滤系统,就好像进了迷宫一样,目前暂未搞懂,明天继续搞。但今天必须分享点什么,那就分享一下自己对 Python 参数传递吧,很多初学者容易错。
万物皆对象。Python 中的对象分为两类,一类是不可变对象:数值,字符串,元组。另一类是可变对象:列表,字典,集合。对于不可变对象,无法实现自己修改自己的操作,如果被修改了,那么一定是新的对象,而不再是原来的对象。比如正面的代码:
a = 1
b = a
a = a + 1
这里首先将 1 赋值于 a,即 a 指向了 1 这个对象,如下面的流程图所示:
不可变对象1.png接着 b = a 则表示,让变量 b 也同时指向 1 这个对象。这里要注意,Python 里的对象可以被多个变量所指向或引用。
不可变对象2.png最后执行 a = a + 1。需要注意的是,Python 的数据类型,例如整型(int)、字符串(string)等等,是不可变的。所以,a = a + 1,并不是让 a 的值增加 1,而是表示重新创建了一个新的值为 2 的对象,并让 a 指向它。但是 b 仍然不变,仍然指向 1 这个对象。
不可变对象3.png上述以数值做为示例,可以类比字符串和元组对象来加深理解。可变对象就比较好理解了,比如列表在执行 append 方法后新增一个元素,但原来指向此列表的变更仍然指向此列表,在内存中的地址不变,也可以说实现了自增。
python 中的参数传递
这里首先引用 Python 官方文档中的一段说明:
“Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per Se.”
准确地说,Python 的参数传递是赋值传递(pass by assignment),或者叫作对象的引用传递(pass by object reference)。Python 里所有的数据类型都是对象,所以参数传递时,只是让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递一说。
比如,我们来看下面这个例子:
def fun(x):
x=10
a=1
fun(a)
print(a)
#输出
1
这里,将 1 这个对象传递给了 x 变量,在函数体内,x 变量又指向了新的对象 10,因此此函数并不改变变量 a 的数值 。如果想改变 a 的值,那么可以在函数最后增加 return x 语句,然后在让变更 a = fun(a)。
在看一个例子:
def my_func3(l2):
l2.append(4)
l1 = [1, 2, 3]
my_func3(l1)
l1
[1, 2, 3, 4]
这里,l1 对象传递给 l2,l1 与 l2 指向同一个内存对象 [1, 2, 3],l2 增加一个元素 4 代表着 l1 也增加元素 4。说到这里,你可以使用字典或元组自己测试下。
需要注意的是,这里的赋值或对象的引用传递,不是指向一个具体的内存地址,而是指向一个具体的对象。
清楚了这一点,如果你想通过一个函数来改变某个变量的值,通常有两种方法。一种是直接将可变数据类型(比如列表,字典,集合)当作参数传入,直接在其上修改;第二种则是创建一个新变量,来保存修改后的值,然后将其返回给原变量。在实际工作中,我们更倾向于使用后者,因为其表达清晰明了,不易出错。