Python函数参数类型*、**的区别


Posted in Python onApril 11, 2015

刚开始学习python,python相对于java确实要简洁易用得多。内存回收类似hotspot的可达性分析, 不可变对象也如同java得Integer类型,with函数类似新版本C++的特性,总体来说理解起来比较轻松。只是函数部分参数的"*"与"**",闭包等问题,着实令人迷糊了一把,弄清概念后写下此文记录下来,也希望本文能够帮助其他初学者。

所以本文是一篇学习笔记,着重于使用的细节和理解上,首先分别介绍了函数各种参数类型在调用和声明时的区别,及其在混用时需要注意的一些细节,之后讲了闭包相关的内容。如果有不对的地方欢迎指正。

函数参数不带“*”,"*" 与 "**"的区别
理解这个问题得关键在于要分开理解调用和声明语法中3者得区别.

函数调用区别

1. 不同类型的参数简述
#这里先说明python函数调用得语法为:

func(positional_args, keyword_args,

 *tuple_grp_nonkw_args, **dict_grp_kw_args)

 

 #为了方便说明,之后用以下函数进行举例

 def test(a,b,c,d,e):

  print a,b,c,d,e

举个例子来说明这4种调用方式得区别:
#-------------------------------

#positional_args方式

>>> test(1,2,3,4,5)

1 2 3 4 5
#这种调用方式的函数处理等价于

a,b,c,d,e = 1,2,3,4,5

print a,b,c,d,e
#-------------------------------

#keyword_args方式

>>> test(a=1,b=3,c=4,d=2,e=1)

1 3 4 2 1
#这种处理方式得函数处理等价于

a=1

b=3

c=4

d=2

e=1

print a,b,c,d,e
#-------------------------------

#*tuple_grp_nonkw_args方式

>>> x = 1,2,3,4,5

>>> test(*x)

1 2 3 4 5

#这种方式函数处理等价于
a,b,c,d,e = x

print a,b,c,d,e

#特别说明:x也可以为dict类型,x为dick类型时将键传递给函数

>>> y

{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}

>>> test(*y)

a c b e d
#---------------------------------

#**dict_grp_kw_args方式

>>> y

{'a': 1, 'c': 6, 'b': 2, 'e': 1, 'd': 1}

>>> test(**y)

1 2 6 1 1
#这种函数处理方式等价于

a = y['a']

b = y['b']

... #c,d,e不再赘述

print a,b,c,d,e

2. 不同类型参数混用需要注意的一些细节

接下来说明不同参数类型混用的情况,要理解不同参数混用得语法需要理解以下几方面内容.

首先要明白,函数调用使用参数类型必须严格按照顺序,不能随意调换顺序,否则会报错. 如 (a=1,2,3,4,5)会引发错误,; (*x,2,3)也会被当成非法.

其次,函数对不同方式处理的顺序也是按照上述的类型顺序.因为#keyword_args方式和**dict_grp_kw_args方式对参数一一指定,所以无所谓顺序.所以只需要考虑顺序赋值(positional_args)和列表赋值(*tuple_grp_nonkw_args)的顺序.因此,可以简单理解为只有#positional_args方式,#*tuple_grp_nonkw_args方式有逻辑先后顺序的.

最后,参数是不允许多次赋值的.

举个例子说明,顺序赋值(positional_args)和列表赋值(*tuple_grp_nonkw_args)的逻辑先后关系:

#只有在顺序赋值,列表赋值在结果上存在罗辑先后关系

#正确的例子1

>>> x = {3,4,5}

>>> test(1,2,*x)

1 2 3 4 5

#正确的例子2

>>> test(1,e=2,*x)

1 3 4 5 2
#错误的例子

>>> test(1,b=2,*x)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: test() got multiple values for keyword argument 'b'
#正确的例子1,处理等价于

a,b = 1,2 #顺序参数

c,d,e = x #列表参数

print a,b,c,d,e
#正确的例子2,处理等价于

a = 1 #顺序参数

e = 2 #关键字参数

