python之函数参数浅谈

一 形参与实参的介绍
函数的参数分为形式参数和实际参数,简称形参和实参:
形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。
实参即在调用函数时,括号内传入的值,值可以是常量,变量,表达式或三者的组合:
1.实参是常量

res=my_min(1,2)

2.实参是变量

a=1
b=2
res=my_min(a,b)

3.实参是表达式

res=my_min(10*2,10*my_min(3,4))

2.实参可以是常量,变量,表达式的任意组合

a=2
my_min(1,2,10*my_min(3,4))

在调用参数时,实参(值)会赋值给形参(变量名)。在python中,变量名与值只是单纯的绑定关系,而对于函数来说,这种绑定关系只在函数调用时生效,在调用结束后解除

二形参与实参的具体使用

2.1位置参数
位置即顺序,位置参数值的是按顺序定于的参数,需要从两个角度去看:
1.在定义函数时,按照从左到右的顺序依次定义形参,称之为位置形参,凡是按照这种形式定义的形参都必须被传值

def register(name,age,sex):#定义位置形参:name,age,sex,三者都必须被传值
    print('Name: %s Age: %s Sex: %s' %(name,age,sex))
def register(name,age,sex):
    print('name:{} age:{} sex:{}'.format(name,age,sex))
def register(name,age,sex):
    print('age:{1} sex:{2} name:{0}'.format(name,age,sex))
register('zhaowei',19,'male')

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

def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值
    print('Name:%s Age:%s Sex:%s' %(name,age,sex))
register() #TypeError:缺少3个位置参数

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

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

需要注意在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对同一个参数重复赋值
2.3默认参数
在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,需要将值经常改变的参数定义为位置参数,而将值改变较少的参数定义为默认 参数。例如编写一个注册学生信息的函数,如果大多数学生的性别都是男,那完全可以将形参sex定义为默认参数

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

定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度

>>> register('tom',17) #大多数情况,无需为sex传值,默认为male
Name:tom Age:17 Sex:male
>>> register('Lili',18,'female') #少数情况,可以为sex传值female
Name:Lili Age:18 Sex:female

需要注意:
1.默认参数必须在位置参数之后
2.默认参数的值仅在函数定义阶段被赋值一次

>>> x=1
>>> def foo(arg=x):
...     print(arg)
... 
>>> x=5 #定义阶段arg已被赋值为1,此处的修改与默认参数arg无任何关系
>>> foo()
1

1.默认参数的值通常应设为不可变类型

def foo(n,arg=[]):
    arg.append(n)
    return arg

print(foo(2))
print(foo(3))
print(foo(4))
print(foo(5))
[2]
[2, 3]
[2, 3, 4]
[2, 3, 4, 5]

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

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

2.4可变长度的参数(与的用法)
参数的长度可变指的是在调用函数时,实参的个数可以不固定,而在调用函数时,实参的定义无非就是按位置或者关键字两种形式,这就要求形参提供两种解决方案来分别处理两种形式的可变长度的参数
2.4.1可变长度的位置参数
如果在最后一个形参名前加
号,那么在调用函数时,溢出的位置实参都会被接收,以元组的形式保存下来赋值给该形参

def foo(x,y,z=1,*args):
    print(x)
    print(y)
    print(z)
    print(args)

print(foo(1,2,3,4,5,6,7))
1
2
3
(4, 5, 6, 7)

如果我们事先生成一个列表,仍然是可以传给*args的

def foo(x,y,z,*args):
    print(x)
    print(y)
    print(z)
    print(args)
l=[4,5,6,7,8]
print(foo(1,2,*l))
1
2
4
(5, 6, 7, 8)

注意如果在传入l时没有加*,那l就是一个普通的位置参数了

def foo(x,y,z,*args):
    print(x)
    print(y)
    print(z)
    print(args)
l=[4,5,6,7,8]
print(foo(1,2,l))
1
2
[4, 5, 6, 7, 8]
()

如果形参为常规的参数(位置或默认),实参仍可以是*的形式

def foo(x,y,z,*args):
    print(x)
    print(y)
    print(z)
    print(args)
1
2
3
(4, 5, 6, 7)

如果我们想要求多个值的和,*args就派上用场了

