为函数提供文档:
>>> def max_my(x,y):
... '''
... aaa
... '''
... return x,y
...
>>> help(max_my)
Help on function max_my in module __main__:
max_my(x, y)
aaa
(END)
多个返回值:
程序需要有 多个返回值 ,则既可将多个值包装成列表之后返回,也可直接返回多个值 。 如果 Python 函数直接返回多个值, Python 会自动将多个返回值封装成元组。
递归函数:
递归是非常有用的,例如程序希望遍历某个路径下的所有文件,但这个路径下的文件夹的深度是未 知的,那么就可以使 用递归来实现这个需求。系统可定义一个函数,该函数接收一个文件路径作为参数,该函数可遍历出当前路径下的所有文件和文件路径一一-在该函数的函数体中再次调用函数自身来 处 理该路径 下的所有文件路径。递归一定要向己知方向进行。
函数的参数:
注:Python 要求将带默认值的参数定义在形参列表的最后 。
按照形参位置传入的参数被称为位置参数。如果使用位置参数的方式来传入参数值,则必须严格按照走义函数时指定的顺序来传入参数值:如果根据参数名来传入参数值,则无须遵守定义形参的顺序,这种方式被称为关键字参数。需要说明的是,如果希望在调用函数时混合使用关键字参数和位置参数,则关键字参数必须位于位置参数之后 。 换句话说,在关键字参数之后的只能是关键字参数 。由于 Python 要求在调用函数时关键字参数必须位于位置参数的后面,因此在定义函数时指定了默认值的参数(关键字参数)必须在没有默认值的参数之后 。
参数收集(个数可变的参数):
Python 允许在形参前面添加一个星号(*),这样就意味着该参数可接收多个参数值,多个参数值被当成元组传入。
>>> def test(a,*books):
... print(books)
... for b in books:
... print(b)
... print(a)
...
>>> test(5,"qqq","www")
('qqq', 'www')
qqq
www
5
>>>
由上面看,参数收集的本质就是 一个元组 ,Python 会将传给 books 参数的多个值收集成一 个元组 。
Python 允许个数可变的形参可以处于形参列表的任意位置(不要求是形参列表的最后一个参数〉,但 Python 要求一个函数最多只能带一个支持“普通”参数收集的形参。函数的第 一 个参数是个可变的形参,由于该参数可接收个数不等 的 参数值,因此如果需要给后面的参数传入参数值,则必须使用关键字参数(以参数名传参) ; 否 则 ,程序会把所传入的多个值都当成是传给 参数收集的形参。
Python 还可以收集关键宇参数,此时 Python 会将这种关键宇参数收集成字典。为了让 Python能收集关键字参数,需要在参数前面添加两个星号。在这种情况下,一个函数可同时包含一个支持“普通”参数收集的参数和 一个支持关键字参数收集的参数。
>>> def test(x,y,z=3,*books,**scores):
... print(x,y,z)
... print(books)
... print(scores)
...
>>> test(1,2,3,"111","222",语文=89,数学=94)
1 2 3
('111', '222')
{'语文': 89, '数学': 94}
逆向参数收集:指的是在程序己 有 列表、元组、 宇典等对象 的前提下,把它们的元素“ 拆开”后传给函数的参数
>>> def test(name,message):
... print("user:",name)
... print("message:",message)
...
>>> my_list = ["111","222"]
>>> test(*my_list)
user: 111
message: 222
为了让程序将 my_list 列表 的两个元素传给 test() 函数,程序在传入 的 my_list 参数之前添加 了一个 星号。
实际上,即使是支持收集的参数,如果程序需要将 一个元组传给该参数,那么同样需要使用逆向收集:
>>> def foo(name,*nums):
... print("name参数",name)
... print("name参数",nums)
...
>>> my_tuple = (1,2,3)
#使用逆向收集将 my_tuple包含的多个元素传给 nums 参数, nums 再将 my_tuple的多个元素收集成元组 。
>>> foo('fkit',*my_tuple)
name参数 fkit
name参数 (1, 2, 3)
#将元组传给参数nums,再将nums收集成元组,因此 有两个括号
>>> foo('fkit',my_tuple)
name参数 fkit
name参数 ((1, 2, 3),)
字典也支持逆向收集,字典将会以关键字参数的形式传入。
>>> def bar(book,price,desc):
... print(book,"这本书的价格是:",price)
... print('描述信息',desc)
...
>>> my_dict = {'price':89,'book':"疯狂python",'desc':"aaaa"}
>>> bar(**my_dict)
疯狂python 这本书的价格是: 89
描述信息 aaaa
函数的参数传递机制:
Python中函数的参数传递机制都是“值传递”。 所谓值传递,就是将实际参数值的副本(复制品)传入函数 , 而参数本身不会受到任何影响 。
注意:向函数swap()中传递字典dw:这种参数传递方式是不 折不扣的值传递方式(只是传的是引用/地址) , 系统一样复制了 dw 的 副本传入swap ()函数 。 但由于 dw 只是 一个引用 变量 ,因此系统复制的 是 dw 变量 ,并未 复 制字 典 本身。当程序在 swap ()函 数 中操作 dw 参数 时 ,由于 dw 只是 一 个引用变量,放实际操作的还是字典对象。此时,不管是操作主程序中的 dw 变量,还是操作 swap() 函数里的 dw 参数,其实操作的都是它们共同引用的字典对象,它 们 用的是同 一 个字典对象。因 此, 当在 swap() 函数中交换 dw 参数所 引 用字典对象的 a 、 b 两个元素的值后,可以看到在主程序中 dw 变量所引用字典对象的 a 、 b两个元素的值也被交换了。
通过上面介绍可以得出如下两个结论:
不管什么 类型的参数,在 Python 函 数中对参数直接使用“ =” 符号赋值是没用 的 ,直接使用“=”符号赋值并不能改变参数。
如果需要让函数修改某些数据 ,则可以通过把这些数据包装成列表、字典等可变对象,然后把列表、宇典等可变对象作为参数传入函数,在函数 中 通过列 表 、 字典的方法修改它们,这样才能改变这些数据。
变量作用域:
局部变量。在函数中定义的变量,包括参数,都被称为局部变量。
全局变量。在函数外面、全局范围内定义的变量 , 被称为全局变量。
每个函数在执行时,系统都会为该函数分配一块“ |临时内存空间”,所有的局部变量都被保存在这块临时内存空间内。当函数执行完成后,这块内存空间就被释放了,这些局部变量也就失效了,因此离开函数之后就不能再访问局部变量了。从这个角度来看 , 不管是局部范围还是全局范围,这些变革和它们的值就像一个“看不见”的字典 , 其中变量名就是字典的 key ,变量值就是字典的 value 。
globals() : 该函数返回全局范围内所有变量组成的“变量字典” 。
locals():该函数返回当前局部范围内所有变量组成的“变量字 典” 。
vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如 果不传入object参数, vars()和loacals()的作用完全相同。
globals()和 locals()看似完全不同,但它们实际上也是有联系的, 关于这 两个函数的区别和联系大致有以下两点 。
locals()总是获取当前局部范围内所有变量组成的“变量字典”,因此,如果在全局范围内(在函数之 外 )调用 l ocals()函数,同样会获取全局范围内所有变量组成的“变量宇典;而globals()无论在哪里执行, 总是获取全局范围内所有变量 组成的“变量字典。
一般来说,使用 locals()和 globals()获取的“变量字典”只应该被访问,不应该被修改 。但实际上,不管是使用 globals()还是使用 locals() 获取的全局范围内的“变量字典’ , 都可以被修改,而这种修改会真正改变全局变量本身:但通过 locals()获取的局部范围内的“变量字典”,即使对它修改也不会影响局部变量。
全局变量默认可以在所有函数内被访问 , 但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽 (hide)全局变量的情形。
>>> name = "Charlie"
>>> def test():
... print(name)
... name ="孙悟空"
...
>>> test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in test
UnboundLocalError: local variable 'name' referenced before assignment
Python 语法规定 : 在函数内部对不存在的变量赋值 时, 默认就是重新定义新的局部变量。因此这行代码相当于重新定义了 name 局部变量,这样 name 全局变 量就被遮蔽了,所以会报错。通过两种方式来修改上:
(1)访问被遮蔽的全局变量
如果程序希望在print(name)代码依然能访问 name 全局变量,且在print(name)代码之后可重新定义 name局部变量-----也就是在函数中可以访问被遮蔽的全局变量, 此时可通过 globals () 函数来实现,如下:
>>> def test():
... print(globals()['name'])
... name ="孙悟空"
...
>>> test()
Charlie
>>> print(name)
Charlie
>>>
(2)在函数中声明全局变量
为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用 global 语句来声明全局变量。增加了“ global name”声明之后,程序会把 name 变量当成全局变量,这意味着 test()函数后面对 name 赋值的语句只是对全局变量赋值,而不是重新定义局部变量。如下:
>>> def test():
... global name
... print(name)
... name = "孙悟空"
...
>>> test()
Charlie
>>> print(name)
孙悟空
局部函数:
函数都是在全局范围内定义的,它们都是全局函数,Python 还支持在函数体内定义函数 , 这种被放在函数体内定义的函数称为局部函数。
局部函数内的变量 也会遮蔽它所在函数内的局部变量,如下:
>>> def foo():
... name = 'Charlie'
... def bar():
... print(name)
... name = '孙悟空'
... bar()
>>> foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
该错误是由局部变量遮蔽局部变量导致的,为了声明 bar()函数中的“ name=’孙悟空”’ 赋值语句不是定义新的局部变量,只是访问它所在
foo ()函数内的 name 局部变 量 , Python 提供了 nonlocal 关键字,通过 nonlocal 语句即可声明访问赋值语句只是访问该函数所在函数内的局部变量。
nonlocal 和前面介绍的 global 功能大致相似,区别只是 global 用于声明访问全局变量,而 nonlocal 用于声明访问当前函数所在函数内的局部变量。
局部函数与 lambda 表达式:
如果说函数是命名的、方便复用的代码块,那么 lambda表达式则 是功能更灵活 的代码块,局部函数的函数名没有太大的意义,那么就考虑使用 lambda 表达式来简化局部函数的写法。
>>> c = lambda n: n*n
>>> c(3)
9
Python 要求 lambda 表达式只能是单行表达式。
lambda 表达式的语法格式如下:
lambda [parameter_list ] : 表达式
几个要点:lambda 表达式必须使用 lambda 关键字定义 。在 lambda 关键字之后、冒号左边的是参数列表,可以没有参数,也可以有多个参数 。 如果有多个参数, 则 需要用逗号隔开,冒号右边是该 lambda 表达式的返回值 。
lambda 表达式的本质就是匿名的、单行函数体的函数。可以写成函数形式,比如 lambda x,y : x+y 可以携程def add(x, y): return x+y
总体来说,函数比 lambda 表达式的适应性更强, lambda 表达式只能创建简单的函数对象,但 lambda 表达式依然有如下两个用途 。(1)对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁。(2)对于不需要多次复用的函数 , 使用 lambda 表达式可以在用完之后立即释放,提高了性能 。