函数和过程
在许多编程语言中,函数和过程其实是分开的。一般认为函数(function)是有返回值的,而过程(procedure)是简单、特殊并且没有返回值的。Python严格来说,只有函数,没有过程。举个例子:
调用print(hello())之后打印了两行文字,第一行我们当然知道是hello()函数执行的,第二行的None是怎么回事呢?其实,当不写return语句的时候,默认Python会认为函数是return None的。所以说Python所有的函数都有返回值。
函数变量的作用域
也许大家已经听说了局部变量和全局变量,变量的作用域也就是平常所说的变量可见性,一般的编程语言都有局部变量(Local Variable) 和 全局变量(Global Variable) 之分。
# 计算折扣价
def discounts(price, rate):
final_price = price * rate
return final_price
old_price = float(input("请输入原价:"))
rate = float(input("请输入折扣率:"))
new_price = discounts(old_price, rate)
print("打折后的价格是:", new_price)
其执行结果如下:
局部变量
这是一个简单的计算折扣价的代码段,来分析一下代码:在函数discounts()中,两个参数是price和rate,还有一个是final_price,它们都是discounts()函数中的局部变量。为什么称为局部变量呢,不妨修改一下代码:
# 计算折扣价
def discounts(price, rate):
final_price = price * rate
return final_price
old_price = float(input("请输入原价:"))
rate = float(input("请输入折扣率:"))
new_price = discounts(old_price, rate)
print("打折后的价格是:", new_price)
# 添加一行试图打印局部变量final_price的值
print("打印局部变量final_price的值:", final_price)
可以看到程序像刚才一样输出之后便报错了,错误原因是:final_price没有被定义过,也就是说,Python找不到final_price这个变量。这是因为final_price只是一个局部变量,它的作用只在它的地盘上——discounts()函数的定义范围内——有效,出了这个范围,就不属于它的地盘了,它将什么都不是。
总结一下:在函数里边定义的参数以及变量,都称为局部变量,出了这个函数,这些变量都是无效的。 事实上的原理是,Python在运行函数的时候,利用栈(Stack)进行存储,当执行完该函数后,函数中的所有数据都会被自动删除,所以在函数外边是无法访问到函数内部的局部变量的。
全局变量
与局部变量相对的是全局变量,程序中的old_price、new_price、rate都是在函数外边定义的,它们都是全局变量,全局变量拥有更大的作用域,例如在函数中可以访问到它们:
# 计算折扣价
def discounts(price, rate):
final_price = price * rate
# 添加一行试图打印全局变量old_price的值
print("打印全局变量old_price的值:", old_price)
return final_price
old_price = float(input("请输入原价:"))
rate = float(input("请输入折扣率:"))
new_price = discounts(old_price, rate)
print("打折后的价格是:", new_price)
其执行结果为:
可以看到全部变量的访问范围更广,不过使用全局变量的时候要千万小心,在Python中,你可以在函数中肆无忌惮的访问一个全局变量,但是如果你试图去修改它,就会出现下面这样的结果:
# 计算折扣价
def discounts(price, rate):
final_price = price * rate
old_price = 50 # 这里试图修改全部变量
print("在局部变量中修改后的old_price的值是:", old_price)
return final_price
old_price = float(input("请输入原价:"))
rate = float(input("请输入折扣率:"))
new_price = discounts(old_price, rate)
print("全局变量old_price现在的值是:", old_price)
print("打折后的价格是:", new_price)
其执行结果为:
出现这个结果的原因是:如果在函数内部试图修改全局变量,那么Python会创建一个新的局部变量替代(名字跟全局变量相同),但真正的全部变量是没有改变的,所以实现会和大家预期的不同。
总结一下:全局变量在整个代码段中都是可以访问到的,但是不要试图在函数内部去修改全局变量的值,因为那样Python会自动在函数内部新建一个名字相同的局部变量代替。
global关键字
上面提到,全局变量的作用域是整个模块,也就是代码段内所有的函数内部都可以访问到全局变量。但需要注意的是,在函数内部仅仅访问全局变量可以,不要试图去修改它。因为那样的话,Python会使用 屏蔽(Shadowing) 的方式“保护”全局变量:一旦函数内部试图修改全局变量,Python就会在函数内部自动创建一个名字一样的局部变量,这样修改的结果只会修改到局部变量,而不会影响全局变量。举个小例子:
如果你认为有必要在函数中去修改这个全局变量,那么你不妨可以使用global关键字来达到目的。
内嵌函数(内部函数)
Python的函数定义是可以嵌套的,也就是允许在函数内部创建另一个函数,这种函数叫作内嵌函数或者内部函数。下面看一个简单的内嵌函数的例子:
关于内部函数的使用,有一个比较值得注意的地方,就是内部函数整个作用域都在外部函数之内,就像上述例子中的fun2()整个函数的作用域都在fun1()里边。除了在fun1()这个函数体中可以随意调用fun2()这个内部函数外,出了fun1(),就没有任何可以对fun2()进行的调用。
如果在fun1()外调用内部函数fun2(),就会报错:
闭包(closure)
闭包(closure)是函数式编程的一个重要的语法结构,函数式编程是一种编程范式,不同的编程语言实现闭包的方式不同,Python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。举个例子:
也可以这样写:
通过上面的例子来理解闭包的概念:如果在一个内部函数里(funY就是这个内部函数)对外部作用域(但不是在全局作用域)的变量进行引用(x就是被引用的变量,x在外部作用域funX函数里面,但不在全局作用域里),则这个内部函数(funY)就是一个闭包。
使用闭包需要注意的是,因为闭包的概念就是由内部函数而来的,所以也不能在外部函数以外的地方对内部函数进行调用:
在闭包中,外部函数的局部变量对应内部函数的局部变量,事实上相当于之前讲的全局变量跟局部变量的对应关系,在内部函数中,只能对外部函数的局部变量进行访问,但不能进行修改。
再看一个例子:
这个报错信息跟之前讲解全局变量的时候基本一样,Python认为在内部函数(funY)的x是局部变量的时候,外部函数(funX)的x就被屏蔽了起来,所以执行 x *= x 的时候,在右边根本就找不到局部变量x的值,因此报错。
而在Python3中,对于这个问题,也有解决办法——nonlocal关键字。
如果希望在内部函数里可以修改外部函数里的局部变量的值,那么就可以使用nonlocal关键字,使用方式和global关键字一样:
lambda表达式
Python允许使用lambda关键字来创建匿名函数。
Python中的lambda表达式语法非常精简,其基本语法是:
参
数
1
,
参
数
2
,
.
.
.
:
返
回
值
参数1,参数2,... : 返回值
参数1,参数2,...:返回值
在冒号(:)左边放原函数的参数,可以有多个参数,用逗号(,)隔开即可;冒号右边是返回值。
给一个简单的例子:
其写为lambda表达式来定义这个函数,就会变为这样:
我们发现lambda语句实际上是返回一个函数对象,如果要对它进行使用,只需要进行简单的赋值操作即可:
当然,如果有多个参数直接用逗号隔开即可:
lambda表达式的作用:
1、 Python写一些执行脚本时,使用lambda就可以省下定义函数过程,就不需要专门定义一个函数然后再写调用,使用lambda就可以使得代码更加精简。
2、 对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数,有时候给函数起名是个比较头疼的问题,使用lambda就不需要考虑命名的问题了。
3、 简化代码的可读性,由于阅读普通函数经常要跳到开头def定义的位置,使用lambda函数可以省去这样的步骤。
两个常用BIF:filter()和map()
接下来介绍两个比较实用的BIF:filter()和map()。
一、filter()
这一个内建函数是一个过滤器。我们每天会接触大量的数据,通过过滤器,就可以保留你所关注的信息,把其他不感兴趣的东西直接丢掉。那么Python中的这个filter()是怎么实现这个功能呢?先来看一下Python给出的函数部分注释:
这里只截取了关于函数用法的部分,大概意思是:filter有两个参数,第一个参数可以是一个函数也可以是None,如果第一个参数是一个函数的话,则将第二个可迭代数据里的每一个元素作为函数的参数进行计算,把返回True的值筛选出来;如果第一个参数为None,则直接将第二个参数中为True的值筛选出来。
举几个简单例子:
1、 第一个参数为None时
2、利用filter(),写一个筛选奇数的过滤器:
在学习了lambda表达式后,可以将上述过程转换为一行:
二、map()
map()这个内置函数也有两个参数,仍然是一个函数和一个可迭代序列,将序列的每一个元素作为函数的参数进行运算加工,直到可迭代序列每个元素都加工完毕,返回所有加工后的元素构成的新序列。
其用法和filter()类似,举个简单的例子: