Python 学习笔记之 函数
Contents
要点概括:
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性和代码的重复利用率。
我们可以使用Python提供的内建函数,如
print()
,也可以自己创建自定义函数。
1.定义函数
在定义函数的时候需要注意一下规则:
- 函数代码块以
def
关键词开头,后接函数标识符名称和圆括号()
。 - 任何传入参数和自变量必须放在圆括号内,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串,该字符串用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- 可以使用
return [表达式]
结束函数,选择性地返回一个值给调用方。不带表达式的return
相当于返回None
。
定义一个函数用def
关键字,格式如下:
|
|
默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。下面定义一个函数用来输出Hello World!
,如下所示:
|
|
输出结果如下:
|
|
下面定义一个带有参数的函数,如下所示:
|
|
输出结果如下:
|
|
2.函数调用
在调用函数的时候,通常需要知道函数的名称和参数。如果传入的参数数量不对,或者传入的参数数量对了而参数类型不能被函数接受,则程序就会报错,如下所示:
|
|
输出结果如下:
|
|
3.参数传递
3.1可变对象与不可变对象
在 Python 中,可变对象与不可变对象如下所示:
- 可变对象:List(列表)、Set(集合)、Dictionary(字典)
- 不可变对象:Number(数字)、String(字符串)、Tuple(元组)
(1)对于不可变对象,如变量赋值a = 1
后再赋值a = 2
,这里实际是新生成了一个int
对象2
,再让a
指向它,而1
被丢弃,不是改变a
的值,而是新生成了a
。
(2)对于可变对象,如变量赋值list = [1, 2, 3, 4,]
后再赋值list[2] = 5
,相当于更改了列表list
的第三个元素的值,而列表list
本身没有动,只是其内部的一部分值被修改了。
在Python参数传递的时候,对于可变对象与不可变对象有如下说明:
此外:
(1)对于不可变对象,如fun(a)
,传递的只是a
的值,没有影响a
对象本身。比如在fun(a)
内部修改a
的值,只是修改另一个复制的对象,不会影响a
本身。
(2)对于可变对象,如fun(list)
,则是将list
真正的传过去,修改后fun
外部的list
也会受影响。
Python中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
3.2不可变对象的传递
下面给出对不可变对象进行传递,如下所示:
|
|
实例中有int
对象2
,指向它的变量是b
,在传递给change_int
函数时,按传值的方式复制了变量b
,a
和b
都指向了同一个int
对象,在 a=10
时,则新生成一个int
值对象10
,并让a
指向它。
3.3可变对象的传递
可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了,如下所示:
|
|
输出结果如下:
|
|
可以看出,传入函数的对象和在末尾添加新内容的对象用的是同一个引用。
4.参数类型
以下是调用函数时可使用的参数类型:
- 必需参数
- 关键字参数
- 默认参数
- 不定长参数
4.1必需参数
必需参数要用正确的顺序传入函数,调用时的数量必须和声明时的一样,如下所示:
|
|
4.2关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
下面使用参数名来进行函数调用,如下所示:
|
|
输出结果如下:
|
|
在进行函数调用的时候也可以不需要指定参数顺序,如下所示:
|
|
输出结果如下:
|
|
4.3 默认参数
调用函数时,如果没有传递参数,则会使用默认参数。
注意:默认参数必须放在最后,否则会报错。
以下实例中如果没有传入age
参数,则使用默认值,如下所示:
|
|
输出结果如下:
|
|
4.4不定长参数
你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述两种参数不同,声明时不会命名。基本语法如下所示:
|
|
加了星号*
的参数会以元组(Tuple)的形式导入,存放所有未命名的变量参数。如下所示:
|
|
输出结果如下:
|
|
如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量,如下所示:
|
|
输出结果如下:
|
|
另外一种带有两个星号**
的参数会以字典的形式导入,基本语法如下所示:
|
|
示例如下所示:
|
|
输出结果如下:
|
|
在声明函数时,参数中星号*
可以单独出现,如下所示:
|
|
但是如果单独出现星号*
,则它后面的参数必须用关键字传入,如下所示:
|
|
5.匿名函数
Python
使用lambda
来创建匿名函数,不再使用def
语句定义一个函数。
使用lambda
时应注意以下几点:
lambda
只是一个表达式,函数体比def
简单很多。lambda
的主体是一个表达式,而不是一个代码块。仅仅能在lambda
表达式中封装有限的逻辑进去。lambda
函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。- 虽然
lambda
函数看起来只能写一行,却不等同于C
或C++
的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda
函数的语法只包含一个语句,具体格式如下所示:
|
|
一个简单的lambda
实例如下所示:
|
|
输出结果如下:
|
|
当然,lambda
匿名函数也是可以使用关键字参数
进行参数传递,如下所示:
|
|
同样的,lambda
匿名函数也是可以设定默认值,如下所示:
|
|
输出结果如下:
|
|
所以,如果只打算给其中一部分参数设定默认值,那么应当将其放在靠后的位置(和定义函数时一样,避免歧义),否则会报错。
6.return 语句
return [表达式]
语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return
语句返回None
。
下面给出return
语句的用法,如下所示:
|
|
输出结果如下:
|
|
7.变量作用域
Python 中的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。
Python 的作用域一共有4种,分别是:
- L(Local)局部作用域
- E(Enclosing)闭包函数外的函数中
- G(Global)全局作用域
- B(Built-in)内建作用域
以L–>E–>G–>B
的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。作用范围示例如下所示:
|
|
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下所示:
|
|
输出结果如下:
|
|
可以看出,实例中msg
变量定义在if
语句块中,但外部还是可以访问的。下面将msg
定义在函数内,则它就是局部变量,外部不能访问,如下所示:
|
|
7.1全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域
,定义在函数外的拥有全局作用域
。局部变量只能在其被声明的函数内部
访问,而全局变量可以在整个程序范围
内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中,如下所示:
|
|
输出结果如下:
|
|
7.2global 和 nonlocal 关键字
当内部作用域想修改外部作用域的变量时,就要用到global
和nonlocal
关键字了。如果想为一个在函数外的变量重新赋值,并且这个变量会作用于许多函数中时,就需要告诉Python这个变量的作用域是全局变量。此时用global
语句就可以变成这个任务,如下所示:
|
|
输出结果如下:
|
|
如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量,则需要nonlocal
关键字了。也就是说,nonlocal
关键字表示否定当前命名空间的作用域,寻找父函数的作用域并绑定对象;使用nonlocal
关键字可以在一个嵌套的函数中修改嵌套作用域中的变量。
我们先来看一个例子,如下所示:
|
|
上面的程序中,在嵌套的inner()
函数内,count = 12
会创建一个新的变量,但是我们想要使用count = 12
,也就是想要输出结果为12,那么就需要用到nonlocal
关键字了,如下所示:
|
|
还有一种情况,如下所示:
|
|
之所以出现局部作用域引用错误,是在因为test()
函数中,a
使用的是局部变量,未定义,无法修改。但我们可以通过修改a
为局部变量,通过函数参数传递即可是程序正常运行,如下所示:
|
|