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比较文件夹比另一同名文件夹多出的文件并复制出来的方法
Mar 05 Python
利用QT写一个极简单的图形化Python闹钟程序
Apr 07 Python
详解在Python程序中使用Cookie的教程
Apr 30 Python
Python中encode()方法的使用简介
May 18 Python
Python实现的将文件每一列写入列表功能示例【测试可用】
Mar 19 Python
Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】
Mar 30 Python
python使用paramiko模块通过ssh2协议对交换机进行配置的方法
Jul 25 Python
自适应线性神经网络Adaline的python实现详解
Sep 30 Python
浅析Python 抽象工厂模式的优缺点
Jul 13 Python
python3操作redis实现List列表实例
Aug 04 Python
如何利用Python实现n*n螺旋矩阵
Jan 18 Python
Python使用mitmproxy工具监控手机 下载手机小视频
Apr 18 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
How do I change MySQL timezone?
2008/03/26 PHP
PHP伪静态写法附代码
2008/06/20 PHP
ThinkPHP入口文件设置及相关注意事项分析
2014/12/05 PHP
php实现36进制与10进制转换功能示例
2017/01/10 PHP
PHP实现字符串的全排列详解
2019/04/24 PHP
用js实现控制内容的向上向下滚动效果
2007/06/26 Javascript
XRegExp 0.2: Now With Named Capture
2007/11/30 Javascript
引用外部脚本时script标签关闭的写法
2014/01/20 Javascript
JQuery1.8 判断元素是否绑定事件的方法
2014/07/10 Javascript
jQuery中常用动画效果函数(日常整理)
2016/09/17 Javascript
详解Javascript函数声明与递归调用
2016/10/22 Javascript
JS判断是否手机或pad访问实现方法
2016/12/09 Javascript
jquery实现手机端单店铺购物车结算删除功能
2017/02/22 Javascript
Vuejs仿网易云音乐实现听歌及搜索功能
2017/03/30 Javascript
layui分页效果实现代码
2017/05/19 Javascript
jQuery实现用户信息表格的添加和删除功能
2017/09/12 jQuery
微信小程序网络层封装的实现(promise, 登录锁)
2019/05/08 Javascript
js简单的分页器插件代码实例
2019/09/11 Javascript
微信小程序 (地址选择1)--选取搜索地点并显示效果
2019/12/17 Javascript
vue实现移动端H5数字键盘组件使用详解
2020/08/25 Javascript
在Python中使用zlib模块进行数据压缩的教程
2015/06/26 Python
pandas 空的dataframe 插入列名的示例
2018/10/30 Python
在Python中过滤Windows文件名中的非法字符方法
2019/06/10 Python
python找出因数与质因数的方法
2019/07/25 Python
详解python如何引用包package
2020/06/07 Python
python 指定源路径来解决import问题的操作
2021/03/04 Python
CSS3 真的会替代 SCSS 吗
2021/03/09 HTML / CSS
上海奥佳笔试题面试题
2016/11/16 面试题
十佳护士获奖感言
2014/02/18 职场文书
商业融资计划书
2014/04/29 职场文书
学校评语大全
2014/05/06 职场文书
MySQL的join buffer原理
2021/04/29 MySQL
利用Matlab绘制各类特殊图形的实例代码
2021/07/16 Python
Java 超详细讲解数据结构中的堆的应用
2022/04/02 Java/Android
SQL Server 忘记密码以及重新添加新账号
2022/04/26 SQL Server
MySQL 原理与优化之原数据锁的应用
2022/08/14 MySQL