Python学习笔记(5)之函数(二)
0.日常吐槽
十一小长假真的不要出去玩啊,你会见到你一年也见不到的那么多人,大概就是这样的
这样的
这样的
所以还是安安静静待在宿舍好。
不说了,我收拾东西准备明天去天津了。。。
接着上次的来
5. 内嵌函数和闭包
所谓内嵌函数就是函数内部的函数。之前写到,一个函数内部就是一个空间,空间中的变量是局部变量,一般不会与外部的变量发生关系。自然地,内嵌函数的作用域也是外部函数的内部空间,也就是说内嵌函数的调用只能在外部函数内。
def fun1():
print('这是外部函数')
def fun2():
print('这是内部函数')
fun2()
fun1()
================ RESTART: E:/Academics/python/codes/test3.py ================
这是外部函数
这是内部函数
在函数外调用则会出错
def fun1():
print('这是外部函数')
def fun2():
print('这是内部函数')
fun2()
================ RESTART: E:\Academics\python\codes\test3.py ================
Traceback (most recent call last):
File "E:\Academics\python\codes\test3.py", line 5, in <module>
fun2()
NameError: name 'fun2' is not defined
5.1 数据的读取
函数中可以读取全局变量,那么内嵌函数能否读取全局变量或者外部函数的局部变量?如果两者同时存在读取的顺序是什么样的呢?下面做几个实验。
test = 0
def fun1():
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
0
结论:内嵌函数可以读取全局变量。
def fun1():
test = 1
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
结论:内嵌函数可以读取外部函数的局部变量。
test = 0
def fun1():
test = 1
def fun2():
print(test)
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
test = 0
def fun1():
test = 1
def fun2():
test = 2
def fun3():
print(test)
fun3()
fun2()
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
2
结论:内嵌函数读取变量时体现了一种“就近原则”。
5.2 nonlocal 与 global
global
声明函数中的变量为全局变量,而nonlocal
代指上一层变量。
def fun1():
test = 1
def fun2():
nonlocal test
test = 2
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
1
2
再举一个例子:
1. test = 0
2.
3. def fun1():
4. test = 1
5. def fun2():
6. global test
7. print(test)
8. test = 2
9. print(test)
10. fun2()
11.
12. print(test)
13. fun1()
14. print(test)
15. ================ RESTART: E:\Academics\python\codes\test2.py ================
16. 0
17. 1
18. 0
19. 2
简单分析下几次打印结果:
- 第一行定义了全局变量
test=0
,所以12行第一次打印结果为0 - 13行调用
fun1()
后,首先fun1里定义局部变量test=1
,接着13行打印局部变量test,得到第二次打印结果1 - fun1运行到第10行调用
fun2()
,接着fun2声明全局变量test=0
,从而第7行打印全局变量0 - fun2运行到第八行将全局变量test修改为2,至此fun1运行完毕,释放所有内存。14行打印全局变量test,结果为2。
总之,nonlocal
允许内嵌函数读取并修改上一层环境中的局部or全局变量,而global
则是直接跳到最外层读取和修改全局变量。
自然地有一个问题,要是想往外跳两层怎么办?我试验了一下使用两个nonlocal
是可以的,像下面这样:
test = 0
def fun1():
test = 1
def fun2():
nonlocal test
def fun3():
nonlocal test
test = 3
fun3()
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
3
3
或者干脆fun2里什么都不写也可以,如下:
test = 0
def fun1():
test = 1
def fun2():
def fun3():
nonlocal test
test = 3
fun3()
print(test)
fun2()
print(test)
fun1()
================ RESTART: E:\Academics\python\codes\test2.py ================
3
3
上面这个就很神奇,看上去像是运行fun3时nonlocal
向上一层找test没有找到,然后就自动找到了再上一层即fun1中的test=1
,然后修改了这个值。
无论哪一个做法都避免不了要干扰到fun2,不知没有直接让fun3去修改fun1中的值的办法,而保留fun2中的test=2不受影响?如果有大神看到这里知道怎么做的话希望不吝赐教!
5.3 闭包(closure)
闭包其实我也没太搞懂,就先简单写点,等弄懂了再来补充。
闭包从表现形式上定义为:如果在一个内部函数里对外部作用域(但不是全局作用域)的变量进行引用,并且外部函数的返回值是内部函数,那么内部函数就成称为闭包。
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
def FunX(x):
def FunY(y):
return x * y
return FunY
此时FunY
就称为闭包。如果对FunX
进行调用:
>>> i = FunX(8)
>>> type(i)
<class 'function'>
>>> i(5)
40
也可以直接写成:
>>> FunX(8)(5)
40
总结一下,闭包主要的两个特点是:
- 外部函数返回内部函数的引用
- 外部函数把临时函数绑定给内部函数
- 调用完外部函数后内存未释放
6. lambda表达式创建匿名函数
lambda
表达式的作用就是创建一个匿名函数,语法为lambda (parameter): (return value)
。例如:
>>> f = lambda x,y : x+y
>>> f(2,3)
5
6.1 filter过滤器
filter(function or None, iterable)
函数有两个参数,第一个参数是函数或None
,第二个参数是用于迭代的对象。当第一个参数是None时,则返回迭代对象中True的值;当第一个参数是函数时,则将迭代对象代入函数进行运算,并返回结果为True的原始值。因此filter可以和lambda函数结合起来使用。filter返回的是一个迭代对象,可用list
或tuple
等将其列举出来。
>>> a = filter(None , [1,0,True,False])
>>> list(a)
[1, True]
一个筛选奇数的程序
>>> num = range(10)
>>> list(filter(lambda x : x%2 , num))
[1, 3, 5, 7, 9]
6.2 map映射
map(function, iterable)
函数返回迭代对象经过函数运算后的值。同样可以和lambda表达式巧妙结合。
>>> list(map(lambda x : x**2 , range(10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
7.递归
在其他编程语言也常使用递归的方法。所谓递归便是在一个函数中调用它自身。递归最重要的是要有一个停止的返回值,否则会陷入死循环。写几个递归常见的例子:
- 求阶乘:
def factorial(n):
if n == 1:
return n
else:
return n * factorial(n-1)
number = int(input('请输入一个正整数:'))
result = factorial(number)
print('%d的阶乘是:%d' % (number, result))
============== RESTART: E:/Academics/python/codes/factorial.py ==============
请输入一个正整数:6
6的阶乘是:720
- 求Fibonacci数列
def fib(n):
if n==1 or n==2:
return 1
else:
return fib(n-1) + fib(n-2)
number = int(input('请输入一个正整数:'))
result = fib(number)
print('a(%d) = %d' % (number , result))
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
请输入一个正整数:3
a(3) = 2
>>>
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
请输入一个正整数:5
a(5) = 5
>>>
============== RESTART: E:/Academics/python/codes/Fibonacci.py ==============
请输入一个正整数:25
a(25) = 75025
- 汉诺塔
这是递归算法里十分经典的例子了。汉诺塔的基本规则是:将在柱X上的圆盘移到柱Z上面,每次只能移动任何一个柱子上面的一个圆盘,且必须保证小的在上大的在下。
Tower of Hanoidef hanoi(n,x,y,z):
if n==1:
print(x, '-->',z)
else:
hanoi(n-1,x,z,y) #将前n-1个圆盘从X移动到Y
print(x,'-->',z) #将最后一个圆盘从X移动到Z
hanoi(n-1,y,x,z) #将Y上的n-1个圆盘移动到Z
number=int(input('请输入圆盘个数:'))
hanoi(number,'X','Y','Z')
================ RESTART: E:/Academics/python/codes/hanoi.py ================
请输入圆盘个数:3
X --> Z
X --> Y
Z --> Y
X --> Z
Y --> X
Y --> Z
X --> Z
递归算法比较简练,也很适用于解决一些复杂问题。但缺点也是明显的,就是它会非常占用内存,比如Fibonacci数列里n稍微大一点程序就崩掉了。所有有时候老老实实用迭代还是蛮好的,也好理解,就是长了点而已。
往期回顾
Python学习笔记(0)之Hello,python!
Python学习笔记(1)之列表list
Python学习笔记(2)之元组、字典&集合
Python学习笔记(3)之字符串string
Python学习笔记(4)之函数(一)