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实现删除文件与目录的方法
Nov 10 Python
详解Python程序与服务器连接的WSGI接口
Apr 29 Python
python3实现暴力穷举博客园密码
Jun 19 Python
python中利用Future对象异步返回结果示例代码
Sep 07 Python
numpy排序与集合运算用法示例
Dec 15 Python
python3.6.3转化为win-exe文件发布的方法
Oct 31 Python
对python周期性定时器的示例详解
Feb 19 Python
python Tkinter的图片刷新实例
Jun 14 Python
Python实现性能自动化测试竟然如此简单
Jul 30 Python
Python三元运算与lambda表达式实例解析
Nov 30 Python
python 微信好友特征数据分析及可视化
Jan 07 Python
python实现逻辑回归的示例
Oct 09 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 xfocus防注入资料
2008/04/27 PHP
快速开发一个PHP扩展图文教程
2008/12/12 PHP
php中session过期时间设置及session回收机制介绍
2014/05/05 PHP
PHP实现显示照片exif信息的方法
2014/07/11 PHP
PHP生成RSS文件类实例
2014/12/05 PHP
php 判断页面或图片是否经过gzip压缩的方法
2017/04/05 PHP
js验证表单大全
2006/11/25 Javascript
js replace 与replaceall实例用法详解
2013/08/03 Javascript
异步动态加载JS并运行(示例代码)
2013/12/13 Javascript
浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
2014/11/23 Javascript
js基础之DOM中元素对象的属性方法详解
2016/10/28 Javascript
利用VUE框架,实现列表分页功能示例代码
2017/01/12 Javascript
BootStrap表单宽度设置方法
2017/03/10 Javascript
Angular CLI 安装和使用教程
2017/09/13 Javascript
JS中的多态实例详解
2017/10/15 Javascript
vue的安装及element组件的安装方法
2018/03/09 Javascript
nodejs更改项目端口号的方法
2018/05/13 NodeJs
利用vue.js把静态json绑定bootstrap的table方法
2018/08/28 Javascript
vue-cli3+typescript新建一个项目的思路分析
2019/08/06 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
基于NodeJS开发钉钉回调接口实现AES-CBC加解密
2020/08/20 NodeJs
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
Vue 实现拨打电话操作
2020/11/16 Javascript
Python 时间处理datetime实例
2008/09/06 Python
Python过滤txt文件内重复内容的方法
2018/10/21 Python
python实现将列表中各个值快速赋值给多个变量
2020/04/02 Python
意大利珠宝店:Luxury Zone
2019/01/05 全球购物
乌克兰第一的珠宝网上商店:Gold.ua
2019/11/29 全球购物
大学生冰淇淋店商业计划书
2014/01/14 职场文书
大学新生军训自我鉴定
2014/03/18 职场文书
八荣八耻的活动方案
2014/08/16 职场文书
健康状况证明模板
2014/10/23 职场文书
文明上网主题班会
2015/08/14 职场文书
财务人员廉洁自律心得体会
2016/01/13 职场文书
如何计划开一家便利店?
2019/07/31 职场文书
Django集成富文本编辑器summernote的实现步骤
2021/05/31 Python