def add(*args):
    res=0
    for i in args:
        res+=i
    return res
re=add(1,2,3,4,5)
print(re)

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

def foo(x,**kwargs):#在最后一个参数kwargs前加**
    print(x)
    print(kwargs)
aa=foo(1,a=2,y=3,z=4)
print(aa)
1
{'a': 2, 'y': 3, 'z': 4}
None

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

def foo(x,**kwargs):#在最后一个参数kwargs前加**
    print(x)
    print(kwargs)
dict1={'a':1,'b':2,'c':3}
print(foo(9,**dict1))
9
{'a': 1, 'b': 2, 'c': 3}
None

注意:如果传入的dict1时没有加**,那dict1就只是一个普通的位置参数了

def foo(x,**kwargs):#在最后一个参数kwargs前加**
    print(x)
    print(kwargs)
dict1={'a':1,'b':2,'c':3}
print(foo(9,dict1))
Traceback (most recent call last):
  File "E:/pycharm/项目/day06.py", line 76, in <module>
    print(foo(9,dict1))
TypeError: foo() takes 1 positional argument but 2 were given

如果形参为常规参数(位置或默认),实参可以是**形式

def foo(x,y,z=3):
    print(x)
    print(y)
    print(z)

print(foo(**{'x':1,'y':2,'z':3}))
1
2
3
None

如果我们要编写一个用户认证函数,起初可能只基于用户名密码的验证就可以了,可以使用**kwargs为日志的扩展提供良好的环境,同时保持了函数的简洁性

def foo(user,pwd,**kwargs):
    ...

2.5命名关键字参数
在定义了**kwargs参数后,函数调用者就可以传入任意的关键字参数key=value,如果函数体代码的执行需要依赖某个key,必须在函数内进行判断

def foo(name,age,**kwargs):
    if 'sex' in kwargs:
        ...
    if 'height'in kwargs:
        ...

想要限定函数的调用者必须以key=value的形式传值,python提供了专门的语法:需要在定义形参时,用*作为一个分隔符号,*号之后的形参称为命名关键字参数。对于这类参数,在函数调用时,必须按照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):
    print('Name:%s,Age:%s,Sex:%s,Height:%s' %(name,age,sex,height))
res=register('lili',38,height=70)
print(res)
Name:lili,Age:38,Sex:male,Height:70
None

需要强调的是:sex不是默认参数,height也不是位置参数,因为二者均在号后,所以都是命名关键字参数,形参sex=‘male’属于命名关键字参数的默认值,因而即便是放在形参height之前也不会有问题,另外,如果形参中已经有一个args了,命名关键字参数就不再需要一个单独的作为分隔符了。

def register(name,age,*args,sex='male',height):
    print('Name:%s,Age:%s,Args:%s,Sex:%s,Height:%s' %(name,age,args,sex,height))

register('lili',20,3,3,4,4,4,44,height='30')
Name:lili,Age:20,Args:(3, 3, 4, 4, 4, 44),Sex:male,Height:30

2.6组合使用
综上所述所有参数可任意组合使用,但定义顺序一定是:位置参数,默认参数,args,命名关键字参数,**kwargs
关键字参数
args与关键字参数kwargs通常是在一起使用的,如果一个函数的形参为*args与kwargs,那么代表该函数可以接收任何形式,任意长度的参数

def foo(*args,**kwargs):
    ...

在该函数内部还可以把接收的参数传给另外一个函数

def func(x,y,z):
    print(x,y,z)
def wrapper(*args,**kwargs):
    func(*args,**kwargs)

res=wrapper(1,y=2,z=3)
print(res)
1 2 3
None

按照上述写法,在为函数wrapper传参时,其实是遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:
1.位置实参1被接受,以元组的形式保存下来,赋值给args,即args=(1),关键字实参z=3,y=2被**接收,以字典的形式保存下来,赋值给kwargs即kwargs={‘y’:2,‘z’:3}
2.执行func(args,kwargs),即func((1),{‘y’:2,‘z’:3}),等同与func(1,y=2,z=3)
提示:*args,**kwargs中的args和kwargs被替换成其他名字并无语法错误,但是使用args,kwargs是约定俗成的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值