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

简单分析下几次打印结果:

  1. 第一行定义了全局变量test=0,所以12行第一次打印结果为0
  2. 13行调用fun1()后,首先fun1里定义局部变量test=1,接着13行打印局部变量test,得到第二次打印结果1
  3. fun1运行到第10行调用fun2(),接着fun2声明全局变量test=0,从而第7行打印全局变量0
  4. 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

总结一下,闭包主要的两个特点是:

  1. 外部函数返回内部函数的引用
  2. 外部函数把临时函数绑定给内部函数
  3. 调用完外部函数后内存未释放

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返回的是一个迭代对象,可用listtuple等将其列举出来。

>>> 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.递归

在其他编程语言也常使用递归的方法。所谓递归便是在一个函数中调用它自身。递归最重要的是要有一个停止的返回值,否则会陷入死循环。写几个递归常见的例子:

  1. 求阶乘:
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
  1. 求Fibonacci数列

a_1=a_2=1
a_n=a_{n-1}+a_{n-2},n>2

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
  1. 汉诺塔

这是递归算法里十分经典的例子了。汉诺塔的基本规则是:将在柱X上的圆盘移到柱Z上面,每次只能移动任何一个柱子上面的一个圆盘,且必须保证小的在上大的在下。

Tower of Hanoi
def 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)之函数(一)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容

  • 原谅我到这么迟才开始写2016年的年度总结。 这一年,我18岁 。这一年,我仿佛走过了狗血电视剧中常走的套路。这...
    大花痴与小宁静阅读 226评论 0 1
  • 自己内心演绎了一场撕逼大戏。 故事情节一,外表风平浪静,你心仪演绎果1万场戏的某人。抓狂中。 故事情节二,刚才又喝...
    曼曼风雨阅读 64评论 0 0
  • 忽然间嗓子疼的难以入睡,凡间事太多,好想林间憩几天,终是要面对,还是省了林间憩,儿孙自有儿孙福,我还是看书,追剧,...
    黑妹wln阅读 147评论 0 0
  • 1 最熟悉的老人,应该就是他了,老尹,比我大了30岁。 心怀忐忑却故意漫不经心地问他,能不能成为我文章主角的时候,...
    婴儿看世界阅读 557评论 10 16
  • 7月3日 星期二 晴 我的导师说:爱情最重要属性是什么?是要“开心”。如果你爱的人只会给你烦恼,让你痛苦,那么...
    能量女王刘大红阅读 192评论 0 0