函数(下)1. Python的乐高积木2. 灵活即强大3. 我的地盘听我的4. 内嵌函数和闭包4.1 global关键字4.2 内嵌函数4.3 闭包(closure)5. lambda表达式6. 递归7. 介绍两个BIF:filter()和map()零基础入门学Python系列内容的学习目录 → \rightarrow →零基础入门学Python系列内容汇总。
需要学习的基础知识有:函数、参数、闭包、lambda表达式、递归等。因本部分内容较多,故分为上下两个篇章。
1、2、3部分内容见零基础入门学Python(六)—— 函数(上)
4、5、6、7部分内容见零基础入门学Python(六)—— 函数(下)
前半部分内容见零基础入门学Python(六)—— 函数(上)。
4. 内嵌函数和闭包 4.1 global关键字全局变量的作用域是整个模块(整个代码段),也就是代码段内所有的函数内部都可以访问到局部变量。但要注意的一点是,在函数内部仅仅去访问全局变量就好,不要试图去修改它。因为那样的话,Python会使用屏蔽(Shadowing)的方式“保护”全局变量:一旦函数内部试图修改全局变量,Python就会在函数内部自动创建一个名字一模一样的局部变量,这样修改的结果只会修改到局部变量,而不会影响到全局变量。看下面的例子:
example1: >>>count = 5
>>>def myFun():
count = 10
print(count)
>>>myFun()
10
>>>count
5
在函数中修改全局变量可能会导致程序可读性变差、出现莫名其妙的BUG、代码的维护成本提高,但仍然觉得有必要在函数中去修改这个全局变量的情况下,那么不妨可以使用global
关键字来达到目的。修改程序如下:
example2: >>>count = 5
>>>def myFun():
global count
count = 10
print(count)
>>>myFun()
10
>>>count
10
Python的函数定义是可以嵌套的,也就是允许在函数内部创建另一个函数,这种函数叫作内嵌函数或者内部函数。看一个例子:
example1: >>>def fun1():
print(" fun1()正在被调用…")
def fun2():
print(" fun2()正在被调用…")
fun2()
>>> fun1()
fun1()正在被调用…
fun2()正在被调用…
内部函数整个作用域都在外部函数之内,即上面例子中的fun2()
整个函数的作用域都在fun1()
里边。需要注意的地方是,除了在fun1()
这个函数体中可以随意调用fun2()
这个内部函数外,出了fun1()
就没有任何可以对fun2()
进行的调用。如果在fun1()
外部试图调用内部函数fun2()
,就会报错:
>>> fun2()
Traceback (most recent call last):
file “<pyshell#38>”, line 1, in < module >
fun2()
nameError: name ‘fun2’ is not defined
闭包(closure)是函数式编程的一个重要的语法结构,函数式编程是一种编程范式,著名的函数式编程语言就是liSP语言。Python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。看个例子:
example1: >>>def funX(x):
def funY(y) :
return x * y
return funY
>>> i = funX(8)
>>> i(5)
40
也可以直接写为:
>>> i = funX(8)(5)
40
通过上面的例子理解闭包的概念:如果在一个内部函数里(funY
就是这个内部函数)对外部作用域(但不是在全局作用域)的变量进行引用(x
就是被引用的变量,x
在外部作用域funX
函数里面,但不在全局作用域里),则这个内部函数(funY
)就是一个闭包。
使用闭包需要注意的是:因为闭包的概念就是由内部函数而来的,所以我们也不能在外部函数以外的地方对内部函数进行调用,下面的做法是错误的:
>>> funY(5)
Traceback (most recent call last):
file “< pyshell#39>”, line 1, in < module >
funY(5)
nameError: name ‘funY’ is not defined
在闭包中,外部函数的局部变量对应内部函数的局部变量,事实上相当于之前的全局变量跟局部变量的对应关系。在内部函数中,只能对外部函数的局部变量进行访问,但不能进行修改。
example2: >>>def funX( ):
x = 5
def funY( ) :
x *= x
return x
return funY
>>>funX( )( )
Traceback (most recent call last):
file “<pyshell#7>”, line 1, in < module >
funX()()
file “<pyshell#6>”, line 4, in funY
x *= x
UnboundLocalError: local variable ‘x’ referenced before assignment
这个报错信息跟之前全局变量的时候基本一样,Python认为在内部函数的x
是局部变量的时候,外部函数的x
就被屏蔽了起来,所以执行x *= x
的时候,在右边根本就找不到局部变量x
的值,因此报错。
在python3以前并没有直接的解决方案,只能间接地通过容器类型来存放,因为容器类型不是放在栈里,所以不会被“屏蔽”掉。之前学习的字符串、列表、元组都可以往里的放的就是容器类型,于是可以把代码修改为如下:
example3: >>>def funX( ):
x = [ 5 ]
def funY( ) :
x[ 0 ] *= x[ 0 ]
return x[ 0 ]
return funY
>>>funX( )( )
25
到了python3中,如果希望内部函数里可以修改外部函数里的局部变量的值,那么可以使用nonlocal
关键字:
example4: >>>def funX( ):
x = 5
def funY( ) :
nonlocal x
x *= x
return x
return funY
>>>funX( )( )
25
Python允许使用lambda关键字来创建匿名函数。
example1: >>>def ds(x):
return 2 * x + 1
>>>ds(5)
11
如果使用lambda语句来定义这个函数:
example2: >>>lambda x : 2 * x + 1
<function < lambda > at 0x000002BBE5C75048>
Python的lambda表达式语法非常精简,基本语法是在冒号( :
)左边放原函数的参数,可以有多个参数,用逗号( ,
)隔开即可,冒号右边是返回值。lambda语句返回的是一个函数对象,如果要对它进行使用,只需要进行简单的赋值 *** 作即可:
example3: >>>g = lambda x : 2 * x + 1
>>> g(5)
11
下面是lambda表达式带两个参数的例子:
example4: >>> def add(x, y):
return x + y
>>> add(3, 4)
7
>>> # 把它转换为lambda表达式:
>>>g = lambda x, y : x + y
>>>g(3, 4)
7
lambda表达式的作用:
Python写一些执行脚本时,使用lambda就可以省下定义函数过程,使得代码更加精简;对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数,使用lambda就不需要考虑命名的问题;简化代码的可读性,由于阅读普通函数经常要跳到开头def定义的位置,使用lambda函数可以省去这样的步骤。6. 递归递归从原理上来说就是函数调用自身的行为。
example1: >>>def recursion():
recursion()
>>> recursion()
Traceback (most recent call last):
file “<pyshell#8>”, line 1, in < module >
recursion()
file “<pyshell#7>”, line 2, in recursion
recursion()
file “<pyshell#7>”, line 2, in recursion
recursion()
file “<pyshell#7>”, line 2, in recursion
recursion()
[PrevIoUs line repeated 990 more times]
RecursionError: maximum recursion depth exceeded
这个例子是初学者使用递归时最容易出现的错误,从理论上说,这个程序将永远执行下去直至耗尽所有内存资源。不过python3处于“善意的保护”,对递归的深度默认限制是100层,所以我们的代码才会停下来。不过如果写网络爬虫等工具,可能会爬很深,那么我们可以自己设置递归的深度限制。方法如下:
example2: >>>import sys
>>>sys.setrecursionlimit(1000000) # 将递归限制设置为100万层
例1:写一个求阶乘的函数。
非递归版本def recursion(n): result = n for i in range(1, n): result *= i return resultnumber = int(input('请输入一个整数:'))result = recursion(number)print("%d的阶乘是:%d" % (number, result))
程序实现结果如下:
请输入一个整数:10
10的阶乘是:3628800
def factorial(n): if n == 1: return 1 else: return n * factorial(n-1)number = int(input('请输入一个整数:'))result = factorial(number)print("%d的阶乘是:%d" % (number,result))
程序实现结果如下:
请输入一个整数:10
10的阶乘是:3628800
上面这个例子满足了递归的两个条件:
(1) 调用函数本身。
(2) 设置了正确的返回条件。
递归的实现可以是函数自身调用自身,每次函数的调用都需要进行压栈、d栈、保存和恢复寄存器的栈 *** 作,所以在这上边是非常消耗时间和空间的。另外,如果递归一旦忘记了返回,或者错误地设置了返回条件,那么执行这样的递归代码就会变成一个无底洞:只进不出!但是当递归用在妙处的时候,可以使代码简洁、精练。
例2:用递归实现斐波那契数列。
迭代实现def fab(n): a1 = 1 a2 = 1 a3 = 1 if n < 1: print("输入有误!") return -1 while (n-2) > 0: a3 = a1 + a2 a1 = a2 a2 = a3 n -= 1 return a3n=int(input("请输入经过的月份:"))result = fab(n)if result != -1: print('总共有%d对小兔子诞生!'% result)
递归实现def fab(n): if n < 1: print("输入有误!") return -1 if n == 1 or n == 2: return 1 else: return fab(n-1) + fab(n-2)n = int(input("请输入经过的月份:"))result = fab(n)if result != -1: print('总共有%d对小兔子诞生!'% result)
可以看出使用递归实现逻辑非常简单,但是当我们把n设为35时,程序执行起来就非常耗时了,而此时用迭代代码实现基本上是毫秒级的。
7. 介绍两个BIF:filter()和map() 1. filter()filter()内建函数是一个过滤器,通过过滤器可以保留我们所关注的信息,把其他不感兴趣的东西直接丢掉。
>>> help(filter)
example1: >>> temp = filter(None, [1, 0, False, True])
>>>List(temp)
[1, True]
利用filter(),尝试写一个筛选奇数的过滤器:
example2: >>> def odd(x):
return x % 2
>>> temp = filter(odd, range(10))
>>> List(temp)
[1, 3, 5, 7, 9]
学习lambda表达式后,可以把上述过程转化成一行:
example3: >>>List(filter(lambda x : x % 2, range(10)))
[1, 3, 5, 7, 9]2. map()
在编程领域,map一般作“映射”来解释。map()这个内置函数也有两个参数,仍然是一个函数和一个可迭代序列,将序列的每一个元素作为函数的参数进行运算加工,直到可迭代序列的每个元素都加工完毕,返回所有加工后的元素构成的新序列。
example4: >>> List(map(lambda x : x * 2, range(10)))
>>>List(temp)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 总结
以上是内存溢出为你收集整理的零基础入门学Python(六)—— 函数(下)全部内容,希望文章能够帮你解决零基础入门学Python(六)—— 函数(下)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)