python基础(十二):函数的参数

一、形参和实参是什么?

函数的参数分为形式参数和实际参数,简称形参和实参:
形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。

def func(x,y): #x和y为形参
	print(x,y)

实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合。

func(1,2) #1和2为实参

形参和实参的关系:

在调用阶段,实参(变量值)会赋值给形参(变量名)

二、形参与实参的具体使用

1、两种形参的区别

(1)位置形参

在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值

def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
    print('Name:{} Age:{} Sex:{}'.format(name,age,sex))
(2)默认形参

默认形参调用时,可以不传值,那么就是默认值,传值就覆盖默认值。

>>> def register(name,age,sex='male'): #默认sex的值为male
...     print('Name:{} Age:{} Sex:{}'.format(name,age,sex))
...

需要注意:

  • 默认参数必须在位置参数之后
  • 默认参数的值仅在函数定义阶段被赋值一次
  • 默认参数的值通常应设为不可变类型
def foo(n,arg=[]):    
     arg.append(n)    
     return arg    
foo(1)    
[1] 
foo(2)    
[1, 2] 
foo(3)    
[1, 2, 3]

每次调用是在上一次的基础上向同一列表增加值,修改如下

def foo(n,arg=None):    
     if arg is None:    
         arg=[]    
     arg.append(n)    
     return arg    
foo(1)    
[1] 
foo(2)    
[2] 
foo(3)    
[3]

2、两种实参的区别

(1)位置实参

在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应

def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
    print('Name:{} Age:{} Sex:{}'.format(name,age,sex))
register('吴晋丞',18,'男') #定义位置实参:'吴晋丞',18,'男',按照顺序和形参相对应
(2)关键字实参

在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值

>>> register(sex='male',name='lili',age=18)
Name:lili Age:18 Sex:male

需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值

>>> register('lili',sex='male',age=18) #正确使用

#错误示例:
>>> register(name='lili',18,sex='male') #SyntaxError:关键字参数name=‘lili’在位置参数18之前
>>> register('lili',sex='male',age=18,name='jack') #TypeError:形参name被重复赋值

3、可变长参数

参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,实参的定义无非是按位置或者按关键字两种形式,这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数

(1)可变长位置参数在形参和实参中的应用
  • 形参中的应用

如果在最后一个形参名前加*号,那么在调用函数时,溢出的位置实参,都会被接收,以元组的形式保存下来赋值给该形参

>>> def foo(x,y,z=1,*args): #在最后一个形参名args前加*号
...     print(x)
...     print(y)
...     print(z)
...     print(args)
... 
>>> foo(1,2,3,4,5,6,7)  #实参1、2、3按位置为形参x、y、z赋值,多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args,即args=(4, 5, 6,7)

1
2
3
(4, 5, 6, 7)
  • 实参中的应用

如果我们事先生成了一个列表(也可以是元组、字符串),仍然是可以传值给*args的

>>> def foo(x,y,*args):
...     print(x)
...     print(y)
...     print(args)
... 
>>> L=[3,4,5]
>>> foo(1,2,*L) # *L就相当于位置参数3,4,5。foo(1,2,*L)就等同于foo(1,2,3,4,5)
1
2
(3, 4, 5)
(2)可变长关键字参数在形参和实参中的应用
  • 形参中的应用

如果在最后一个形参名前加**号,那么在调用函数时,溢出的关键字参数,都会被接收,以字典的形式保存下来赋值给该形参

>>> def foo(x,**kwargs): #在最后一个参数kwargs前加**
...     print(x)        
...     print(kwargs)   
... 
>>> foo(y=2,x=1,z=3) #溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs
1
{'z': 3, 'y': 2}
  • 实参中的应用

如果我们事先生成了一个字典,仍然是可以传值给**kwargs的

>>> def foo(x,y,**kwargs):
...     print(x)
...     print(y)
...     print(kwargs)
... 
>>> dic={'a':1,'b':2} 
>>> foo(1,2,**dic) #**dic就相当于关键字参数a=1,b=2。foo(1,2,**dic)等同foo(1,2,a=1,b=2)
1
2
{'a': 1, 'b': 2}

4、命名关键字参数(了解)

想要限定函数的调用者必须以key=value的形式传值,Python3提供了专门的语法:需要在定义形参时,用*号作为一个分隔符号,*号之后的形参称为命名关键字参数。对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值。

>>> def register(name,age,*,sex,height): #sex,height为命名关键字参数
...     pass
... 
>>> register('lili',18,sex='male',height='1.8m') #正确使用

#错误示例:
>>> register('lili',18,'male','1.8m') # TypeError:未使用关键字的形式为sex和height传值
>>> register('lili',18,height='1.8m') # TypeError没有为命名关键字参数height传值。

命名关键字参数也可以有默认值(注意:这个不叫默认形参),从而简化调用

>>> def register(name,age,*,sex='male',height):
	pass

如果形参中已经有一个*args了,命名关键字参数就不再需要一个单独的*作为分隔符号了,而是\*args作为分隔符号,后面是命名关键字参数

>>> def register(name,age,*args,sex='male',height):#sex,height仍然为命名关键字参数

有人可能还会问如果是**kwargs呢?是不是以**kwargs为分隔符?

答:这样会报错,这样也没有什么用,因为**kwargs就是用来接收多余的关键字实参的,如果多余的不是关键字实参,会报错。你再用命名关键字参数,限制必须关键字实参传值,没有意义。

在这里插入图片描述

5、组合使用

综上所述所有参数可任意组合使用,但形参定义顺序必须是:位置参数、默认参数、\*args、命名关键字参数、**kwargs.

实参的定义顺序可以是:位置实参、*、关键字参数、**

func(111,*(333,444),y=222,**{'a':555,'b':666})
#只要*和**打散之后遵循位置实参在关键字参数左边即可。*相当于一堆位置实参,**相当于一堆关键字参数

可变参数*args与关键字参数**kwargs通常是组合在一起使用的,如果一个函数的形参为*args与**kwargs,那么代表该函数可以接收任何形式、任意长度的参数

>>> def wrapper(*args,**kwargs):
...     pass

在该函数内部还可以把接收到的参数传给另外一个函数(这在4.6小节装饰器的实现中大有用处)

>>> def func(x,y,z):
...     print(x,y,z)
... 
>>> def wrapper(*args,**kwargs): #(1,)元组 {'z':3,'y':2}字典
...     func(*args,**kwargs) # *(1,) **{'z':3,'y':2},实参使用*和**,打散元组、字典
...
>>> wrapper(1,z=3,y=2)
1 2 3
提示: *args、**kwargs中的args和kwargs被替换成其他名字并无语法错误,但使用args、kwargs是约定俗成的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章