有返回值的函数
文章目录
- 有返回值的函数
- 1、返回值
- 2、增量式开发
- 3、组合
- 4、布尔函数
- 5、再谈递归
- 6、信仰之跃
- 7、再举一例
- 8、检查类型
- 9、调试
- 空函数(void) :产生某种效果,像打印一个值或是移动乌龟,但是并不产生一个值;
- 有返回值的函数 :调用这样的函数会生成一个值, 通常将其赋值给某个变量或是作为表达式的一部分;
1、返回值
- 返回值:调用一个函数会生成一个值,这个值称为函数的返回值;
- 调用一个有返回值的函数会生成一个值;
- 通常将函数返回值赋值给某个变量或是作为表达式的一部分;
- 空函数,泛泛地来看,它们没有返回值,更准确地说,它们的返回值是 None ;
- 有返回值的函数中,以包含一个表达式的 return 语句结束:“马上从该函数返回,并使用接下来的表达式作为返回值”;
- return 语句中表达式可以是任意复杂的;
- 条件语句的每一个分支内各有一个返回语句会很有用,一旦一条返回语句执行,函数则终止,不再执行后续的语句;
- 出现在某条return语句之后的代码,或者在执行流程永远不会到达之处的代码,被称为死代码(dead code);
- 在一个有返回值的函数中, 最好保证程序执行的每一个流程最终都会碰到一个 return 语句;
- 函数按照流程执行完毕,未碰到一个 return 语句,返回值将是 None;
def absolute_value(x):
if x < 0:
return -x
if x > 0:
return x
>>> absolute_value(0)
None
def compare(x, y):
if x > y:
return 1
elif x < y:
return -1
elif x == y:
return 0
print(compare(10,10))
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new23.py
0
2、增量式开发
- 增量式开发( incremental development ) :通过每次只增加和测试少量代码,来避免长时间的调试,这种开发方式即为增量式开发( incremental development );
- 从一个能运行的程序开始,并且每次只增加少量改动,无论你何时遇到错误,都能够清楚定位错误的源头;
- 用临时变量存储中间值,这样你就能显示并检查它们;
- 一旦程序正确运行,你要删除一些脚手架代码,或者将多条语句组成复合表达式,但是前提是不会影响程序的可读性;
def hypotenuse1(a,b):
return 0
def hypotenuse2(a, b):
squared = a**2 + b**2
print("squared = ",squared)
return 0
def hypotenuse3(a, b):
squared = a**2 + b**2
c = math.sqrt(squared)
print("c = ",c)
return 0
def hypotenuse(a, b):
squared = a**2 + b**2
c = math.sqrt(squared)
return c
print(hypotenuse1(3,4))
print(hypotenuse2(3,4))
import math
print(hypotenuse3(3,4))
print(hypotenuse(3,4))
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new24.py
0
squared = 25
0
c = 5.0
0
5.0
- 脚手架代码(scaffolding) :上述练习中,
print("squared = ",squared)
这样的代码对程序的构建很有用,但不是最终产品的一部分,这样的代码即为脚手架代码(scaffolding) ;
3、组合
- 可以从一个函数内部调用另一个函数;
def area(radius):
return math.pi * radius**2
def distance(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
dsquared = dx**2 + dy**2
result = math.sqrt(dsquared)
return result
def circle_area(xc, yc, xp, yp):
return area(distance(xc, yc, xp, yp))
4、布尔函数
- 函数可以返回布尔值(booleans);
- 布尔函数返回值:True 或者 False(如果无返回值,则为 None );
def is_between(x, y, z) :
if x <= y and y <= z:
return True
else:
return False
print(is_between(5, 6, 4))
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new25.py
False
5、再谈递归
- 一个递归定义的函数的例子(阶乘函数 factorial):
def factorial(n):
if n == 0:
return 1
else:
recurse = factorial(n-1)
result = n * recurse
return result
- 递归定义类似循环定义,因为定义中包含一个对已经被定义的事物的引用;
- 上面递归定义的函数(阶乘函数)引入实参3时的堆栈图:
6、信仰之跃
- “信仰之跃”:阅读代码,当遇到一个函数调用时,不再去跟踪程序执行流程,而是假设这个函数正确运行并返回了正确的结果;
- 当遇到递归调用时, 不用顺着执行流程,应该假设每次递归调用能够正确工作(返回正确的结果);
- 当然,在没写完函数的时就假设函数正确工作有一点儿奇怪, 但这也是为什么这被称作信仰之跃了;
7、再举一例
- 另一个递归定义的函数(斐波那契数列 fibonacci ):
def fibonacci (n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fibonacci(n-1) + fibonacci(n-2)
- 如果试图跟踪执行流程,即使是相当小的 n ,也足够头疼的,但遵循信仰之跃这种方法,如果假设这两个递归调用都能正确运行,很明显将他们两个相加就是正确结果;
8、检查类型
- 重构后的阶乘函数,演示了一个有时被称作监护人(guardian) 的模式:
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 n * factorial(n-1)
- 前两个条件扮演监护人的角色,避免接下来的代码使用引发错误的值;
- 监护人使得验证代码的正确性成为可能;
9、调试
- 将一个大程序分解为较小的函数为调试生成了自然的检查点, 如果一个函数不如预期的运行,有三个可能性需要考虑:
- 该函数获得的实参有些问题,违反先决条件;
- 该函数有些问题,违反后置条件;
- 返回值或者它的使用方法有问题;
- 为了排除第一种可能,你以在函数的开始增加一条 print 语句来打印形参的值(也可以是它们的类型), 或者你可以写代码来显示地检查先决条件;
- 如果形参看起来没问题,就在每个 return 语句之前增加一条 print 语句,来打印返回值;
- 如果可能,手工检查结果;
- 考虑用一些容易检查的值来调用该函数;
- 如果该函数看起来正常工作,则检查函数调用,确保返回值被正确的使用(或者的确被使用了!);
- 在一个函数的开始和结尾处增加打印语句,可以使执行流程更明显;
def factorial(n):
space = ' ' * (4 * n)
print(space, 'factorial', n)
if n == 0:
print(space, 'returning 1')
return 1
else:
recurse = factorial(n-1)
result = n * recurse
print(space, 'returning', result)
return result
factorial(4)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new26.py
factorial 4
factorial 3
factorial 2
factorial 1
factorial 0
returning 1
returning 1
returning 2
returning 6
returning 24