b,c,d = x #列表参数
#错误的例子,处理等价于

a = 1 #顺序参数

b = 2 #关键字参数

b,c,d = x #列表参数

#这里由于b多次赋值导致异常,可见只有顺序参数和列表参数存在罗辑先后关系

函数声明区别

理解了函数调用中不同类型参数得区别之后,再来理解函数声明中不同参数得区别就简单很多了.

1. 函数声明中的参数类型说明

函数声明只有3种类型, arg, *arg , **arg 他们得作用和函数调用刚好相反. 调用时*tuple_grp_nonkw_args将列表转换为顺序参数,而声明中的*arg的作用是将顺序赋值(positional_args)转换为列表. 调用时**dict_grp_kw_args将字典转换为关键字参数,而声明中**arg则反过来将关键字参数(keyword_args)转换为字典.
特别提醒:*arg 和 **arg可以为空值.

以下举例说明上述规则:

#arg, *arg和**arg作用举例

def test2(a,*b,**c):

 print a,b,c

#---------------------------

#*arg 和 **arg可以不传递参数

>>> test2(1)

1 () {}

#arg必须传递参数

>>> test2()

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: test2() takes at least 1 argument (0 given)
#----------------------------

#*arg将顺positional_args转换为列表

>>> test2(1,2,[1,2],{'a':1,'b':2})

1 (2, [1, 2], {'a': 1, 'b': 2}) {}

#该处理等价于

a = 1 #arg参数处理

b = 2,[1,2],{'a':1,'b':2} #*arg参数处理

c = dict() #**arg参数处理

print a,b,c
#-----------------------------

#**arg将keyword_args转换为字典

>>> test2(1,2,3,d={1:2,3:4}, c=12, b=1)

1 (2, 3) {'c': 12, 'b': 1, 'd': {1: 2, 3: 4}}

#该处理等价于

a = 1 #arg参数处理

b= 2,3 #*arg参数处理

#**arg参数处理

c = dict()

c['d'] = {1:2, 3:4}

c['c'] = 12

c['b'] = 1

print a,b,c

2. 处理顺序问题

函数总是先处理arg类型参数,再处理*arg和**arg类型的参数. 因为*arg和**arg针对的调用参数类型不同,所以不需要考虑他们得顺序.

def test2(a,*b,**c):

 print a,b,c

>>> test2(1, b=[1,2,3], c={1:2, 3:4},a=1)

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: test2() got multiple values for keyword argument 'a'

#这里会报错得原因是,总是先处理arg类型得参数

#该函数调用等价于

#处理arg类型参数:

a = 1

a = 1  #多次赋值,导致异常

#处理其他类型参数

...

print a,b,c

闭包

python的函数,原本只能访问两个区域的变量:全局,和局部(函数上下文). 实际上,函数本身也是一个对象,也有自己的作用域. 闭包通过函数与引用集合的组合,使得函数可以在它被定义的区域之外执行. 这个集合可以通过func_closure来获取这个引用集合. 这与python处理全局变量得方式一样,只不过全局变量将引用集合存储在__globals__字段中.func_closure是一个存储cell类型的元组,每个cell存储一个上下文变量.

另外,旧版本得python的内部函数不能在其他作用域使用的原因,并不是因为每个作用域的变量严格相互隔离,而是脱离原本的作用域后,函数失去了原本上下文的引用。需要注意的是,闭包存储的上下文信息一样是浅拷贝,所以传递给内部函数的可变对象仍然会被其他拥有该对象引用得变量修改.

举个例子:

>>> def foo(x,y):

...     def bar():

...             print x,y

...     return bar

... 

#查看func_closure的引用信息

>>> a = [1,2]

>>> b = foo(a,0)

>>> b.func_closure[0].cell_contents

[1, 2]

>>> b.func_closure[1].cell_contents

0

>>> b()

[1, 2] 0
#可变对象仍然能被修改

>>> a.append(3)

>>> b.func_closure[0].cell_contents

[1, 2, 3]

>>> b()

