目录
一、函数
1.1 函数是什么
1.2 语法格式
1.3 函数参数
1.4 函数的返回值
1.5 变量作用域
1.6 函数的执行过程
1.7 链式调用
1.8 嵌套调用
1.9 函数递归
1.10 参数默认值
1.11 关键字参数
1.12 小结
一、函数
1.1 函数是什么
数学上的函数, 比如 y = sin x , x 取不同的值, y 就会得到不同的结果.编程中的函数,是一段可以被重复使用的代码片段.
# 1. 求 1 - 100 的和 sum = 0 for i in range(1, 101): sum += i print(sum) # 2. 求 300 - 400 的和 sum = 0 for i in range(300, 401): sum += i print(sum) # 3. 求 1 - 1000 的和 sum = 0 for i in range(1, 1001): sum += i print(sum)# 定义函数 def calcSum(beg, end): sum = 0 for i in range(beg, end + 1): sum += i print(sum) # 调用函数 sum(1, 100) sum(300, 400) sum(1, 1000)1.2 语法格式
def函数名(形参列表):函数体return返回值
函数名(实参列表)//不考虑返回值返回值=函数名(实参列表)//考虑返回值
deftest1():print('hello')#如果光是定义函数,而不调用,则不会执行.
test3()#还没有执行到定义,就先执行调用了,此时就会报错.deftest3():print('hello')
就像昆虫记里面,必须先念诗才能成尊。
1.3 函数参数
def calcSum(beg, end): sum = 0 for i in range(beg, end + 1): sum += i print(sum) calcSum(1, 100) calcSum(300, 400) calcSum(1, 1000)在执行sum(1, 100)的时候,就相当于beg = 1, end = 100,然后在函数内部就可以针对1-100进行运算.在执行sum(300, 400)的时候,就相当于beg = 300, end = 400,然后在函数内部就可以针对300-400进行运算.
- 一个函数可以有一个形参,也可以有多个形参,也可以没有形参.
- 一个函数的形参有几个,那么传递实参的时候也得传几个.保证个数要匹配.
def test(a, b, c): print(a, b, c) test(10)- 和C++ / Java不同, Python是动态类型的编程语言,函数的形参不必指定参数类型.换句话说,一个函数可以支持多种不同类型的参数.
def test(a): print(a) test(10) test('hello') test(True)1.4 函数的返回值
此处的 "输入", "输出" 是更广义的输入输出, 不是单纯指通过控制台输入输出.我们可以把函数想象成一个 "工厂". 工厂需要买入原材料, 进行加工, 并生产出产品.函数的参数就是原材料, 函数的返回值就是生产出的产品.
def calcSum(beg, end): sum = 0 for i in range(beg, end + 1): sum += i print(sum) calcSum(1, 100)def calcSum(beg, end): sum = 0 for i in range(beg, end + 1): sum += i return sum result = calcSum(1, 100) print(result)我们一般倾向于第二种写法.实际开发中我们的一个通常的编程原则, 是 "逻辑和用户交互分离". 而第一种写法的函数中, 既包含了计算逻辑, 又包含了和用户交互(打印到控制台上). 这种写法是不太好的, 如果后续我们需要的是把计算结果保存到文件中, 或者通过网络发送, 或者展示到图形化界面里, 那么第一种写法的函数, 就难以胜任了.而第二种写法则专注于做计算逻辑, 不负责和用户交互. 那么就很容易把这个逻辑搭配不同的用户交互代码, 来实现不同的效果.
# 判定是否是奇数 def isOdd(num): if num % 2 == 0: return False else: return True result = isOdd(10) print(result)# 判定是否是奇数 def isOdd(num): if num % 2 == 0: return False return True result = isOdd(10) print(result)如果num是偶数,则进入if之后,就会触发return False,也就不会继续执行return True
def getPoint(): x = 10 y = 20 return x, y a, b = getPoint()def getPoint(): x = 10 y = 20 return x, y _, b = getPoint()1.5 变量作用域
def getPoint(): x = 10 y = 20 return x, y x, y = getPoint()在函数getPoint()内部定义的x, y只是在函数内部生效.一旦出了函数的范围,这两个变量就不再生效了.
def getPoint(): x = 10 y = 20 return x, y getPoint() print(x, y)x = 20 def test(): x = 10 print(f'函数内部 x = {x}') test() print(f'函数外部 x = {x}')- 在函数内部的变量,也称为"局部变量"
- 不在任何函数内部的变量,也称为"全局变量
x = 20 def test(): print(f'x = {x}') test()x = 20 def test(): global x x = 10 print(f'函数内部 x = {x}') test() print(f'函数外部 x = {x}')如果此处没有 global , 则函数内部的 x = 10 就会被视为是创建一个局部变量 x, 这样就和全局变量 x 不相关了.
for i in range(1, 10): print(f'函数内部 i = {i}') print(f'函数外部 i = {i}')1.6 函数的执行过程
- 调用函数才会执行函数体代码.不调用则不会执行.
- 函数体执行结束(或者遇到return语句),则回到函数调用位置,继续往下执行.
def test(): print("执行函数内部代码") print("执行函数内部代码") print("执行函数内部代码") print("1111") test() print("2222") test() print("3333")
- 点击行号右侧的空白, 可以在代码中插入断点
- 右键, Debug, 可以按照调试模式执行代码. 每次执行到断点, 程序都会暂停下来.
- 使用 Step Into (F7) 功能可以逐行执行代码.
1.7 链式调用
# 判定是否是奇数 def isOdd(num): if num % 2 == 0: return False else: return True result = isOdd(10) print(result)print(isOdd(10))
这是一种比较常见的写法.
1.8 嵌套调用
def test(): print("执行函数内部代码") print("执行函数内部代码") print("执行函数内部代码")def a(): print("函数 a") def b(): print("函数 b") a() def c(): print("函数 c") b() def d(): print("函数 d") c() d()def a(): print("函数 a") def b(): a() print("函数 b") def c(): b() print("函数 c") def d(): c() print("函数 d") d()注意体会上述代码的执行顺序. 可以通过画图的方式来理解.
可以通过 PyCharm 调试器看到函数调用栈和栈帧.在调试状态下, PyCharm 左下角一般就会显示出函数调用栈.
def a(): num1 = 10 print("函数 a") def b(): num2 = 20 a() print("函数 b") def c(): num3 = 30 b() print("函数 c") def d(): num4 = 40 c() print("函数 d") d()选择不同的栈帧, 就可以看到各自栈帧中的局部变量.思考:上述代码, a, b, c, d 函数中的局部变量名各不相同. 如果变量名是相同的, 比如都是 num , 那么这四个函数中的 num 是属于同一个变量, 还是不同变量呢?显然是不同的。
1.9 函数递归
def factor(n): if n == 1: return 1 return n * factor(n - 1) result = factor(5) print(result)- 存在递归结束条件.比如if n == 1就是结束条件.当n为1的时候,递归就结束了.
- 每次递归的时候,要保证函数的实参是逐渐逼近结束条件的.
def factor(n): return n * factor(n - 1) result = factor(5) print(result)如前面所描述, 函数调用时会在函数调用栈中记录每一层函数调用的信息.但是函数调用栈的空间不是无限大的. 如果调用层数太多, 就会超出栈的最大范围, 导致出现问题
- 递归类似于"数学归纳法" ,明确初始条件,和递推公式,就可以解决一系列的问题.
- 递归代码往往代码量非常少.
- 递归代码往往难以理解,很容易超出掌控范围
- 递归代码容易出现栈溢出的情况
- 递归代码往往可以转换成等价的循环代码.并且通常来说循环版本的代码执行效率要略高于递归版本.
1.10 参数默认值
def add(x, y, debug=False): if debug: print(f'调试信息: x={x}, y={y}') return x + y print(add(10, 20)) print(add(10, 20, True))def add(x, debug=False, y): if debug: print(f'调试信息: x={x}, y={y}') return x + y print(add(10, 20))1.11 关键字参数
def test(x, y): print(f'x = {x}') print(f'y = {y}') test(x=10, y=20) test(y=100, x=200)1.12 小结
- 函数的定义
- 函数的调用
- 函数的参数传递