python入门笔记15——函数用法和底层分析(3)
函数用法和底层分析
lambda 表达式和匿名函数
lambda 表达式可以用来声明匿名函数。lambda 函数是一种简单的、在同一行中定义函数的方法。lambda 函数实际生成了一个函数对象。
lambda 表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。
lambda 表达式的基本语法如下:
arg1/arg2/arg3 为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。
f = lambda a,b,c:a+b+c
print(f)
print(f(1,2,3))
print((lambda a,b,c:a+b+c)(1,2,3))
运行结果
<function <lambda> at 0x0000023A1B6051F0>
6
6
g = [lambda x:x*2, lambda y:y*3, lambda z:z+1]
print(g[0](1), g[1](1), g[2](1))
运行结果
2 3 2
eval()函数
功能:
将字符串 str 当成有效的表达式来求值并返回计算结果。
语法:
eval(source[, globals[, locals]]) -> value。
参数:
source:一个Python 表达式或函数compile()返回的代码对象 。
globals:可选。必须是 dictionary 。
locals:可选。任意映射对象。
s = "print('abcde')"
eval(s)
x = 2
y = 3
z = eval("x+y")
print(z)
dict1 = {
"a":1, "b":2}
d = eval("a+b", dict1)
print(d)
abcde
5
3
递归函数
递归函数指的是:自己调用自己的函数,在函数体内部直接或间接的自己调用自己。递归类似于大家中学数学学习过的“数学归纳法”。 每个递归函数必须包含两个部分:
- 终止条件
表示递归什么时候结束。一般用于返回值,不再调用自己。 - 递归步骤
把第 n 步的值和第 n-1 步相关联。
递归函数由于会创建大量的函数对象、过量的消耗内存和运算能力。在处理大量数据时,谨慎使用。
代码示例:使用递归函数计算阶乘(factorial)

def factorial(n):
if n == 1:
return n
else:
return n * factorial(n-1)
if __name__ == "__main__":
print(factorial(5))
代码示例:实现汉诺塔
游戏规则:从左到右 A B C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面。
案例1
只有一个盘子时,则直接有A,移动到C 完成。
案例2
如果有两个盘子时,移动方案如下:
A—>B #借助B,帮助C拿到最后一个盘子
A—>C #最后一个盘子移动完成后,剩余的盘子已经全部到B上了
B—>C #将剩余的盘子移动到C上,完成
案例3
如果有三个盘子,盘子数量N = 3,移动方案如下:
如果有四个盘子,盘子数量N = 4,移动方案如下:
规律分析:
- 当盘子只有一个是,即N=1,只有一个动作,从A移动到C即结束。
- 当有N个盘子时:
- 上半部分: 移动一定和n-1盘子移动时的动作相同。出发地都是A,只不过,n-1个盘子移动的目的是B,而不是C。这样方便取出最后一个盘子
- 中间部分: 一定是由A移动到C
- 下半部分: 此时等待移动的盘子已经都在B上,而不是A上,移动步骤类似于n-1个盘子的移动。出发地是B,目的地是C
递归分析:
- 经过上面例子可以得到。如果想要移动n个盘子到指定目的,那么一定要先将n-1个盘子移动到备用柱上。
- 当n-1个盘子移动后,必定要将最后一个盘子移动到C上。
- 经过上面的调换后,剩下待处理的盘子还有n-1个,此时盘子已经在B上而不是在A上。此时需要从新判断剩余的盘子个数。如果依然大于1个那么还是需要按照1的步骤移动第n-1个盘子之上的所有盘子共n-2到A上。从而好让第n-1个盘子从B移动到C。
总结:到第3步时,发现和有和第1步第2步类似地方。只需要改变盘子的目的点和出发点,就能刚好递归。
def move(n,a,b,c):
if n == 1:
print(a,"--->",c)
else:
move(n-1,a,c,b)
print(a,"--->",c)
move(n-1,b,a,c)
if __name__ == "__main__":
move(3,"A","B","C")
运行结果
A ---> C
A ---> B
C ---> B
A ---> C
B ---> A
B ---> C
A ---> C
嵌套函数(内部函数)
&emsp嵌套函数: 在函数内部定义的函数!
代码示例:嵌套函数定义
def f1():
print("f1 is running")
def f2():
print("f2 is running")
f2()
f1()
f1 is running
f2 is running
上面程序中,f2()就是定义在 f1 函数内部的函数。f2()的定义和调用都在 f1()函数内部。
一般在什么情况下使用嵌套函数?
- 封装 - 数据隐藏
外部无法访问“嵌套函数”。 - 贯彻 DRY(Don’t Repeat Yourself) 原则
嵌套函数,可以让我们在函数内部避免重复代码。 - 闭包。
使用嵌套函数避免重复代码:
def printChineseName(name,familyName):
print("{0} {1}".format(familyName,name))
def printEnglishName(name,familyName):
print("{0} {1}".format(name, familyName))
#使用 1 个函数代替上面的两个函数
def printName(isChinese, familyname, name):
def inner_print(a, b):
print("{0}, {1}".format(a, b))
if isChinese:
inner_print(familyname, name)
else:
inner_print(name, familyname)
if __name__ == '__main__':
printName(True, "Feng", "San")
printName(False, "Feng", "Russell")
Feng, San
Russell, Feng
nonlocal 关键字
nonlocal 用来声明外层的局部变量。
global 用来声明全局变量。
使用 nonlocal 声明外层局部变量
#测试 nonlocal、global 关键字的用法
a = 100
def outer():
b = 10
def inner():
nonlocal b
print(b)
b = 20
inner()
print(b)
global a
print(a)
a = 200
outer()
print(a)
LEGB 规则
Python 在查找“名称”时,是按照 LEGB 规则查找的:
- Local 指的就是函数或者类的方法内部。
- Enclosed 指的是嵌套函数(一个函数包裹另一个函数,闭包)。
- Global 指的是模块中的全局变量。
- Built in 指的是 Python 为自己保留的特殊名称。
如果某个 name 映射在局部(local)命名空间中没有找到,接下来就会在闭包作用域(enclosed)进行搜索,如果闭包作用域也没有找到,Python 就会到全局(global)命名空间中进行查找,最后会在内建(built-in)命名空间搜索 (如果一个名称在所有命名空间中都没有找到,就会产生一个 NameError)。
#测试LEGB
# str = "global"
def outer():
# str = "outer"
def inner():
# str = "inner"
print(str)
inner()
outer()
依次修改注释后,可以观察控制台打印的内容,体会 LEBG 的搜索顺序。