《每天五分钟冲击python基础之函数参数》(十八)

前言

对于上节课的 递归函数 ,很多同学在后台私信我,说递归函数这块是没有看明白是什么意思的,那么我在这里给你们举例说明一下递归函数的原理吧!
在这里插入图片描述
大致的递归的原理就是这样的,是有点复杂的,但是也不太难,如果还是没有看明白的同学,建议先跳过这个,因为这个在现在的阶段,了解一下就可以了,不必太深入这个。好的,那么就开始我们今天的课程吧!

什么是函数的参数

在定义python函数时可定义形参(形式参数的意思),这些形参的值要等到调用时才能确定下来,由函数的调用者负责为形参传入参数值,简单来说,就是谁调用函数,谁负责传入参数值。

关键字(keyword)参数

python函数的参数名不是无意义的,python允许在调用函数时通过名字来传入参数值,因此,python函数的参数名应该具有更好的语义----程序可以立刻明确传入函数的每个参数的含义。
按照形参位置传入的参数被称为位置参数。如果使用位置参数的方式来传入参数值,则必须严格按照定义函数时指定的顺序来传入参数值;如果根据参数名来传入参数值,则无须遵守定义形参的顺序,这种方式被称为关键字(keyword)参数,例:

def girth(width, height):
	print("width:", width)
	print("height:", height)
	return 2 * (width + height)

#传统调用函数的方式,根据位置传入参数值
print(girth(3.2, 4.8))
#根据关键字参数来传入参数值
print(girth(width = 3.2, height = 4.8))
#在使用关键字参数时可交换位置
print(girth(height = 4.8, width=3.2))
#部分使用关键字参数,部分使用位置参数
print(girth(3.5, height = 4.8))

输出结果:
width: 3.2
height: 4.8
16.0

上面程序定义了一个简单的girth()函数,该函数包含 width、height两个参数,该函数与前面定义的函数并没有任何区别。

接下来在调用函数时,既可使用传统的根据位置参数来调用(如:girth(3.2, 4.8)),也可根据关键字参数来调用(如:girth(width = 3.2, height = 4.8)),在使用关键字参数调用时可交换参数的位置(如:girth(height = 4.8, width=3.2)),还可混合使用位置参数和关键字参数(如:girth(3.5, height = 4.8))

需要说明的是,如果希望在调用函数时混合使用关键字参数和位置参数,则关键字参数必须位于位置参数之后,换句话说,在关键字参数之后的只能是关键字参数,例如如下代码是错误的:

print(girth(width = 3.5, 4.8))

输出结果:
SyntaxError:positional argument follows keyword argument

参数默认值

在某些情况下,程序需要在定义函数时为一个或多个形参指定默认值-----这样在调用函数时就可以省略为该形参传入参数值,而是直接使用该形参的默认值。
语法格式如下:

def 函数名 (形参名 = 默认值) :

从上面的语法格式可以看出,形参的默认值紧跟在形参之后,中间以英文 “=” 隔开。例:

def say_hi (name = "virus", message = "欢迎你学习python"):
	print(name, ",", "你好")
	print("消息是:", message)

say_hi()
say_hi("baba")
say_hi("baba","恭喜你通过python等级考试")

输出结果:
直接使用默认参数:
virus , 你好
消息是: 欢迎你学习python

传入参数覆盖默认参数:
baba , 你好
消息是: 欢迎你学习python
baba , 你好
消息是: 恭喜你通过python等级考试

从上面程序可以看出,如果只传入一个位置参数,由于该参数位于第一位,系统会将该参数值传给name参数。因此,我们不能按如下方式调用say_hi()函数,例:

def say_hi (name = "virus", message = "欢迎你学习python"):
	print(name, ",", "你好")
	print("消息是:", message)

say_hi("恭喜你通过python的等级考试")

输出结果:
恭喜你通过python的等级考试 , 你好
消息是: 欢迎你学习python

