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生成器generator之next和send的运行流程(详解)
May 08 Python
python好玩的项目—色情图片识别代码分享
Nov 07 Python
Python 爬虫之Beautiful Soup模块使用指南
Jul 05 Python
Python设计模式之享元模式原理与用法实例分析
Jan 11 Python
Python利用matplotlib做图中图及次坐标轴的实例
Jul 08 Python
python多线程+代理池爬取天天基金网、股票数据过程解析
Aug 13 Python
解决Django删除migrations文件夹中的文件后出现的异常问题
Aug 31 Python
浅谈python中统计计数的几种方法和Counter详解
Nov 07 Python
Python实现计算长方形面积(带参数函数demo)
Jan 18 Python
Python 实现将numpy中的nan和inf,nan替换成对应的均值
Jun 08 Python
Python3 用matplotlib绘制sigmoid函数的案例
Dec 11 Python
 Python 中 logging 模块使用详情
Mar 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
SONY SRF-22W(33W)的电路分析和维修案例
2021/03/02 无线电
第四节 构造函数和析构函数 [4]
2006/10/09 PHP
PHP读取RSS(Feed)简单实例
2014/06/12 PHP
使用配置类定义Codeigniter全局变量
2014/06/12 PHP
php实现cookie加密的方法
2015/03/10 PHP
Ubuntu12下编译安装PHP5.3开发环境
2015/03/27 PHP
php实现阿拉伯数字和罗马数字相互转换的方法
2015/04/17 PHP
Node.js中调用mysql存储过程示例
2014/12/20 Javascript
javascript实现2016新年版日历
2016/01/25 Javascript
原生JavaScript编写canvas版的连连看游戏
2016/05/29 Javascript
BootStrap按钮标签及基本样式
2016/11/23 Javascript
vuejs绑定class和style样式
2017/04/11 Javascript
让div运动起来 js实现缓动效果
2017/07/06 Javascript
详解Vue如何支持JSX语法
2017/11/10 Javascript
浅谈Angular文字折叠展开组件的原理分析
2017/11/24 Javascript
JavaScript callback回调函数用法实例分析
2018/05/08 Javascript
Angular6 写一个简单的Select组件示例
2018/08/20 Javascript
JS实现的贪吃蛇游戏完整实例
2019/01/18 Javascript
js实现一个页面多个倒计时的3种方法
2019/02/25 Javascript
手把手教你 CKEDITOR 4 扩展插件制作
2019/06/18 Javascript
layui富文本编辑器前端无法取值的解决方法
2019/09/18 Javascript
JavaScript实现滑块验证解锁
2021/01/07 Javascript
python根据开头和结尾字符串获取中间字符串的方法
2015/03/26 Python
python实现爬虫统计学校BBS男女比例之数据处理(三)
2015/12/31 Python
用Python登录好友QQ空间点赞的示例代码
2017/11/04 Python
Python中staticmethod和classmethod的作用与区别
2018/10/11 Python
使用python绘制二元函数图像的实例
2019/02/12 Python
Python实现的远程文件自动打包并下载功能示例
2019/07/12 Python
python gdal安装与简单使用
2019/08/01 Python
详解Python中第三方库Faker
2020/09/25 Python
英国汽车和货车租赁网站:Hertz英国
2016/09/02 全球购物
ruby如何进行集成操作?Ruby能进行多重继承吗?
2013/10/16 面试题
中职生自荐信范文
2014/06/15 职场文书
经贸日语专业自荐信
2014/09/02 职场文书
乡镇党建工作汇报材料
2014/10/27 职场文书
2019下半年英语教师的教学工作计划(3篇)
2019/09/25 职场文书