在调用函数时,大多数情况下主调函数和被调函数之间有数据传递关系,这就是有参数的函数形式。函数参数的作用是传递数据给函数使用,函数利用接收的数据进行具体的操作处理。函数参数在定义函数时放在函数名称后面的一对圆括号中。
在使用函数时,经常会用到形式参数(简称形参)和实际参数(简称实参),二者都叫作参数。下面将先通过讲解形式参数与实际参数的作用来说明二者的区别,再通过一个实例对二者进行深入探讨。
形式参数和实际参数在作用上的区别如下:
● 形式参数:在定义函数时的参数。
● 实际参数:调用函数时的参数,也就是将函数的调用者提供给函数的参数。
根据实际参数的类型不同,可以分为将实际参数的值传递给形式参数和将实际参数的引用传递给形式参数两种情况。其中,当实际参数为不可变对象时,进行值传递;当实际参数为可变对象时,进行引用传递。
【例4.3】定义一个名称为demo()的函数,然后为demo()函数传递一个字符串类型的变量作为参数(代表值传递),并在函数调用前后分别输出该字符串变量,再为demo()函数传递一个列表类型的变量作为参数(代表引用传递),并在函数调用前后分别输出该列表。
# 定义函数 def demo(obj): print("原值:", obj) obj += obj # 调用函数 print("=========值传递========") mot = "唯有在被追赶的时候,你才能真正地奔跑。" print("函数调用前:", mot) demo(mot) # 采用不可变对象—字符串 print("函数调用后:", mot) print("=========引用传递========") list1 = ['绮梦', '冷伊一', '香凝', '黛兰'] print("函数调用前:", list1) demo(list1) # 采用可变对象—列表 print("函数调用后:", list1)
上面代码的执行结果如图4.2所示。
图4.2 参数传递结果
从上面的执行结果中可以看出,在进行值传递时,改变形式参数的值后实际参数的值不改变;在进行引用传递时,改变形式参数的值后实际参数的值也发生改变。
位置参数也称必备参数,必须按照正确的顺序传递到函数中,即调用时的数量和位置必须和定义时一致。
在调用函数时,指定的实际参数的数量必须与形式参数的数量一致,否则将抛出TypeError异常,提示缺少必要的位置参数。
在调用函数时,指定的实际参数的位置必须与形式参数的位置一致,否则将产生以下两种结果。
拋出该异常主要是因为实际参数的类型与形式参数的类型不一致,并且在函数中这两种类型还不能转换。
例如出现如图4.3所示的异常信息,主要是因为传递的整型数值不能与字符串进行连接操作。
图4.3 提示不支持的操作数类型
在调用函数时,如果指定的实际参数与形式参数的位置不一致,但是它们的数据类型一致,那么就不会抛出异常,而是产生结果与预期不符的问题。
关键字参数是指使用形式参数的名字来确定输入的参数值。通过该方式指定实际参数时,不再需要与形式参数的位置完全一致,只要将参数名写正确即可。这样可以避免用户需要牢记参数位置的麻烦,使得函数的调用和参数传递更加灵活方便。
调用函数时如果没有指定某个参数将拋出异常,可以为该参数设置默认值,即在定义函数时直接指定形式参数的默认值。这样,当没有传入参数时,可以直接使用定义函数时设置的默认值。定义带有默认值参数的函数的语法格式如下:
def functionname(...,[parameter1 = defaultvalue1]): [functionbody]
参数说明:
● functionname:函数名称,在调用函数时使用。
● parameterl=defaultvaluel:可选参数,用于指定向函数中传递的参数,并且为该参数设置默认值为defaultvaluel。
● functionbody:可选参数,用于指定函数体,即该函数被调用后要执行的功能代码。
在定义函数时,必须在所有参数的最后指定默认的形式参数,否则将产生语法错误。
【例4.4】定义一个根据身高、体重计算BMI指数的函数fun_bmi(),为其第一个参数指定默认值。
def fun_bmi(height, weight, person="路人"): """ 功能:根据身高和体重计算BMI指数 :param person:姓名 :param height:身高 :param weight:体重 :return: """ bmi = weight / (height * height) print(person + "的BMI指数为:" + str(bmi)) # 判断身材是否合理 if bmi < 18.5: print("您的体重过轻~@_@~\n") if bmi >= 18.5 and bmi < 24.9: print("正常范围,注意保持(-_-) \n") if bmi >= 24.9 and bmi < 29.9: print("您的体重过重 ~@_@\n")
然后调用该函数,不指定第一个参数,代码如下:
fun_bmi(1.73,60)
执行结果如图4.4所示。
图4.4 执行结果
使用可变对象作为函数参数的默认值时,多次调用可能会导致意料之外的情况。
【例4.5】编写一个名称为demo()的函数,并为其设置一个带默认值的参数,再调用该函数。
def demo(obj = []): # 定义函数并为obj指定默认值 print("obj的值:",obj) obj.append(1) demo()
执行结果如下:
obj的值: []
连续两次调用demo()函数,并且都不指定实际参数,执行结果如下:
obj的值: [] obj的值: [1]
这显然不是我们想要的结果。为了防止出现这种情况,最好使用None作为可变对象的默认值,并加上必要的检查代码。修改后的代码如下:
def demo(obj = None): # 定义函数并为obj指定默认值 if obj == None: obj = [] print("obj的值:",obj) obj.append(1)
执行结果如下:
obj的值: [] obj的值: []
定义函数时,为形式参数设置默认值要牢记一点:默认参数必须指向不可变对象。
在Python中,还可以定义可变参数。可变参数也称不定长参数,即传入函数中的实际参数可以是任意多个。
定义可变参数主要有两种形式:一种是*parameter,另一种是**parameter。
这种形式表示接收任意多个实际参数并将其放到一个元组中。
【例4.6】定义一个函数,让其可以接收任意多个实际参数。
def printcoffee(*coffername): # 定义输出我喜欢的咖啡名称的函数 print("\n我喜欢的咖啡有:") for item in coffername: print(item) # 输出咖啡名称
调用3次上面的函数,分别指定不同的实际参数,代码如下:
printcoffee('蓝山') printcoffee('蓝山', '卡布奇诺', '土耳其', '巴西', '哥伦比亚') printcoffee('蓝山', '卡布奇诺', '曼特宁', "摩卡")
执行结果如图4.5所示。
图4.5 执行结果
如果想要使用一个已经存在的列表作为函数的可变参数,可以在列表的名称前加“*”。
【例4.7】引用一个已经存在的列表。
param = ['蓝山', '卡布奇诺', '土耳其'] printcoffee(*param)
通过调用printcoffee()函数,执行结果如下:
我喜欢的咖啡有: 蓝山 卡布奇诺 土耳其
**parameter表示关键字参数。可变参数允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个元组。而关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个字典。
【例4.8】引用多个元素,经过程序的处理后,自动整理所有元素为一个字典元素,然后输出。
def printcoffee(*coffername): # 定义输出我喜欢的咖啡名称的函数 print("\n我喜欢的咖啡有:") for item in coffername: print(item) # 输出咖啡名称
调用3次上面的函数,分别指定不同的实际参数,代码如下:
printcoffee('蓝山') printcoffee('蓝山', '卡布奇诺', '土耳其', '巴西', '哥伦比亚') printcoffee('蓝山', '卡布奇诺', '曼特宁', "摩卡")
按照从左到右的顺序定义的参数。
● 位置形参:必选参数。
● 位置实参:按照位置给形参传值。
按照key=value的形式定义的实参。
无须按照位置为形参传值。
注意的问题:
● 关键字实参必须在位置实参右边。
● 对同一个形参不能重复传值。
形参在定义时就已经为其赋值。
可以传值也可以不传值,经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)。
注意的问题:
● 只在定义时赋值一次。
● 默认参数的定义应该在位置形参右边。
● 默认参数通常应该定义成不可变类型。
可变指的是实参值的个数不固定,而实参有按位置和按关键字两种形式定义,针对这两种形式的可变,形参对应有两种解决方案来完整地存放它们,分别是*args和**kwargs。
“*”后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递。