上面调用时传入的 “恭喜你通过python的等级考试 ” 字符串将传给name参数,而不是message参数。我们也不能按如下方式来调用say_hi()函数,例:

def say_hi (name = "virus", message = "欢迎你学习python"):
	print(name, ",", "你好")
	print("消息是:", message)

say_hi(name="hki", "恭喜你通过python等级考试")

输出结果:
SyntaxError: positional argument follows keyword argument

因为python规定:关键字参数必须位于位置参数的后面,因此提示错误:positional argument follows keyword argument

参数收集(个数可变的参数)

很多编程语言都允许定义个数可变的参数,这样可以在调用函数时传入任意多个参数。python当然也不例外,python允许在形参前面添加一个星号(*),这样就意味该参数可接收多个参数值,多个参数值被当成元组传入,例:

def test(a, *books):
	print(books)
	for b in books :
		print(b)
	print(a)

test(5, "abc", "bcs")

输出结果:
('abc', 'bcs')
abc
bcs
5

从上面的运行结果可以看出,当调用 test() 函数时,books参数可以传入多个字符作为参数值。从 test() 的函数体代码来看,参数收集的本质就是一个元组:python会将传给 books 参数的多个值收集成一个元组。

python允许个数可变的形参可以处于形参列表的任意位置(不要求是形参列表的最后一个参数),但python要求一个函数最多只能带一个支持 “普通” 参数收集的形参,例:

def test(*books, a):
	print(books)
	for b in books :
		print(b)
	print(a)

test("abc", "bcs", "yyds", a = 5)

输出结果:
('abc', 'bcs', 'yyds')
abc
bcs
yyds
5

正如从上面程序中所看到的,test() 函数的第一个参数就是个数可变的形参,由于该参数可接收个数不等的参数值,因此如果需要给后面的参数传入参数值,则必须使用关键字参数;否则,程序会把所传入的多个值都当成是传给books参数的。

python还可以收集关键字参数,此时python会将这种关键字参数收集成字典。为了让python能收集关键字参数,需要在参数前面添加两个星号。在这种情况下,一个函数可用时包含一个支持“普通”参数收集的参数和一个支持关键字参数收集的参数,例:

def test(*books, **message):
	print(books)
	print(message)

test("abc", "bcs", "yyds", a = 5, b = 85)

输出结果:
('abc', 'bcs', 'yyds')
{'a': 5, 'b': 85}

逆向参数收集

所谓逆向参数收集,指的是在程序已有列表、元组、字典等对象的前提下,把它们的元素“拆开”后传给函数的参数。

逆向参数收集需要在传入的列表、元组参数之前添加一个星号,在字典参数之前添加两个星号,例:

def test(name, message):
	print("用户是:", name)
	print("欢迎信息:", message)

my_list = ["virus", "欢迎来学习python"]

test(*my_list)

输出结果:
用户是: virus
欢迎信息: 欢迎来学习python
def test(name, message):
	print("用户是:", name)
	print("欢迎信息:", message)

my_list = {"name":123, "message":"欢迎来学习python"}

test(**my_list)

输出结果:
用户是: 123
欢迎信息: 欢迎来学习python

函数的参数传递机制

python的参数值是如何传入函数的呢?这是由python函数的参数传递机制来控制的。python中函数的参数传递机制都是 “值传递”。所谓的值传递,就是将实际的参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。例:

def chuandi (a, b):
	a, b = b, a
	print("这是 chuandi 里的a:", a)
	print("这是 chuandi 里的b:", b)

a = 10
b = 20
chuandi(a, b)
print("这是外面定义的a:", a)
print("这是外面定义的b:", b)

输出结果:
这是 chuandi 里的a: 20
这是 chuandi 里的b: 10
这是外面定义的a: 10
这是外面定义的b: 20

在这里插入图片描述

变量的作用域

