当前位置: 首页 > 科技观察

一个看似初级的Python小问题,高手也难保不掉进坑里

时间:2023-03-17 01:24:25 科技观察

一个看似初级的Python问题,就算是高手也不会掉坑里。转载本文请联系Python作业导师公众号。昨晚有个同学问了一个很有意思的问题。题目本身很简单,但是包含了几个初学者比较难理解的知识点,在编码实践中随处可见。如果对这些知识点的理解出现偏差,即使是经验丰富的Python程序员,一不小心也会掉坑里。deff(w,a,b):w.append(a)w=w+[b]returnww,a,b=[5,9],2,1w=f(w,a,b)+wprint(w)上面的代码稍微改动一下,函数中的赋值运算(=)就变成了加法运算(+=),请问正确的输出结果是哪个?deff(w,a,b):w.append(a)w+=[b]returnww,a,b=[5,9],2,1w=f(w,a,b)+wprint(w)到正确回答问题,首先要理解可变对象和不可变对象的概念,以及可变对象和不可变对象是如何作为函数参数向函数传递参数的。那么,什么是可变对象,什么是不可变对象?在Python中,整数(int)、浮点数(float)、布尔值(bool)、元组(tuple)和字符串(str)等。内置类,一旦实例化,就不能改变,是不可变对象;而列表(list)、字典(dict)、集合(sets)等内置类则在实例化后得到对象,可以任意修改。初学者读到这里可能有些不明白:元组和字符串是不能改变的,就像老师和课本上说的那样,为什么整型和浮点型对象是不可变的呢?x=1之后,x就不能改变了吗?加1不等于2吗?显然,这是对对象概念的误解。x=1表示x指向一个值为1的整型对象,但x并不是真正的整型对象,而是一个名字。我们称之为变量名或对象名。x加1变成2,不是给一个值为1的整数对象加1,而是让x指向另一个值为2的整数对象。>>>id(x)2794729897296>>>x+=1>>>id(x)2794730437616>>>y=[1,2]>>>id(y)2794699482248>>>y+=[3]>>>id(y)2794699482248>>>y=y+[4]>>>id(y)2794699482376借助Python的id函数(返回变量名指向的对象的首内存地址),可以清楚的看到加法等操作在(+=)之后,x指向的整型对象的地址发生了变化,而y指向的list对象的地址没有变化。但是,对y进行赋值操作(=)后,y指向的列表对象的地址发生了变化。这说明赋值操作(=)在变量名和对象之间建立了新的对应关系,而加法操作(+=)不会改变变量名和对象之间的对应关系,除非对象是不可变的。了解了可变对象和不可变对象的概念之后,就很容易理解可变对象和不可变对象是如何作为函数参数来给函数传递参数的了。如下图所示,红色箭头指向作为参数传递给函数的可变对象,绿色箭头指向作为参数传递给函数的不可变对象。对于不可变对象来说,无论函数内部如何改变这些参数,都不会影响函数外部的不可变对象,因为它们是不可变的。对于可变对象,在函数内部对它们的任何操作都作用于对象本身,而这个对象就是函数外部变量所指向的对象。需要注意的是,函数的参数传递并不要求实参和形参同名。下图中红绿箭头对应的实参与形参同名。这只是我个人的习惯,不是规则要求。是时候开始正事了。第一段代码中,将可变对象w和不可变对象a、b作为参数传入函数后,内部变量名w和外部变量名w指向同一个对象,append操作自然会影响外部变量名称w指向的内容。列表对象。后续的赋值操作将函数内部的变量名w指向另一个新的列表对象,所以外部变量名w指向的列表对象不会改变。如下图所示,不言而喻,代码最终输出的应该是D,即[5,9,2,1,5,9,2]。在第二段代码中,由于+=运算并没有改变内部变量w的指针,所以外部变量w指向的列表自然就变成了[5,9,2,1],最终输出的是二[5,9,2,1],正确答案为E,如下图所示。