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利用lxml读写xml格式的文件
Aug 10 Python
django项目运行因中文而乱码报错的几种情况解决
Nov 07 Python
Python对List中的元素排序的方法
Apr 01 Python
用python求一个数组的和与平均值的实现方法
Jun 29 Python
Python常用模块os.path之文件及路径操作方法
Dec 03 Python
使用python3 实现插入数据到mysql
Mar 02 Python
django API 中接口的互相调用实例
Apr 01 Python
Python之变量类型和if判断方式
May 05 Python
Python使用Excel将数据写入多个sheet
May 16 Python
python使用hdfs3模块对hdfs进行操作详解
Jun 06 Python
Python 多线程之threading 模块的使用
Apr 14 Python
解决Tkinter中button按钮未按却主动执行command函数的问题
May 23 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
php中使用cookie来保存用户登录信息的实现代码
2012/03/08 PHP
destoon找回管理员密码的方法
2014/06/21 PHP
Netbeans 8.2与PHP相关的新特性介绍
2016/10/08 PHP
CI框架使用composer安装的依赖包步骤与方法分析
2016/11/21 PHP
浅谈Laravel队列实现原理解决问题记录
2017/08/19 PHP
静态页面的值传递(三部曲)
2006/09/25 Javascript
Extjs学习笔记之五 一个小细节renderTo和applyTo的区别
2010/01/07 Javascript
Javascript学习笔记7 原型链的原理
2010/01/11 Javascript
javascript多种数据类型表格排序代码分析
2010/09/11 Javascript
动态标签 悬停效果 延迟加载示例代码
2013/11/21 Javascript
jquery实现省市select下拉框的替换(示例代码)
2014/02/22 Javascript
jquery默认校验规则整理
2014/03/24 Javascript
Javascript定义类(class)的三种方法详解
2015/03/13 Javascript
jquery uploadify隐藏上传进度的实现方法
2017/02/06 Javascript
用vue构建多页面应用的示例代码
2017/09/20 Javascript
javaScript之split与join的区别(详解)
2017/11/08 Javascript
详解VUE2.X过滤器的使用方法
2018/01/11 Javascript
vue获取当前激活路由的方法
2018/03/17 Javascript
Express之托管静态文件的方法
2018/06/01 Javascript
layer.open提交子页面的form和layedit文本编辑内容的方法
2019/09/27 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
详解Python中的元组与逻辑运算符
2015/10/13 Python
python使用str &amp; repr转换字符串
2016/10/13 Python
flask的orm框架SQLAlchemy查询实现解析
2019/12/12 Python
tensorflow实现测试时读取任意指定的check point的网络参数
2020/01/21 Python
python中pdb模块实例用法
2021/01/15 Python
HTML5到底会有什么发展?HTML5的前景展望
2015/07/07 HTML / CSS
详解前端HTML5几种存储方式的总结
2016/12/27 HTML / CSS
P D PAOLA法国官网:西班牙著名的珠宝首饰品牌
2020/02/15 全球购物
sort命令的作用和用法
2012/11/04 面试题
物业客服专员岗位职责
2013/11/30 职场文书
实习鉴定评语
2014/01/19 职场文书
《小壁虎借尾巴》教学反思
2014/02/16 职场文书
人力资源管理专业应届生求职信
2014/04/24 职场文书
体育口号大全
2014/06/18 职场文书
教师素质教育心得体会
2016/01/19 职场文书