这些年,你们一起踩过的坑(2)
上次我们踩坑总结文章 这些年,你们一起踩过的坑(1) 受到了不少同学的认可。我也确信文中所涉及的问题是非常具有普遍性的,对绝大多数初学者都会有帮助。
虽然这种干货文章要比蹭热点水文的阅读数少多了,而且一般系列文章的阅读都会呈下降趋势。但有价值的东西,也总归还是要有人来做一做,就算吃力不讨好我也认了。有缘人看到了,学到了,那就挺好。
前文我们说了 6 个常见问题,今天继续,主要谈几个跟函数相关的问题。
1. 为什么我写的代码没有执行?
这是刚接触到函数的同学可能遇上的一个疑问。
函数的定义并不会去执行其中的代码。只有在函数被调用时,这些代码才会执行。
举个形象点的例子来类比,函数就是一个生产小黄鸭的加工机器,函数定义就是把这个机器造出来,生成原料是函数的参数,最终的小黄鸭成品就是函数的返回值。只是把机器造好(定义函数)并不会生产出小黄鸭(调用)。
2. 为什么我调用了函数,依然没有结果?
Python 中函数调用的语法是函数名后面加上括号,括号里是参数。即使没有任何参数,括号也必须有。否则,你就只是放了一个函数对象,什么也没有做。(不加括号的情况下,你可以把函数赋值给另一个变量。)
这个问题经常出现在写入文件的课程,很多人发现程序执行后,文件并没有改变。原因就是最后写了 f.close
,并没有调用文件关闭,以至于写入的内容并没有被保存到文件中。
3. 为什么我加了括号,还是没有输出结果?
这是个很高频的问题【敲黑板!】。很多人在一开始没理解函数的返回值(return)和输出(print)的区别。
函数里 return 后面的东西叫做返回值,它是函数执行的结果,是函数里特有的。一旦遇到 return,函数就结束了。如果你没有手动指定 return,函数会在全部执行完代码后默认 return None。
而 print,之前我们已经说了,就是向控制台输出内容。它不影响函数本身的执行过程,是一个独立的行为。(print 本身也是一种函数)
套用刚才的例子,这个小黄鸭机器上有个喇叭(控制台),每次生成出一只小黄鸭,就会响一声(print 输出)。而小黄鸭成品才是返回值。喇叭可以响很多次,发出各种不同声音(print 各种信息),但每次生成出的小黄鸭只有一个。
函数的返回值可以赋值给变量,之后再被使用。如果没有赋值,那返回值就没有用了。所以上述函数,如果希望输出,有两种写法,一是在函数中直接 print,另一种是:
s = show_hello() print(s)
4. 为什么我调用了函数、加了括号,还赋值了,还是没拿到结果?
常见的函数有两种模式,一种是把结果作为返回值,另一种则是直接对参数本身进行操作。
继续前面的例子,生成小黄鸭的机器就是前一种。而另一种机器,你放上去一只小黄鸭,它会给这个小黄鸭抛光上色。这种机器就是第二种,它不产生新的返回值,只是对原有数据做一些处理。
典型例子就是 list 排序的两种方法 sort
和 sorted
。
sorted(lst)
方法是把参数列表里的元素进行排序后,生成一个新列表作为返回值。它不会影响原有的列表。
lst.sort()
则是对列表本身进行排序,改变了原有列表数据。但它没有返回值。
类似的情况还有 random
模块的 shuffle
方法和 sample
方法。你来尝试分析下两者的不同?
至于一个函数是对参数进行改变,还是生成一个新的返回值,这个文档里都会注明,要养成查文档的习惯。或者,自己在 python shell 写两行代码试一试也就清楚了。
5. 为什么我在函数里修改了变量,但没有效果?
函数里修改了 x,但是最终结果并没有变化。类似的还有程序直接报错变量未定义、不想改变的值被修改了等情况。造成的原因,都是和变量作用域、全局变量、参数传递方式相关。
这是一类老生常谈的问题,之前已专门写过几篇文章详细阐释,这里就不再啰嗦了,对相关概念还不理解的请务必抽空看一看:
全局变量和局部变量
函数的参数传递
可变对象与不可变对象
关于深浅拷贝
今天就说这么多。该系列还会继续,如果你有想听的方面,也可以本文下留言。