匿名函数 函数嵌套 函数递归 作用域
例题引导:
一个由元组构成的列表:a = [(‘b’,3), (‘a’,2), (‘d’,4), (‘c’,1)]
要求:使用lambda表达式和函数sorted(),分别按照元组第一个元素排序(即abcd排序),按照元组第二个元素排序(即1234排序),输出相应的列表结果。
要点:了解lambda表达式和排序函数sorted()用法
参考答案:
a = [('b',3), ('a',2), ('d',4), ('c',1)]
print(sorted(a, key=lambda x:x[0]))
print(sorted(a, key=lambda x:x[1]))
学习引导:
- 匿名函数
- 函数的返回值
- 函数的说明文档
- 函数的嵌套
- 变量作用域
- 函数递归
匿名函数
python可以使用lambda来创建匿名函数
所谓匿名,即不再使用def这样的标准语句来专门定义函数
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda中封装有限的逻辑进去。
lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间中的参数。
有名函数的定义规则为【def+函数名+参数】;而匿名函数则是用【lambda+参数+表达式】的方式定义函数
lambda的语法只包含一个语句,格式如下:
lambda [arg1[,arg2,...,argN]]:expression
下面用几个例子对两种函数进行对比,以便大家理解。
用def格式写
#用def格式写
def func(x,y):
return x*y
func(2,3)
#6
用匿名函数写
#用匿名函数写
func=lambda x,y:x*y
func(3,4)
#12
从上面例子可以看出匿名函数相较标准函数的优点有:
- 不用取名称,因为给函数取名是比较头疼的一件事,特别是函数比较多的时候
可以直接在使用的地方定义,如果需要修改,直接找到修改即可,方便以后代码的维护工作 - 语法结构简单
函数的返回值
return[表达式]语句用于退出函数,选择性的向调用方返回一个表达式。不带参数值的return语句返回None。
下面演示一下return语句的用法:
def sum(arg1,arg2):
"返回两个参数的和"
total=arg1+arg2
print('函数内:',total)
return total
#调用sum函数
total=sum(23,32)
print('函数外:',total)
#函数内: 55
#函数外: 55
函数的说明文档
定义:即对函数进行简单的解释说明(一般对参数和返回值进行说明)
作用:方便他人理解和自己日后的复读
语法结构如下:
def 函数名(参数):
'函数的说明文档内容'
函数体
return 返回值
对于函数的内置函数,可以使用help()函数查看其内置文档。
函数的嵌套
在函数中再定义一个函数
语法结构如下:
def outer():
def inner():
print('inner')
print('outer')
inner()
outer()
# inner() #该句会报错
#outer
#inner
一个案例
#一个案例
def outer():
str='Hello World'
def inner():
print(str)
return inner
info=outer()
info()
#Hello World
变量作用域
一个标识符的可见范围,就是标识符的作用域。一般常说的是变量的作用域。
函数中的局部作用域:local
嵌套函数中父级函数的局部作用域:enclosing
全局作用域:global
系统内置的变量:如 int、str、list 等关键字
局部变量:在某个函数内部定义,作用在函数内部。生命周期:从变量被创建开始到函数结束死亡。
全局变量:定义在.py模块内部,作用在整个.py模块。生命周期:从变量被创造开始到.py模块结束死亡。
案例演示
def Demo1():
num=1
print(num)
Demo1()
print(num) #该句报错。因为num是局部作用,所以在函数外面是找不到num这个变量的。
#1
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-56-d3aaf0ba8dea> in <module>
5
6 Demo1()
----> 7 print(num)
NameError: name 'num' is not defined
对上例的解释
num = 0
def Demo1():
num = 1
print("在demo1中的结果:",num)
def Demo2():
print("在demo2中的结果:",num)
Demo1()
Demo2()
#在demo1中的结果: 1
#在demo2中的结果: 0
从结果可以看出,我们在demo1中num = 1,只在demo1内部起作用,而全局变量num仍然是0;因为在python中,在函数内部改变全局变量的值,会默认的在函数内部创建一个新的变量,全局变量并没有改变。
要想要改变全局变量的值,需要在函数内部用global声明。
现修改上面代码如下:
num = 0
def Demo1():
global num
num = 1
print("在demo1中的结果:",num)
def Demo2():
print("在demo2中的结果:",num)
Demo1()
Demo2()
print("在函数外面中的结果:",num)
#在demo1中的结果: 1
#在demo2中的结果: 1
#在函数外面中的结果: 1
还有一种情况是当全局变量是可变数据类形,我们可以通过修改可变数据类型的方法,修改可变类型的内容,从而实现修改全局变量。
(这里因为可变数据类型改变时,会在内存中改变数据的值)
num_list = [1,2,3]
def Demo1(demo1_list):
demo1_list.append(4)
print("在demo1中的结果:",demo1_list)
def Demo2():
print("在demo2中的结果:",num_list)
Demo1(num_list)
Demo2()
print("在函数外面中的结果:",num_list)
#在demo1中的结果: [1, 2, 3, 4]
#在demo2中的结果: [1, 2, 3, 4]
#在函数外面中的结果: [1, 2, 3, 4]
列表 += 与 + 的区别
#+:
num_list = [1,2,3]
def Demo1(demo1_list):
demo1_list = demo1_list + [4,5,6]
print("在demo1中的结果:",demo1_list)
def Demo2():
print("在demo2中的结果:",num_list)
Demo1(num_list)
Demo2()
print("在函数外面中的结果:",num_list)
#在demo1中的结果: [1, 2, 3, 4, 5, 6]
#在demo2中的结果: [1, 2, 3]
#在函数外面中的结果: [1, 2, 3]
#+=:
num_list = [1,2,3]
def Demo1(demo1_list):
demo1_list += [4,5,6]
print("在demo1中的结果:",demo1_list)
def Demo2():
print("在demo2中的结果:",num_list)
Demo1(num_list)
Demo2()
print("在函数外面中的结果:",num_list)
#在demo1中的结果: [1, 2, 3, 4, 5, 6]
#在demo2中的结果: [1, 2, 3, 4, 5, 6]
#在函数外面中的结果: [1, 2, 3, 4, 5, 6]
因为:
对于+号操作,可变对象和不可变对象调用的都是__add__操作
对于+=号操作,可变对象调用add__,不可变对象调用的是__iadd(不可变对象没有iadd) __iadd__是原地修改
函数递归
定义:在调用一个函数的过程中直接或间接的调用该函数本身,称之为递归调用。
递归调用最多能调用999层。
#基础模型一
def func():
print('from func')
func() #直接调用自身
func()
#基础模型二
def func():
print('from func')
bar() #间接调用自身
def bar():
print("from bar")
func()
func()
虽然以上两中方式为函数递归的基础模型,但往往不能直接这样使用。因为没有一个函数的结束条件,仅仅相当于一个死循环。
递归分为两个重要的阶段: 递推+回溯
递推:函数不断减少问题规模直至最终的终止条件。
回溯:拿到最终明确的值后,返回给上次调用进行处理,直至初始层。
下面为一个练习题:解决年龄问题,求出Jack的年龄
"""
Jack 他比小王 大两岁。 4 age(3) + 2
小王 他比大枫 大两岁。 3 age(2) + 2
大枫 他比美丽 大两岁。 2 age(1) + 2
美丽:我今年18. 1 18
"""
def age(n):
if n == 1:
return 18
else:
return age(n-1) + 2
print(age(4))
#24
注意在Python:
1、递归调用必须有一个明确的结束条件
2、在python中没有尾递归优化,递归调用的效率不高
3、进入下一次递归时,问题的规模必须降低
再来一个例子
输入任意一个数,让其除以2,直到不能再除
def cal(num):
if num%2==0:#先判断能不能整除
num=num//2
return cal(num)
else:
return num
print(cal(8))
#1
小作业:
定义并调用两个函数*fib(n)和PrintFN(m,n)*,输出指定的斐波那契数信息(斐波那契数列:1,1,2,3,5,8,…不了解的自行百度)。函数要求:
fib(n)*:返回斐波那契数列的第n个值
PrintFN(m,n)*:用列表返回[m, n]中的所有斐波那契数。
例如输入:20 100 6,输出:
fib(6) = 13 4
解决汉诺塔问题(汉诺塔规则请自行百度):
有ABC三根柱子,将A柱上的n个盘子移动到C柱。控制台输入盘子个数n,输出移动的所有步骤,格式样例“A–>C”。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶(给定n为正整数),每次可以爬 1 或 2 个台阶。编写函数,返回有多少种不同的方法可以爬到楼顶。
def climbStairs(n): dp = [] dp.append(1) # 初始状态,只有1阶的时候有一种走法 dp.append(2) # 有2阶的时候有两种走法 if n==1: #终止条件 return 1 if n==2: #终止条件 return 2 for i in range(2,n): dp.append(dp[i-1]+dp[i-2]) #递归 return dp[-1]
以下代码是否能执行,原因是?如何修改?
def fun1(): x = 3 def fun2(): x *= x return x return fun2() print(fun1())
写出以下代码的执行结果:
def fun2():
print(2)
def fun3():
print(6)
print(4)
fun3()
print(8)
print(3)
fun2()
print(5)