python基础 - 位置参数、默认参数、可变参数、关键字参数

python基础 - 位置参数、默认参数、可变参数、关键字参数

大家好,我是W

在看源码的时候经常看到各式各样的参数,有的参数有默认值,有的参数没有默认值,有的参数连参数的名都没有,只能看到*args**kwargs。这些东西到底有什么区别,怎么使用呢?接下来我们学习的顺序是位置参数、默认参数、可变参数、关键字参数

位置参数

位置参数顾名思义就是与位置有关的参数,在调用函数的时候需要根据传入参数的位置来一一对应参数。并且,位置参数是不可缺少的,即不传入完整的参数是会报错的。

下面是我们平常看到的函数最普通的形式,普通到我们甚至不知道他的参数还有个名叫位置参数:

def add(num1, num2):
return num1 + num2

if __name__ == '__main__':
	# add 函数的两个参数不可缺省
	# 且传入的参数值与他们的位置有关, 即main函数中add的num1的位置放的1,则在add函数的num1就是1
    sum = add(1, 2)

若调用add函数时传入参数缺省了,则会报TypeError: add() missing 1 required positional argument: 'num2',即却是一个要求的未知参数num2。

总结就是位置参数就是与函数参数位置有关的参数,且函数的位置参数不可缺省,必须传满。

默认参数

默认参数顾名思义就是与默认值有关的参数在位置参数中缺省会报错,为了增加函数的鲁棒性,也为了传参时在可以使用默认设置的情况下不用传那么多参数,参数可以设置默认值,这样即使缺省了该参数也不会报错了

常见的默认参数函数时这样的:

def minus(num1, num2=0):
return num1 - num2

if __name__ == '__main__':
	# 即使num2缺省也不会报错
	# 注意 定义函数时默认参数一定要在位置参数之后
    ans = minus(1)

其实也是相当好理解的,若默认参数可以放在位置参数之前,那我传参的个数少于参数个数,解释器到底是将参数给位置参数还是给默认参数?例如:

def minus2(num1=1, num2):
    return num1 - num2

if __name__ == '__main__':
	# 若给num1,则num2缺省了,不就报错了?
	# 若给num2,我怎么知道你是不是要给num1赋值?
    ans = minus2(1)

总结就是默认参数就是需要设置默认值的参数,默认参数必须放在位置参数之后,当需要使用默认值时可以不传默认参数。

关键字参数

其实关键字参数与默认参数就是同一个概念,在不同视角下的不同命名。

在调用函数时以等号键值对的形式传参,这个参数就叫做关键字参数,即add(1,b=2)1为位置参数,b=2为关键字参数。在定义函数的视角来看,add(a,b=3)a为位置参数,b=3为默认参数,默认值为3。

可变参数

可变参数包含了两种参数传递方式,*args包裹位置传递和**kwargs包裹关键字传递两种方式。在定义函数时有可能出现不确定需要传递的参数的个数的情况,这时可变参数将会给我们带来极大地便利。

*args包裹位置传递

这回直接结合上面两种参数来看:

def fun(num, num2=1, *args):
print(args)  # (3, 4, 5)
print(*(args))  # 3 4 5

if __name__ == '__main__':
	# 1和2 分别对应位置参数与关键字参数
	# 剩余的3 4 5都传递给了 *args
	# 通过print(args)可以看到这个参数装的是一个元组,里面都是剩余的没对应上的参数
	# 通过 *(args)拆包可以直接将里面的参数获取
    fun(1, 2, 3, 4, 5)

从参数定义的位置来看,函数**参数从左到右依次是位置参数、关键字参数(默认参数)、可变参数(*args)**这个位置是固定的,即不能乱改,原因类似于上面的默认参数必须在位置参数之后。

总结就是*args的位置需要放在所有参数之后,它可以容纳所有未对应上位置的实参,且传递参数的时候会被封装成一个元组形式,可以通过拆包的方式获取里面的参数。

**kwargs包裹关键字传递

同样的,直接从代码里看:

def fun(num, num2=1, *args, **kwargs):
print(args)  # (3, 4, 5)
print(*(args))  # 3 4 5
# 直接打印kwargs可以看到传来的是一个字典 即前面的3 4 5 都丢进了*args里,而**kwargs装的是没有对应上的关键字参数
print(kwargs)  # {'name': 'ali', 'age': 100}
# 通过拆包我们看到里面包含的关键字有name age
print(*(kwargs))  # name age
# 通过字典的方式获取关键字对应的值
print(kwargs['name'], kwargs['age'])  # ali 100

if __name__ == '__main__':
    fun(1, 2, 3, 4, 5, name='ali', age=100)

翻源码

先来看下面这段源码,来自于pyplot的barbs函数:

@docstring.copy(Axes.barbs)
def barbs(*args, data=None, **kw):
return gca().barbs(
    *args, **({"data": data} if data is not None else {}), **kw)

显然,被打脸了,data=None作为默认参数是可以放再*args后面,那么我们仿一下这个函数,看看它传进来的值都是这么赋值的。

def fun2(*args, data=None, **kwargs):
    print(args)  # (1, 2, 3, 4)
    print(data)  # 123
    print(kwargs)  # {'name': 'ali', 'age': 100}

if __name__ == '__main__':
	# 1 2 3 4都传给了*args
	# data=123 作为关键字参数传给了data=None 从而后面的kwargs就不能再有data这个关键字了 否则会报重复关键字错误
	# 在确定后面没有默认参数后,将接下来的关键字参数都丢给了**kwargs
    fun2(1, 2, 3, 4, data=123, name='ali', age=100)

接着上面的源码,它调用了gca().barbs并返回这个函数的返回值,来看看这个函数是怎么接收参数的:

def barbs(self, *args, **kw):
# 可以看到这个函数只有*args和**kwargs

那么再来仿一个函数:

def fun2(*args, data=None, **kwargs):
    print(args)  # (1, 2, 3, 4)
    print(data)  # 123
    print(kwargs)  # {'name': 'ali', 'age': 100}

	# 可以看到*args的参数直接丢给fun3了
	# 但是data做了一个简单的判断封装成了字典 跟着**kwargs一起丢给fun3的**kwargs
    fun3(*args, **({'data': data} if data is not None else {}), **kwargs)

def fun3(*args, **kwargs):
    print(args)  # (1, 2, 3, 4)
    print(kwargs)  # {'data': 123, 'name': 'ali', 'age': 100}	

if __name__ == '__main__':
    fun2(1, 2, 3, 4, data=123, name='ali', age=100)

从这个例子能学到什么呢?

首先要纠正错误说法,默认参数也是可以放在*args后面,想想也是没错的,因为编译器可以识别出后面的关键字参数已经不是*args的内容了,接下来遇上了默认参数,一一赋值给关键字参数,剩下的再给**kwargs就可以了。

其次,**kwargs是可以接收多个字典并且封装成一个大字典的,他的过程就是将所有关键字参数、字典都拆解然后再重新封装成字典的过程,最终在fun3中接收到的就是一个大字典。

总结

看了那么多,最终只需要搞清楚这些参数的特点和要求就可以了,具体实际的应用还是需要看使用者的操作。我觉得我还是讲的不够清晰,不过不要紧我还是给大家找了一些相关资料,大家可以自行补充。

python的位置参数、默认参数、关键字参数、可变参数区别

Python 为什么会有命名关键字参数?

发布了18 篇原创文章 · 获赞 4 · 访问量 2207

猜你喜欢

转载自blog.csdn.net/Alian_W/article/details/105131376
今日推荐