Python学习笔记
函数(2)
本章将以Python学习笔记——函数(1) 为基础,继续深入的学习python语言中函数的使用。
普通参数收集
- 在形参前面添加一个型号(*),这样就意味着该参数可接受多个参数值,多个参数值被当成元组传入。
- 参数收集的本质就是一个元祖:Python会将传给带*参数的多个值收集成一个元组
- Python允许个数可变的形参可以出于形参列表的任意位置,但最多只能带一个支持“普通”参数收集的形参。
- 如果支持“普通”参数收集的形参位于前面,后面的参数需要使用关键字参数传值
# books参数支持收集,它可以接受多个参数值
def test(num, *books):
print("num:%d" % num)
print("books:" , books )
test(5, "lancibe", "crazybob", "lan", "ll")
def info(*names ,msg):
for name in names:
print("%s %s" % (name,msg))
info("aa", "bb", "cc", "dd", msg = "欢迎大家")
- 上面程序解释了前面的内容,这样的输出结果是:
num:5
books: ('lancibe', 'crazybob', 'lan', 'll')
aa 欢迎大家
bb 欢迎大家
cc 欢迎大家
dd 欢迎大家
关键字参数收集
- 在参数前面添加两个星号*,该参数支持关键字参数收集,收集的参数被当成dict处理
- 一个函数可以同时支持普通参数收集和关键字参数收集。
def test(num, *books, **scores):
print("num:%d" % num)
print("books:" , books )
print("scores:", scores)
test(20, "lancibe", "lan", "asd", 语文 = 89, 数学 = 92)
def info(*names ,msg, **scores):
for name in names:
print("%s %s" % (name,msg))
print(scores)
# 程序知道msg参数将是传给msg的,因此scores不会收集他
# dict的参数收集,只收集不能明确传入的关键字参数
info("aa", "bb", "cc", msg = "welcome", 语文 = 89, 数学 = 92)
- 上面程序的运行结果是:
num:20
books: ('lancibe', 'lan', 'asd')
scores: {'语文': 89, '数学': 92}
aa welcome
bb welcome
cc welcome
{'语文': 89, '数学': 92}
逆向参数收集
- 在列表、元组前添加*,在字典之前添加**
- 即使是支持收集的参数,如果程序需要将一个元组传给该参数,同样需要使用逆向收集(否则整个元组被当成一个值)。
def test(a,b):
print(a)
print(b)
vals = (20,40)
# 调用函数时,元组不能自动解包
# 默认情况下,元组是一个整体
# test(vals)
test(*vals)
# 对于列表使用方法相同
msgs = ["aa", "asd"]
test(*msgs)
- dict的逆向参数收集:dict前添加两个**,这样字典就可逆向参数收集。字典将会以关键字参数的形式传入。
def test(a,b):
print(a)
print(b)
vals = {"a" : 89 , "b" : 93}
# 可以使用字典的逆向收集,以关键字参数的形式为参数传入参数值
test(**vals)
# 如果使用了一个*,会解包成key
test(*vals)
- 上面程序的执行结果是:
89
93
a
b
变量的作用域
- 根据定义变量的位置,变量的作用域有两种(这是在我们没有学到面向对象之前的情况):
- 局部变量:在函数中定义的变量、以及函数的参数都被称为局部变量。
- 全局变量:在函数外面、全局范围内定义的变量,被称为全局变量。
# 全局变量
a = 35
def fun1():
# 局部变量
b = "lan"
print(b)
print(a)
def fun2():
c = "lancibe"
print(c)
print(b) # 这种访问方式是错误的
print(a)
- 不管是全局范围,还是局部范围,这些变量和他们的值就好像是一个“看不见”的字典
- 其中变量名就是字典的key
- 变量值就是字典的value
- 获取变量字典:
globals()
:该函数返回全局范围内所有变量组成的“变量字典”locals()
:该函数返回当前局部范围内的所有变量组成的“变量字典”vars(object)
:获取指定对象的范围内所有变量组成的“变量字典",如果不传入object参数,vars()
和locals()
的作用完全相同。
# 全局变量
a = 35
def fun1():
# 局部变量
b = "lan"
print(locals())
def fun2():
c = "lancibe"
print(locals())
print(globals())
fun1()
fun2()
- 对于上面的程序,在打印全局变量是我们会看到很多奇怪的没见过的东西,一般都是python自带的变量。出去他们,我们可以看到:
{...'a': 35...}
{'b': 'lan'}
{'c': 'lancibe'}
- 这和我们预期的一致。
- 如果在函数中定义了和全局变量同名的变量,此时就会出现局部变量遮蔽全局变量的情形。
name = "xun"
def fun():
print(name)
name = "lancibe"
print(name)
fun()
- 注意上面的程序是错误的,原因是在fun函数里面,先打印了name,又定义了name,程序会报错
UnboundLocalError: local variable 'name' referenced before assignment
,而不是我们想象的,先打印“xun”,在打印“lancibe”。 - 解决方案有两种,第一:使用
globals()
函数,来访问全局变量中的value,即print(globals()["name"])
。第二:使用global
声明。即在打印前,先有这一行语句:global name
,表示在这个函数中,name是个全局变量,所有对于name的修改、调用等全部是在全局范围内进行的。
局部函数
- 被放在函数体内定义的函数被称为局部函数,在默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭函数内使用。
def fun1():
print("fun1函数")
# 这个就是在其它函数内部定义的函数
def fun2():
for i in range(5):
print("fun2函数")
fun2()
fun1()
- 如果把调用fun1函数的位置换成fun2函数,则会报错:fun2函数未定义,这里和局部变量的意义类似。
- 但是我们可以让封闭函数返回拒不函数,一边程序在其他作用域中使用局部函数
- 如果封闭函数没有将局部函数返回出来,那么局部函数将只能在封闭函数内部调用。
def fun1():
print("fun1函数")
# 这个就是在其它函数内部定义的函数
def fun2():
for i in range(5):
print("fun2函数")
# 注意一定不要加括号,不加括号表示返回函数本身(其实函数也相当于一个值,是function类型的值,如果加上括号表示调用函数
return fun2
# 由于fun1函数的返回值是fun2函数,因此此处就是用r变量保存fun2函数
r = fun1()
# 调用fun2
r()
- 这里就是fun2给了r,
r()
本质是在调用fun2()
,但是结果是这样的:
fun1函数
fun2函数
fun2函数
fun2函数
fun2函数
fun2函数
- 出现了一个
fun1函数
的原因是在将fun2返回给r时,也同时调用了fun1函数,所以执行了一次fun1函数。 - 有一种比较诡异的写法:
fun1()()
这里的作用、答案和上面相同。
局部函数的遮蔽
- 局部函数内的变量也会遮蔽他所在的封闭函数的局部变量
- 局部函数为了避免遮蔽所在封闭函数的局部变量,可以使用
nonlocal
进行声明,对于这部分的,可以理解为作用域小的函数内的变量,会遮蔽同名的作用域大的函数的变量。
def test():
name = "lan"
def info():
print("info函数")
# 声明后面的name变量不是声明新的局部变量,而是引用所在封闭函数的局部变量。
nonlocal name
print("name:",name)
name = "lancibe"
print("name:",name)
info()
test()
nonlocal
和前面介绍的global
功能大致相似:都用于避免变量被遮蔽- 区别只是global用于声明访问全局变量,而nonlocal用于声明访问局部函数所在的封闭函数之内的局部变量。