Python位置参数
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致。
实参和形参数量必须一致
在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),否则python解释器会抛出 TypeError 异常,并提示缺少必要的位置参数。
例如:
def girth(width , height):
return 2 * (width + height)
#调用函数时,必须传递 2 个参数,否则会引发错误
print(girth(3))
运行结果为:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\1.py", line 4, in <module>
print(girth(3))
TypeError: girth() missing 1 required positional argument: 'height'
可以看到,抛出的异常类型为 TypeError,具体是指 girth() 函数缺少一个必要的 height 参数。
同样,多传参数也会抛出异常:
def girth(width , height):
return 2 * (width + height)
#调用函数时,必须传递 2 个参数,否则会引发错误
print(girth(3,2,4))
运行结果为:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\1.py", line 4, in <module>
print(girth(3,2,4))
TypeError: girth() takes 2 positional arguments but 3 were given
通过 TypeErroe 异常信息可以知道,girth() 函数本只需要 2 个参数,但是却传入了 3 个参数。
实参和形参位置必须一致
在调用函数时,传入实际参数的位置必须和形式参数位置一一对应,否则会产生以下 2 种结果:
抛出 TypeError 异常
当实际参数类型和形式参数类型不一致,并且在函数种,这两种类型之间不能正常转换,此时就会抛出 TypeError 异常。
例如:
def area(height,width):
return height*width/2
print(area("C语言中文网",3))
输出结果为:
Traceback (most recent call last):
File "C:\Users\mengma\Desktop\1.py", line 3, in <module>
print(area("C语言中文网",3))
File "C:\Users\mengma\Desktop\1.py", line 2, in area
return height*width/2
TypeError: unsupported operand type(s) for /: 'str' and 'int'
以上显示的异常信息,就是因为字符串类型和整形数值做除法运算。
产生的结果和预期不符
调用函数时,如果指定的实际参数和形式参数的位置不一致,但它们的数据类型相同,那么程序将不会抛出异常,只不过导致运行结果和预期不符。
例如,设计一个求梯形面积的函数,并利用此函数求上底为 4cm,下底为 3cm,高为 5cm 的梯形的面积。但如果交互高和下低参数的传入位置,计算结果将导致错误:
def area(upper_base,lower_bottom,height):
return (upper_base+lower_bottom)*height/2
print("正确结果为:",area(4,3,5))
print("错误结果为:",area(4,5,3))
运行结果为:
正确结果为: 17.5
错误结果为: 13.5
因此,在调用函数时,一定要确定好位置,否则很有可能产生类似示例中的这类错误,还不容易发现。
Python函数关键字参数
目前为止,我们使用函数时所用的参数都是位置参数,即传入函数的实际参数必须与形式参数的数量和位置对应。而本节将介绍的关键字参数,则可以避免牢记参数位置的麻烦,令函数的调用和参数传递更加灵活方便。
关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。
因此,函数的参数名应该具有更好的语义,这样程序可以立刻明确传入函数的每个参数的含义。
例如,在下面的程序中就使用到了关键字参数的形式给函数传参:
def dis_str(str1,str2):
print("str1:",str1)
print("str2:",str2)
#位置参数调用
dis_str("http://c.biancheng.net/python/","http://c.biancheng.net/shell/")
#关键字参数调用
dis_str("http://c.biancheng.net/python/",str2="http://c.biancheng.net/shell/")
dis_str(str2="http://c.biancheng.net/python/",str1="http://c.biancheng.net/shell/")
程序执行结果为:
str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/python/
str2: http://c.biancheng.net/shell/
str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/
可以看到,在调用有参函数时,既可以单独根据位置参数来调用,也可以单独使用关键字参数(程序中第 8 行)来调用。在使用关键字参数调用时,可以任意调换参数传参的位置。
当然,还可以像第 7 行代码这样,使用位置参数和关键字参数混合传参的方式。但需要注意,混合传参时关键字参数必须位于所有的位置参数之后。也就是说,如下代码是错误的:
# 位置参数必须放在关键字参数之前,下面代码错误
dis_str(str1="http://c.biancheng.net/python/","http://c.biancheng.net/shell/")
Python 解释器会报如下错误:
SyntaxError: positional argument follows keyword argument
Python函数默认参数设置
我们知道,在调用函数时如果不指定某个参数,解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
Python 定义带有默认值参数的函数,其语法格式如下:
def 函数名(...,形参名,形参名=默认值):
代码块
注意,在使用此格式定义函数时,指定有默认值的形式参数必须在所有没默认值参数的最后,否则会产生语法错误。
下面程序演示了如何定义和调用有默认参数的函数:
#str1没有默认参数,str2有默认参数
def dis_str(str1,str2 = "http://c.biancheng.net/python/"):
print("str1:",str1)
print("str2:",str2)
dis_str("http://c.biancheng.net/shell/")
dis_str("http://c.biancheng.net/java/","http://c.biancheng.net/golang/")
运行结果为:
str1: http://c.biancheng.net/shell/
str2: http://c.biancheng.net/python/
str1: http://c.biancheng.net/java/
str2: http://c.biancheng.net/golang/
上面程序中,dis_str() 函数有 2 个参数,其中第 2 个设有默认参数。这意味着,在调用 dis_str() 函数时,我们可以仅传入 1 个参数,此时该参数会传给 str1 参数,而 str2 会使用默认的参数,如程序中第 6 行代码所示。
当然在调用 dis_str() 函数时,也可以给所有的参数传值(如第 7 行代码所示),这时即便 str2 有默认值,它也会优先使用传递给它的新值。
同时,结合关键字参数,以下 3 种调用 dis_str() 函数的方式也是可以的:
dis_str(str1 = "http://c.biancheng.net/shell/")
dis_str("http://c.biancheng.net/java/",str2 = "http://c.biancheng.net/golang/")
dis_str(str1 = "http://c.biancheng.net/java/",str2 = "http://c.biancheng.net/golang/")
再次强调,当定义一个有默认值参数的函数时,有默认值的参数必须位于所有没默认值参数的后面。因此,下面例子中定义的函数是不正确的:
#语法错误
def dis_str(str1="http://c.biancheng.net/python/",str2,str3):
pass
显然,str1 设有默认值,而 str2 和 str3 没有默认值,因此 str1 必须位于 str2 和 str3 之后。
有读者可能会问,对于自己自定义的函数,可以轻易知道哪个参数有默认值,但如果使用 Python 提供的内置函数,又或者其它第三方提供的函数,怎么知道哪些参数有默认值呢?
Pyhton 中,可以使用“函数名.__defaults__”查看函数的默认值参数的当前值,其返回值是一个元组。以本节中的 dis_str() 函数为例,在其基础上,执行如下代码:
print(dis_str.__defaults__)
程序执行结果为:
('http://c.biancheng.net/python/',)
函数可变参数(*args,**kwargs)
Python 在定义函数时也可以使用可变参数,即允许定义参数个数可变的函数。这样当调用该函数时,可以向其传入任意多个参数。
可变参数,又称不定长参数,即传入函数中的实际参数可以是任意多个。Python 定义可变参数,主要有以下 2 种形式。
1) 可变参数:形参前添加一个 '*'
此种形式的语法格式如下所示:
*args
args 表示创建一个名为 args 的空元组,该元组可接受任意多个外界传入的非关键字实参。
下面程序演示了如何定义一个参数可变的函数:
# 定义了支持参数收集的函数
def dis_str(home, *str) :
print(home)
# 输出str元组中的元素
print("str=",str)
for s in str :
print(s)
#可传入任何多个参数
dis_str("http://c.biancheng.net","http://c.biancheng.net/python/","http://c.biancheng.net/shell/")
程序执行结果为:
http://c.biancheng.net
str= ('http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/')
http://c.biancheng.net/python/
http://c.biancheng.net/shell/
上面程序中,dis_str() 函数的最后一个参数就是 str 元组,这样在调用该函数时,除了前面位置参数接收对应位置的实参外,其它非关键字参数都会由 str 元组接收(位置参数与可变参数)。
当然,可变参数并不一定必须为最后一个函数参数,例如修改 dis_str() 函数为:
# 定义了支持参数收集的函数
def dis_str(*str,home) :
print(home)
# 输出str元组中的元素
print("str=",str)
for s in str :
print(s)
dis_str("http://c.biancheng.net","http://c.biancheng.net/python/",home="http://c.biancheng.net/shell/")
可以看到,str 可变参数作为 dis_str() 函数的第一个参数。但需要注意的是,在调用该函数时,必须以关键字参数的形式给普通参数传值,否则 Python 解释器会把所有参数都优先传给可变参数,如果普通参数没有默认值,就会报错(可变参数与关键字参数)。
也就是说,下面代码调用上面的 dia_str() 函数,是不对的:
dis_str("http://c.biancheng.net","http://c.biancheng.net/python/","http://c.biancheng.net/shell/")
Python 解释器会提供如下报错信息:
TypeError: dis_str() missing 1 required keyword-only argument: 'home'
翻译过来就是我们没有给 home 参数传值。当然,如果 home 参数有默认参数,则此调用方式是可行的。
2) 可变参数:形参前添加两个'*'
这种形式的语法格式如下:
**kwargs
**kwargs 表示创建一个名为 kwargs 的空字典,该字典可以接收任意多个以关键字参数赋值的实际参数。
例如如下代码:
# 定义了支持参数收集的函数
def dis_str(home,*str,**course) :
print(home)
print(str)
print(course)
#调用函数
dis_str("C语言中文网",\
"http://c.biancheng.net",\
"http://c.biancheng.net/python/",\
shell教程="http://c.biancheng.net/shell/",\
go教程="http://c.biancheng.net/golang/",\
java教程="http://c.biancheng.net/java/")
程序输出结果为:
C语言中文网
('http://c.biancheng.net', 'http://c.biancheng.net/python/')
{'shell教程': 'http://c.biancheng.net/shell/', 'go教程': 'http://c.biancheng.net/golang/', 'java教程': 'http://c.biancheng.net/java/'}
上面程序在调用 dis_str() 函数时,第 1 个参数传递给 home 参数,第 2、3 个非关键字参数传递给 str 元组,最后 2 个关键字参数将由 course 字典接收。
注意,*args 可变参数的值默认是空元组,**kwargs 可变参数的值默认是空字典。因此,在调用具有可变参数的函数时,不一定非要给它们传值。以调用 dis_str(home, *str, **course) 为例,下面的调用方式也是正确的:
dis_str(home="http://c.biancheng.net/shell/")
程序执行结果为:
http://c.biancheng.net/shell/
()
{}
python逆向参数,实参值前添加1个'*'或两个'*'
前面章节中介绍了,Python 支持定义具有可变参数的函数,即该函数可以接收任意多个参数,其中非关键字参数会集中存储到元组参数(*args)中,而关键字参数则集中存储到字典参数(**kwargs)中,这个过程可称为参数收集。
不仅如此,Python 还支持逆向参数收集,即直接将列表、元组、字典作为函数参数,Python 解释器会将其进行拆分,把其中存储的元素按照次序分给函数中的各个形参。
函数实参调用方式:
在以逆向参数收集的方式向函数参数传值时,Pyhon 语法规定,当传入列表或元组实参时,其名称前要带一个 * 号,当传入字典实参时,其名称前要带有 2 个 * 号。
举个例子:
def dis_str(name,add) :
print("name:",name)
print("add",add)
data = ["Python教程","http://c.biancheng.net/python/"]
#使用逆向参数收集方式传值
dis_str(*data)
程序执行结果为:
name: Python教程
add http://c.biancheng.net/python/
再举个例子:
def dis_str(name,add) :
print("name:",name)
print("add:",add)
data = {'name':"Python教程",'add':"http://c.biancheng.net/python/"}
#使用逆向参数收集方式传值
dis_str(**data)
程序执行结果为:
name: Python教程
add: http://c.biancheng.net/python/
此外,以逆向参数收集的方式,还可以给拥有可变参数的函数传参,例如:
def dis_str(name,*add) :
print("name:",name)
print("add:",add)
data = ["http://c.biancheng.net/python/",\
"http://c.biancheng.net/shell/",\
"http://c.biancheng.net/golang/"]
#使用逆向参数收集方式传值
dis_str("Python教程",*data)
程序执行结果为:
name: Python教程
add: ('http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/', 'http://c.biancheng.net/golang/')
上面程序中,也同样可以用逆向参数收集的方式给 name 参数传值,只需要将 "python教程" 放到 data 列表中第一个位置即可。也就是说,上面程序中,以下面代码调用 dis_str() 函数的方式也是可行的:
data = ["Python教程",\
"http://c.biancheng.net/python/",\
"http://c.biancheng.net/shell/",\
"http://c.biancheng.net/golang/"]
#使用逆向参数收集方式传值
dis_str(*data)
执行此程序,会发现其输出结果和上面一致。
再次强调,如果使用逆向参数收集的方式,必须注意 * 号的添加。以逆向收集列表为例,如果传参时其列表名前不带 * 号,则 Python 解释器会将整个列表作为参数传递给一个参数。例如:
def dis_str(name,*add) :
print("name:",name)
print("add:",add)
data = ["Python教程",\
"http://c.biancheng.net/python/",\
"http://c.biancheng.net/shell/",\
"http://c.biancheng.net/golang/"]
dis_str(data)
程序执行结果为:
name: ['Python教程', 'http://c.biancheng.net/python/', 'http://c.biancheng.net/shell/', 'http://c.biancheng.net/golang/']
add: ()