python学习-- 理解'*','*args','**','**kwargs'
刚开始学习Python的时候,对有关args,kwargs,和*的使用感到很困惑。相信对此感到疑惑的人也有很多。我打算通过这个帖子来排解这个疑惑(希望能减少疑惑)。
让我们通过以下5步来理解:
1. 通过一个函数调用来理解’*’的作用
2. 通过一个函数的定义来理解’*args’的含义
3. 通过一个函数的调用来理解’**’的作用
4. 通过一个函数的定义来解’**kwargs’的含义
5. 通过一个应用实例来说明’args’,’kwargs’应用场景以及为何要使用它
通过一个函数调用来理解’*’的作用
定义一个含三个位置参数的函数”fun”.
- >>> def fun(a,b,c):
- ... print a,b,c
- ...
传三个位置参数调用此函数
- >>> fun(1,2,3)
- 1 2 3 #输出
可以看到出入三个位置参数调用此函数,会打印出三个参数
现在我们定义一个含三个整数的数列,并使用’*’
- >>> l = [1,2,3]
- >>> fun(*l)
- 1 2 3 #输出
‘*’ 做了什么?
它拆开数列’l’的数值作为位置参数,并吧这些位置参数传给函数’fun’来调用。
因此拆数列、传位置参数意味着fun(*l)与fun(1,2,3)是等效的,因为l = [1,2,3]。
试着数列中使用其他数值
- >>> l = [4,8,0]
- >>> fun(*l)
- 4 8 0 #输出
接下来我们试着在数列中放四个数值,调用函数会出现什么情况呢
- >>> l = [3,6,9,1]
- >>> fun(*l)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: fun() takes exactly 3 arguments (4 given)
在这次调用中我们并没有得到合适的结果,触发了TypeWrror异常。很容易看到错误内容”fun() takes exactly 3 arguments (4 given)”.
为什么会发生这种情况呢?
数列’l’含有四个数值.因此,我们试图调用’fun(*l)’,’l’中数值拆开传给函数fun作为位置参数。但是,’l’中有四个数值,调用’fun(*l)’相当于调用’fun(3,6,9,1)’,又因为函数’fun’定义中只用三个位置参数,因此我们得到这个错误。同理,同样的步骤,数列’l’中有两个数值情况,注意error内容。
- >>> l = [7,4]
- >>> fun(*l)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: fun() takes exactly 3 arguments (2 given)
‘*l’与位置参数混合使用
- >>> fun(23, *l)
- 23 7 4
在这里,我们给出一个位置参数23,和从数列’l’拆除的两个数值7和4,因此三个参数23,7和4传给了函数’fun’
通过一个函数的定义来理解’*args’的含义
修改函数的定义:
- >>> def fun(*args):
- ... print args
- ...
传一个位置参数调用此函数
- >>> fun(13)
- (13,)
传多个参数调用此函数
- >>> fun(11,93,43)
- (11, 93, 43)
‘*args’在函数定义中是做什么用的?
它接收元组作为位置参数,而非是常见的参数列表。在这里,”args”是个元组。在我们解释中不要担心”常见的参数”这部分的理解,这个会在接下来的例子中逐渐明了。在上个例子中,调用函数打印”args”时,他会打印元组中包含的所有数值。
我们重新定义函数,”*args”与”常规参数列表”混合使用
- >>> def fun(a, *args):
- ... print "a is ", a
- ... print "args is ", args
- ...
在这个函数定义中,参数”a”代表”常规参数列表”。
传四个位置参数调用此函数:
- >>> fun(11,12,34,43)
- a is 11
- args is (12, 34, 43)
很容易看到,’a’打印出为11,即第一个位置参数。’a’之后只一个参数’*args’.因此,’args’接收除常规参数之外的位置参数作为元组。因此元组args作为元组接收12,34和43。
我们也可以传一个位置参数来调用此函数:
- >>> fun(91)
- a is 91
- args is ()
在这里,我们传的唯一一个参数分配给了常规参数’a’.因此,’args’接收到一个空元组。
既然我们获取了”args”,我们可以提取需要的数值来做我们想做的事情。重新定义”fun”:
- >>> def fun(a, *args):
- ... print a
- ... print "args can receive a tuple of any number of arguments, let's print all that."
- ... for arg in args:
- ... print arg
- ...
现在我们传任意个参数来调用此函数:
- >>> fun(1,5,6,7)
- 1
- args can receive a tuple of any number of arguments, let's print all that.
- 5
- 6
- 7
‘args’既然是元组,我们就可以遍历它。
现在我们考虑使用所有能得到的参数的场景。我们需要使用两个函数,第一个函数带有任意个参数,并通过另外一个函数计算除第一参数的其他参数之和。奇怪的用例,但我们只需回顾我们目前所做的。我们的目的就是在一个函数中获取可变参数,并把这些参数餐给另一个函数。
第一步我们写一个函数计算和。在这个用例中,这个函数会在第一个函数中应用。
- >>> def calculate_sum(*args):
- ... return sum(args)
- ...
在这个函数中,我们使用内建函数’sum’,它使用元组或数列作为参数,返回元组所有元素的和。从函数的定义可以看出’args’接收包含传给此函数位置参数的元组.因此,’args’是一个元组,简介的作为函数’sum’的参数。接下来定义另外一个函数,该函数有任意个参数,并利用上一个函数来计算除第一个参数之外的其他参数的和。
- >>> def ignore_first_calculate_sum(a,*iargs):
- ... required_sum = calculate_sum(*iargs)
- ... print "sum is ", required_sum
- ...
我们可以传任意个参数给这个函数。第一个参数被常规参数’a’接收,其他参数被’iargs’作为元组接收。正如我们考虑的案例,计算除第一个参数之外的其他参数的和。因此,我们用’a’接收第一个参数,’iargs’是包含其他参数的元组。我们用到函数’calculate_sum’,但’calculate_sum’需要多个位置参数作为元组传给’args’。所以在函数’ignore_first_calculate_sum’需要拆元组’iargs’,然后将元素作为位置参数传给’calculate_sum’.注意,用’*’拆元组。
所以,我们这样调用’required_sum=calculate_sum(*iargs)’.
‘required_sum=calculate_sum(iargs)’不能这么调用,因为传给’calculate_sum’之前我们需要unpack数值。不使用’*’将不会unpack数值,也就不能执行想要的动作。调用函数如下:
- >>> ignore_first_calculate_sum(12, 1,4,5)
- sum is 10
- >>> ignore_first_calculate_sum()
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: ignore_first_calculate_sum() takes at least 1 argument (0 given)
得到想要的结果。
通过一个函数的调用来理解’**’的作用
定义一个三个参数的函数,并用多种方式调用:
- >>> def fun(a, b, c):
- ... print a, b, c
- ...
- >>> fun(1,5,7)
- 1 5 7
- >>> fun(a=1,b=5,c=7)
- 1 5 7
使用”**”调用函数,这种方式我们需要一个字典.注意:在函数调用中使用”*”,我们需要元组;在函数调用中使用”**”,我们需要一个字典
- >>> d={'b':5, 'c':7}
- >>> fun(1, **d)
- 1 5 7
在函数调用中”**”做了什么?
它unpack字典,并将字典中的数据项作为键值参数传给函数。因此,”fun(1, **d)”的写法与”fun(1, b=5, c=7)”等效.
为了更好的理解再多举几个例子:
- >>> d = {'c':3}
- >>> fun(1, 4, **d)
- 1 4 3
- >>> d = {'a':7, 'b':3, 'c':8}
- >>> fun(**d)
- 7 3 8
让我们制造一些错误:
- >>> d = {'a':7, 'b':3, 'c':8, 'd':90}
- >>> fun(**d)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: fun() got an unexpected keyword argument 'd'
这次调用等同于’fun(a=7, b=3, c=8, d=90)’,但函数只需要三个参数,因此我们得到TypeError
- >>> d = {'a':7, 'b':3,'d':90}
- >>> fun(**d)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: fun() got an unexpected keyword argument 'd'
fun(**d)等同于fun(a=7, b=3, d=90).传给函数”fun”想要的参数个数,但参数列表中并没有’d’,调用中’d’键值参数传给函数导致TypeError.
So, “*” unpacks the dictionary i.e the key values pairs in the dictionary as keyword arguments and these are sent as keyword arguments to the function being called. “” unpacks a list/tuple i.e the values in the list as positional arguments and these are sent as positional arguments to the function being called.
通过函数定义来理解’**kwargs’的含义
重定义函数”fun”:
- >>> def fun(a, **kwargs):
- ... print a, kwargs
- ...
此函数只用一个位置参数,因为常规参数列表中只有一个变量’a’.但是通过”**kwargs”,可以传多个键值参数。
- >>> fun(1, b=4, c=5)
- 1 {'c': 5, 'b': 4}
- >>> fun(45, b=6, c=7, d=8)
- 45 {'c': 7, 'b': 6, 'd': 8}
在函数定义中”**kwargs”意味着什么?
用”**kwargs”定义函数,kwargs接收除常规参数列表职位的键值参数字典。在这里’kwargs’是个字典。
重新定义函数:
- >>> def fun(a, **kwargs):
- ... print "a is ", a
- ... print "We expect kwargs 'b' and 'c' in this function"
- ... print "b is ", kwargs['b']
- ... print "c is ", kwargs['c']
- ...
- >>> fun(1, b=3,c=5)
- a is 1
- We expect kwargs 'b' and 'c' in this function
- b is 3
- c is 5
错误调用:
- >>> fun(1, b=3, d=5)
- a is 1
- We expect kwargs 'b' and 'c' in this function
- b is 3
- c is
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "<stdin>", line 5, in fun
- KeyError: 'c'
上面的调用,位置参数’a’和键值参数’b’都打印出来了。传入的其他键值参数是’d’,函数需要键值参数’c’,并从字典’kwargs’获取。但没有传入键值’c’,引发KeyError.如果传入了键值’c’就不会引发这个错误
- >>> fun(1, b=3, d=5, c=9)
- a is 1
- We expect kwargs 'b' and 'c' in this function
- b is 3
- c is 9
由于’**kwargs’在函数参数列表中,我们可以传任意个键值参数。上面的调用传入了”d”,但函数并没用到。
另外一个错误:
- >>> fun(1, {'b':2, 'c':34})
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: fun() takes exactly 1 argument (2 given)
正如错误提示,函数’fun’只需要一个位置参数,却给了两个。尽管’kwargs’接收键值参数作为一个字典,但你不能传一个字典作为位置参数给’kwargs’.你可以像下面那样调用:
- >>> fun(1, **{'b':2, 'c':34})
- a is 1
- We expect kwargs 'b' and 'c' in this function
- b is 2
- c is 34
在一个字典前使用”**”可以unpack字典,传字典中的数据项作为键值参数。
通过一个应用实例来说明’args’,’kwargs’应用场景以及为何要使用它
在任何时候继承类和重写方法的,我们应当用到’*args’和’**kwargs’将接收到的位置参数和键值参数给父类方法。通过实例我们更好的理解
- >>> class Model(object):
- ... def __init__(self, name):
- ... self.name = name
- ... def save(self, force_update=False, force_insert=False):
- ... if force_update and force_insert:
- ... raise ValueError("Cannot perform both operations")
- ... if force_update:
- ... print "Updated an existing record"
- ... if force_insert:
- ... print "Created a new record"
定义一个类,我们可以创建类的对象,类的对象有一个方法’save()’.假设类的对象可以通过save()方法保存到数据库中。通过函数save()参数来决定是否在数据库中创建一条记录或者更新现存的记录。
构造一个新类,类有’Model’的行为,但我们只有检查一些条件后才会保存这个类的对象。这个新类继承’Model’,重写’Model’的’save()’
- >>> class Child(Model):
- ... def save(self, *args, **kwargs):
- ... if self.name=='abcd':
- ... super(ChildModel, self).save(*args, **kwargs)
- ... else:
- ... return None
- ...
实际上对应的保存动作发生在’Model’的’save’方法中。所以我们调用子类的的’save()’方法而非’Model’的方法.子类ChildModel的’save()’接收任何父类save()需要的参数,并传给父类方法。因此,子类’save()’方法参数列表中有”*args”和”**kwargs”,它们可以接收任意位置参数或键值参数,常规参数列表除外。
下面创建ChildModel实体并保存:
- >>> c=ChildModel('abcd')
- >>> c.save(force_insert=True)
- Created a new record
- >>> c.save(force_update=True)
- Updated an existing record
这里传兼职参数给对象的save()方法。调用的是子类的save(),It received a dictionary containing the keyword argument in “kwargs”. Then it used “**” to unpack this dictionary as keyword arguments and then passed it to the superclass save(). So, superclass save() got a keyword argument ‘force_insert’ and acted accordingly.
python学习-- 理解'*','*args','**','**kwargs'的更多相关文章
- python可变参数*args, **kwargs
python可变参数*args, **kwargs def foo(* args, ** kwargs): print ' args = ', args print ' kwargs = ', k ...
- 理解理解python中的'*','*args','**','**kwargs'
http://blog.csdn.net/callinglove/article/details/45483097 讲了一大堆, 我也是用来理解类继承当中的参数行为的. =============== ...
- 理解python中的'*','*args','**','**kwargs'
本文来源:http://blog.csdn.net/callinglove/article/details/45483097 让我们通过以下6步来理解: 1. 通过一个函数调用来理解’*’的作用 2. ...
- python 中参数*args, **kwargs
def func(*args, **kwargs): print 'args : ', args print 'kwargs :', kwargs def test(): func(1,2,'x',' ...
- python函数参数*args **kwargs
毕业多年,把C++都就饭吃了....今天居然在纠结什么是形参什么是实参..... 定义函数里面写的参数就是形参,因为没有内存占用,实际调用时写的参数就是实参,因为有内存占用和传值 然后就是位置参数,可 ...
- 教你函数如何传参更加方便 ----python 传参 *args **kwargs
*args 和 **kwargs 可以将不定数量的参数传递给函数 *args 和 **kwargs,前者适用于 传入非键值对的可变数量的参数列表,后者适用于 传入不定长度的键值对, 作为参数传递给函 ...
- python 位置参数和关键字参数 *args **kwargs
#!/usr/bin/env pythondef foo(*args,**kwargs): print('args: {0}'.format(args)) print('kwargs {0}'.for ...
- 理解 Python 中的 *args 和 **kwargs
Python是支持可变参数的,最简单的方法莫过于使用默认参数,例如: def test_defargs(one, two = 2): print 'Required argument: ', one ...
- python学习笔记:*args和**kwargs使用原理?
一.*args和**kwargs原理 先看个例子: def test(*args,**kwargs): print("args =",args) print("kwarg ...
随机推荐
- Properties-转换流-打印流-序列化和反序列化-Commons-IO工具类
一.Properties 类(java.util) 概述:Properties 是一个双列集合;Properties 属于map的特殊的孙子类;Properties 类没有泛型,propert ...
- ios 根据字典自动生成属性
- (void)createPropertyCode{ NSMutableString *codes = [NSMutableString string]; // 遍历字典 [self enumera ...
- Java之栈空间和堆空间
1.变量的命名 (1)由字母,数字和下划线构成,首字母以字母或下划线开头 (2)变量的命名遵循见名知义 (3)Java变量命名建议不用中文 (4)变量名首字母建议不用大写字母开头 (5)用驼峰命名法命 ...
- (转载)WPF:DataGrid设置行、单元格的前景色
WPF:DataGrid设置行.单元格的前景色 0. 说明 /********************************** 本示例实现功能1.DataGrid基本操作2.列标题样式3.内容居中 ...
- 洛谷 P2663 越越的组队
题目描述 班级要组织一场综合能力竞赛,全班同学(N个,N是偶数)分成两队互相竞争.老师找到了越越并给了越越一张全班同学综合能力测试的成绩,要求他从全班同学中选出一半(他自己也可能被选),并要求这些同学 ...
- UWP开发:存储容器设置&复合设置数据
有时候为了将应用设置进行分类,需要创建新的容器进行存储应用设置的信息. 1,容器的创建:在一个根容器里嵌套一个新容器 1)首先获取根容器. 2)调用ApplicationDataContainer.C ...
- MovieReview—Ghost in the Shell 2: Innocence(攻壳机动队2:无罪)
Doll killing event The movie was developed around a series of doll murders. Barthes and o ...
- HDU 5451 Best Solver(fibonacci)
感谢这道题让我复习了一遍线代,还学习了一些奇奇怪怪的数论. 令 二项展开以后根号部分抵消了 显然有 所以要求的答案是 如果n比较小的话,可以直接对二项式快速幂,但是这题n很大 这个问题和矩阵的特征值以 ...
- cv2.threshold 阈值灰度
threshold函数的使用 图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果.在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大 ...
- Python——三目运算符
一.三目运算符 1.if语句三目运算符语法格式 Python可以通过if'语句来实现三目运算符的功能,因此可以把这种if语句当做三目运算符,具体语法格式如下: 返回True执行 if 表达式 else ...