(一)返回值
调用一个有返回值的函数会生成一个返回值;通常将这个返回值赋值给变量或者作为表达式的一部分;
def area(radius):
a = math.pi * radius**2
return a
return
语句意味着:立即从该函数返回,并且使用接下来的表达式作为返回值;返回值可以是任意复杂的;- 在一个有返回值的函数中,最好保证程序执行的每一个流程都最终碰到一个return;【充分考虑好每一种分支情况】
(二)增量式开发(incremental development)
- 随着函数越来越复杂,调试的时间会越来越多;为了应对负载的程序,可以尝试增量式开发;
- 增量式开发的目标,是通过每次只增加和测试少量代码,来避免长时间的调试;
- 比如,在写一个函数的时候,可以先写一下这个函数的函数头和形参,函数体可以用
pass
或者其他的return
语句进行代替;当这个函数头可以运行的时候,再往函数体中增加代码; - 当然上面的只是一个简单的类比,只是一种简单的思路:当想要实现一个功能的时候,尤其是复杂的功能,不要想着一步到位,可以将这个功能进行分解,或者先实现一个框架,之后再逐步完善细节;
- 增量式开发的关键:
- 从一个能运行的程序开始,并且每次只增加少量改动。无论你何时遇到错误,都能够清楚定位错误的源头;
- 使用临时变量存储中间值,这样能显示并检查它们;
- 一旦程序能够正确运行,就要删除一些脚手架(scaffolfing)代码;或者将多条语句组合成为复合表达式,当然,前提是不影响程序的可读性;
(三)组合
- 我们可以从一个函数的内部调用另一个函数;
- 一些临时变量对于开发很有用,但是一旦程序正确运行了,我们可以通过合并函数调用,使得程序更加简洁;
(四)布尔函数
- 这种类型的函数可以返回布尔类型(booleans),通常对于隐藏函数内部的复杂测试代码非常有用;
- 布尔函数通常用于条件语句中;
def is_divisible(x, y):
if x%y == 0:
return true;
else:
return false;
==
的返回值是Boolean类型,可以直接使用这个来简化上面的代码:
def is_divisible(x, y):
return x % y == 0
(五)再谈递归
到目前为止,虽然学到的知识Python中很小的一个子集,但是实际上,这已经是一个完备的编程语言,这意味着任何能够被计算的东西都可以用这个语言表达;
- 使用递归计算阶乘:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
- 程序的执行过程:【假如n == 3】
- 堆栈图:
(六)信任之跃【一种阅读代码的方法】
- 跟随程序的执行流程读代码,是一种方法,但是可能很快就会变得错综复杂;
- 另一种方法:当遇到一个函数的时候,不去跟踪函数的执行流程,而是假设这个函数正确运行并且返回了正确的结果;
- 事实上,使用内建函数的时候已经使用的了这种方法,我们总是假设,这个内建函数的执行是正确的;
- 使用递归函数也是一样的,我们不再顺着执行流程,而是假设每次递归都能正确执行;
(七)斐波那契数列【递归】
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacii(n-1)+fibonacci(n-2)
(八)检查数据类型
- 上面的函数中,当输入的参数是一个非整数的时候,这时候,递归将无限进行下去,因为没有边界;
- 我们需要检查传入的参数是不是我们所希望的类型;
- 我们使用内建函数
isinstance
来验证实参的类型:
def factorial(n):
if not isinstance(n, int):
print('Factorial is only defined for integers.')
return None
elif n<0:
print('Factorial is not defined for negative integers.')
return None
elif n == 0:
return 1
else:
return factorial(n-1)*n
- 上面的方法有时候称之为“监护人(Guardian)模式;通过设置监护人,保证后面的代码不会出现错误;
- 当然还有更加灵活的方式:抛出异常
(九)调试
将一个大程序分解为若干较小的函数,为程序的调试生成的自然的检查点;当一个程序不能如预期运行的时候,需要考虑的情况:
- 该函数获取的实参有问题,违反先决条件;
- 该函数有些问题,违反后置条件;
- 返回值或者使用方法有问题;
实参问题
为排除这种可能性,可以在函数的开始增加一条
函数问题
如果函数的形参没有问题,则在函数的
return
之前,打印结果、返回值;考虑用一些简单的值调用这个函数;
调用问题
要确保返回值被正确地使用;