[1, 2, 3] 0
Python 相关文章推荐
Python判断变量是否已经定义的方法
Aug 18 Python
Python Web框架Flask信号机制(signals)介绍
Jan 01 Python
Python中的Matplotlib模块入门教程
Apr 15 Python
python实现用户答题功能
Jan 17 Python
python3安装pip3(install pip3 for python 3.x)
Apr 03 Python
解决在pycharm中显示额外的 figure 窗口问题
Jan 15 Python
python 的 scapy库,实现网卡收发包的例子
Jul 23 Python
Python 共享变量加锁、释放详解
Aug 28 Python
PYTHON EVAL的用法及注意事项解析
Sep 06 Python
python3 mmh3安装及使用方法
Oct 09 Python
解决python中import文件夹下面py文件报错问题
Jun 01 Python
Python pip install之SSL异常处理操作
Sep 03 Python
Python中的多重装饰器
Apr 11 #Python
Python中的各种装饰器详解
Apr 11 #Python
将Django使用的数据库从MySQL迁移到PostgreSQL的教程
Apr 11 #Python
Python返回真假值(True or False)小技巧
Apr 10 #Python
Python选择排序、冒泡排序、合并排序代码实例
Apr 10 #Python
Python字符串中查找子串小技巧
Apr 10 #Python
简单介绍Ruby中的CGI编程
Apr 10 #Python
You might like
Windows下部署Apache+PHP+MySQL运行环境实战
2012/08/31 PHP
ThinkPHP使用心得分享-上传类UploadFile的使用
2014/05/15 PHP
十幅图告诉你什么是PHP引用
2015/02/22 PHP
CI框架实现优化文件上传及多文件上传的方法
2017/01/04 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
2020/05/02 PHP
使用js声明数组,对象在jsp页面中(获得ajax得到json数据)
2013/11/05 Javascript
浅析jQuery Mobile的初始化事件
2015/12/03 Javascript
原生js实现鼠标跟随效果
2017/02/28 Javascript
react实现一个优雅的图片占位模块组件详解
2017/10/30 Javascript
js构建二叉树进行数值数组的去重与优化详解
2018/03/26 Javascript
vue中element 上传功能的实现思路
2018/07/06 Javascript
JavaScript this绑定过程深入详解
2018/12/07 Javascript
vue+导航锚点联动-滚动监听和点击平滑滚动跳转实例
2019/11/13 Javascript
Vue+Element-UI实现上传图片并压缩
2019/11/26 Javascript
Python版实现微信公众号扫码登陆
2020/05/28 Javascript
在Python中处理字符串之isdigit()方法的使用
2015/05/18 Python
python实现文件路径和url相互转换的方法
2015/07/06 Python
举例讲解Linux系统下Python调用系统Shell的方法
2015/11/07 Python
在Python中执行系统命令的方法示例详解
2017/09/14 Python
利用Python+Java调用Shell脚本时的死锁陷阱详解
2018/01/24 Python
解决pycharm无法调用pip安装的包问题
2018/05/18 Python
Python发展史及网络爬虫
2019/06/19 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
python 使用socket传输图片视频等文件的实现方式
2019/08/07 Python
django 自定义filter 判断if var in list的例子
2019/08/20 Python
python爬虫用scrapy获取影片的实例分析
2020/11/23 Python
CSS3 2D模拟实现摩天轮旋转效果
2016/11/16 HTML / CSS
阿迪达斯西班牙官方网站:adidas西班牙
2016/07/21 全球购物
俄罗斯韩国化妆品网上商店:Cosmasi.ru
2019/10/31 全球购物
外国语学院毕业生自荐信
2013/10/28 职场文书
挖掘机司机岗位职责
2014/02/12 职场文书
公司周年庆典标语
2014/10/07 职场文书
2014年个人业务工作总结
2014/11/17 职场文书
党员检讨书范文
2014/12/27 职场文书
被委托人身份证明
2015/08/07 职场文书
php随机生成验证码,php随机生成数字,php随机生成数字加字母!
2021/04/01 PHP