文章目录
- 一、函数的返回值
- 二、函数的作用域
- 2.1局部变量
- 2.2 全局变量
- 三、递归函数
- 1. 什么是递归函数
- 2. 递归的特点
- 3. 4个步骤,写出一个递归函数
- 3.1 第一步,定义函数功能
- 3.2 第二步,找到终止条件
- 3.3 第三步,甩锅
- 3.4 第四步,反甩锅
- 3.5 应用:3以内数字累加和
- 练习题 - 定义一个函数用来检查字符串是否回文字符串
一、函数的返回值
- 函数的返回值就是函数执行以后返回的结果。
- return的作用是退出函数并返回函数的返回值,任何时候,只要执行了return语句就一定会退出函数。
- 函数的返回值可以直接使用,也可以通过一个变量进行接收函数的返回值,一般用变量接收的方法。
def fn():
# return后面可以跟任意对象
return 123
# return 'haha'
# return [4,5,6]
# return {'name':'jerry'}
a = 1
r = fn()
print(r) # 123
print(fn()) # 123
一个函数没有写return ,或者仅仅写了一个return 那么就相当于 return None, 输出为None就是没有返回值。
def fn():
print('hello world')
res = fn()
print(res)
- 尽管函数fn没有使用return语句,但仍然会有返回值,这种情况下,函数默认返回None。
- python的函数允许一次返回多个结果
def fn():
return 1, 2
res = fn()
print(res, type(res)) # (1, 2) <class 'tuple'>
- 函数一次返回多个结果时,是以元组的形式返回的。
- return后面可以连接列表、元组或字典,以返回多个值。
- 如果函数里没有任何数据需要返回,但需要提前结束,也可以使用return。
def fn2():
print('haha')
return
print('heihei')
r = fn2() # haha
print(r) # None
在函数中,return后面的代码都不会执行。return一旦执行函数自动结束。
二、函数的作用域
函数作用域在函数调用时创建,在调用结束时销毁。
函数每调用一次就会产生一个新的函数作用域,在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
函数的作用域指的是函数生效的范围,主要分为两类:局部变量和全局变量。
2.1局部变量
所谓局部变量是定义在函数体内部的变量,即只在函数体内部生效。
def testA():
a = 100
print(a)
testA() # 100
print(a) # 报错:name 'a' is not defined
变量a是定义在
testA
函数内部的变量,在函数外部访问则立即报错。
局部变量的作用:在函数体内部,临时保存数据,即当函数调用完成后,则销毁局部变量。
2.2 全局变量
所谓全局变量,指的是在函数体内、外都能生效的变量。
思考:如果有一个数据,在函数A和函数B中都要使用,该怎么办?
答:将这个数据存储在一个全局变量里面。
# 定义全局变量a
a = 100
def testA():
print(a) # 访问全局变量a,并打印变量a存储的数据
def testB():
print(a) # 访问全局变量a,并打印变量a存储的数据
testA() # 100
testB() # 100
思考:testB
函数需求修改变量a的值为200,如何修改程序?
a = 100
def testA():
print(a)
def testB():
a = 200
print(a)
testA() # 100
testB() # 200
print(f'全局变量a = {a}') # 全局变量a = 100
思考:在testB
函数内部的a = 200
中的变量a是在修改全局变量a
吗?
答:不是。观察上述代码发现,最后一行得到a的数据是100,仍然是定义全局变量a时候的值,而没有返回
testB
函数内部的200。综上:testB
函数内部的a = 200
是定义了一个局部变量。
思考:如何在函数体内部修改全局变量?
a = 100
def testA():
print(a)
def testB():
# global 关键字声明a是全局变量
global a
a = 200
print(a)
testA() # 100
testB() # 200
print(f'全局变量a = {a}') # 全局变量a = 200
如果你希望在函数内部修改全局变量的时候,则需要使用global关键字,来声明变量。
三、递归函数
1. 什么是递归函数
- 递归是一种解决问题的思想,它和循环很像,整体思想是将一个大问题分解为一个一个的小问题,直到问题无法分解时,再去解决问题。
- 从前有座山,山里有座庙,庙里有个老和尚在给一个小和尚讲故事,讲什么故事呢?从前有座山,山里有座庙,庙里有个老和尚在给一个小和尚讲故事,讲什么故事呢?…递归就类似这个故事一样。
- 递归函数是一种特殊的函数,函数内部调用函数自身。
2. 递归的特点
递归式的函数需要2个条件:
- 基线条件
问题可以被分解为最小的问题,当满足基线条件时,递归就不在执行了,就是必须有有一个出口 - 递归条件
将问题可以继续分解的条件, 函数内部自己调用自己
下面是一个函数例子改造成递归函数,你可以对比他们之间的区别。
def my_print(content, count):
for i in range(count):
print(content)
my_print('ok', 2)
改造成递归函数后
def my_print(content, count):
print(content)
if count == 1:
return
my_print(content, count-1)
my_print('ok', 2)
在改造后的递归函数中,会再次调用my_print函数自身。你不禁会担忧,这样自身调用自身,岂不是要形成死循环?没错,会形成死循环,因此递归函数一定要有一个递归终止的条件,在本示例中,递归终止的条件是count=1,当这个条件满足时,就会执行return 语句,函数退出。
3. 4个步骤,写出一个递归函数
递归函数对初学者来说,是无法回避的噩梦。刚刚对程序顺序执行有了一定理解后,突然间告诉你函数可以调用函数自身,近似无限循环的递归调用让你深陷其中无法自拔。
编程,既是一门知识,也是一门手艺,你可以自学知识,但手艺很难自学,没有长时间的经验积累,没有一次次的试错和总结,你的技能就无法得到提升,所以,手艺,需要有人传帮带。
咱们以最简单的计算阶乘为例,将完成递归函数归纳为4个步骤
- 定义函数功能
- 找到终止条件
- 甩锅
- 反甩锅
3.1 第一步,定义函数功能
# 创建一个函数 求任意数的阶乘
def fn(n):
"""
:param n:
:return:
"""
3.2 第二步,找到终止条件
所有的递归函数,一定会有终止条件,如果不符合这个要求,那么就会进入死循环。函数的功能是计算阶乘,阶乘的终止条件是什么呢? 是1啊,1的阶乘是1,你不能计算0的阶乘,1是最小的可以求阶乘的整数,现在,假设调用函数传参n为1,代码该怎么写呢?
# 创建一个函数 求任意数的阶乘
def fn(n):
"""
:param n:
:return:
"""
# 参数n 要求阶乘的数字
# 1 基线条件
# 判断如果n为1的时候,就是它本身就不需要递归了,就是必须有出口
if n == 1:
return 1
函数的功能是计算n的阶乘,如果n等于1,直接返回1就好了
3.3 第三步,甩锅
如果n不是1,该怎么办呢?答案很简单,甩锅,这就是说,现在你搞不定这个事情了,那你可以把锅帅给别人啊,你找到小明,告诉他: 老师让我计算n的阶乘,你现在计算n-1的阶乘,把结果告诉我,要快,不然老师生气了。
你看,把锅甩给了小明,小明如果能算出来n-1的阶乘,你把他的答案乘以n,不就是n的阶乘了么,如果小明算不出来,你对老师也能有交代,是小明的过错,这就叫甩锅,这就是函数的递归调用
def fn(n):
"""
:param n:
:return:
"""
# 参数n 求阶乘的数字
# 1 基线条件
# 判断如果n为1的时候,就是它本身就不需要递归了,就是必须有出口
if n == 1:
return 1
s = fn(n-1) # 等待小明的结果
3.4 第四步,反甩锅
你把锅甩给小明,小明也不傻,他又把锅甩给了小刚,小刚表示很无辜,但事情还是要做,于是又甩给了小红,就这样,一个接着一个的甩,但是要注意,总会有甩不出去的时候,最后,锅甩到了小芳这里,小芳是个好姑娘,聪明乖巧,到她这里,需要计算1的阶乘,小芳心想,甩不出去了,而且也不用甩啊,1的阶乘就是1啊,多简单。
于是,开始了第四个步骤,反甩锅,1的阶层等于1,小芳心想,我已经告诉你答案了,剩下的事情你们处理吧,这口锅沿着一开始的路线反向的甩了回来,每个人都得到了之前自己甩锅的人的答案,现在小明把n-1的阶乘告诉给你,你应该怎么办呢,你应该把结果乘以n,然后return这个结果,最终完成了老师的要求
def fn(n):
"""
计算n的阶乘
:param n:
:return:
"""
# 参数n 要求阶乘的数字
# 1 基线条件
# 判断如果n为1的时候,就是它本身就不需要递归了,就是必须有出口
if n == 1:
return 1
# s = fn(n-1)
# return s*n
# 2 递归条件8 * 7!(上面的代码可以直接简化)
return n * fn(n-1)
print(fn(8))
上面的代码,没有一个字符超出你的学习范围,就是知识;以怎样的逻辑组织代码,理解代码结构,这就是手艺
定义一个函数 来为任意数字做任意幂运算 n ** i
# 10 ** 5 = 10 * 10 ** 4
# 10 ** 4 = 10 * 10 ** 3
# .....
# 10 ** 1 = 10
def fn(n,i):
"""
:param n: 参数n 要做幂运算的数字
:param i: 参数i 要做幂运算的次数
:return:
"""
# 1 基线条件
if i == 1:
return n
# 2 递归条件 10 ** 5 = 10 * 10 ** 4
return n * fn(n,i-1)
print(fn2(5,6))
print(5 ** 6)
3.5 应用:3以内数字累加和
# 3 + 2 + 1
def sum_numbers(num):
# 1.如果是1,直接返回1 -- 出口
if num == 1:
return 1
# 2.如果不是1,重复执行累加并返回结果
return num + sum_numbers(num-1)
sum_result = sum_numbers(3)
# 输出结果为6
print(sum_result)
- 执行结果
练习题 - 定义一个函数用来检查字符串是否回文字符串
def fn(s):
"""判断回文数,递归法"""
if len(s) < 2:
return True
if s[0] == s[-1]:
return fn(s[1:-1])
else:
return False
name1 = '11'
name2 = '1234'
print(fn(name1))
print(fn(name2))