在程序中定义一个变量时,这个变量时有作用范围的,变量的作用范围被称为它的作用域。根据定义变量的位置,变量分为两种。
局部变量:在函数中定义的变量,包括参数,都被称为局部变量
全局变量:在函数外面、全局范围内定义的变量,被称为全局变量。
每个函数在执行时,系统都会为该函数分配一块 “临时内存空间”,所有的局部变量都被保存在这块临时内存空间内。当函数执行完成后,这块内存空间就被释放了,这些局部变量也就失效了,因此离开函数之后就不能再访问局部变量了。
全局变量意味着它们可以在所有函数内被访问。

不管是在函数的局部范围内还是在全局范围内,都可能存在多个变量,每个变量“持有”该变量的值。从这个角度来看,不管是局部范围还是全局范围,这些变量和它们的值就像一个“看不见”的字典,其中变量名就是字典的key,变量值就是字典的value。

实际上,python提供了如下三个工具函数来获取指定范围内的“变量字典”
globals():该函数返回全局范围内所有变量组成的“变量字典”
locals():该函数返回当前局部范围内所有变量组成的“变量字典”
vars():获取在指定对象范围内所有变量组成的“变量字典”,如果不传入object参数,vars()和locals()的作用完全相同。

globals() 和 locals() 看似完全不同,但它们实际上也是有联系的,关于这两个函数的区别和联系大致有以下两点:

locals() 总是获取当前局部范围内所有变量组成的 “变量字典”,因此,如果在全局范围内(在函数之外)调用 locals() 函数,同样会获取全局范围内所有变量组成的 “变量字典”;而 globals() 无论在哪里执行,总是获取全局范围内所有变量组成的 “变量字典”。

一般来说,使用locals()和globals()获取的“变量字典”只应该被访问,不应该被修改。但实际上,不管是使用globals()还是使用locals()获取的全局范围内的“变量字典”,都可以被修改,而这种修改会真正改变全局变量本身;但通过 locals() 获取的局部范围内的 “变量字典”,即使对它修改也不会影响局部变量。
例:

def test():
	age = 20
	print(locals()['age']) #输出20
	locals()['age'] = 12  #函数内改变 age 的值
	print("函数内age的值", age) #依然输出20
	globals()['x'] = 19

test()
globals()['x'] = 89 #使用globals()全局函数改变 x 的值
print(x)

输出结果:
20
函数内age的值 20
89

从上面程序可以清楚地看出,locals()函数用于访问特定范围内的所有变量组成的“变量字典”,而globals()函数则用于访问全局范围内的全局变量组成的“变量字典”。

全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽全局变量的情形,例:

name = "Charlie"

def test():
	print(name)
	name = "Virus" #这里遮蔽了

test()
print(name)

输出结果:
UnboundLocalError: local variable 'name' referenced before assignment

python语法规定:在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量。因此这行代码相当于重新定义了name局部变量,这样name全局变量就被遮蔽了。

为了避免这个问题,可以通过两种方式来修改上面程序
1、访问被遮蔽的全局变量

name = "Charlie"

def test():
	print(globals()['name']) #访问被遮蔽的全局变量
	name = "Virus" #这里遮蔽了

test()
print(name)

输出结果:
Charlie
Charlie

2、在函数中声明全局变量

name = "Charlie"

def test():
	global name #声明全局变量
	print(name)
	name = "Virus" #这里遮蔽了

test()
print(name)

输出结果:
Charlie
Virus

课外监督

最近一部分同学在后台私聊我说,自己每天都准备要学习了,但是一去到电脑桌上就是提不起劲来,怎么办呢?针对这种情况的同学,我决定每天花一点时间对你们学习进行监督和辅导,如有需要的同学,扫描下面的图片,点击 “联系作者” 进行报名。

结语

这节课主要要掌握好参数的收集和逆向收集,这两个点是很重要的,后面开发的时候都会经常用到,而且也要明白函数参数传递的机制,好啦,今节课就先讲到这里,下节课讲函数的局部函数,敬请期待。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值