Python函数参数类型*、**的区别
作者:junjie 发布时间:2022-03-01 18:09:54
刚开始学习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程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。异常处理: 本站
- 一、递归原理小案例分析(1)# 概述递归:即一个函数调用了自身,即实现了递归 凡是循环能做到的事,递归一般都能做到!(2)# 写
- 字符画,一种由字母、标点、汉字或其他字符组成的图画。简单的字符画是利用字符的形状代替图画的线条来构成简单的人物、事物等形象,它一般由人工制作
- Python提供了一些内建函数用于基本对象类型:cmp(),repr(),str(),type()和等同于repr()的('
- 产品通常分两种,一种是遵循现有用户习惯,一种是颠覆用户习惯。至于什么是用户习惯,你现在用右手还是左手操作鼠标,这就是你的习惯。很多公司团队专
- MaxDB和MySQL是独立的数据库管理服务器。系统间的协同性是可能的,通过相应的方式,系统能够彼此交换数据。要想在MaxDB和MySQL之
- 前言Scrapy是一个非常好的抓取框架,它不仅提供了一些开箱可用的基础组建,还能够根据自己的需求,进行强大的自定义。本文主要给大家介绍了关于
- 1. 什么是 CSV 文件CSV(逗号分隔值)文件是使用逗号分隔信息的文本文件。该文件的每一行都是一条数据记录,也就意味着它可以用于以表格的
- 1、Python的内置高阶函数1.1 map()map()会根据提供的函数对指定序列做映射语法格式:map(function, iterab
- 首先,eval模式和train模式得到不同的结果是正常的。我的模型中,eval模式和train模式不同之处在于Batch Normaliza
- 本文实例讲述了Python实现对一个函数应用多个装饰器的方法。分享给大家供大家参考,具体如下:下面的例子展示了对一个函数应用多个装饰器,可以
- 我们可以用动态产生变量的方法,从表格里捕捉数据,动态地创造“剥离”变量引号并且“清理”它,见下列代码,我们只需键入变量名称,选择 query
- 【原文地址】 Recipe: Deploying a SQL Database to a Remote Hosting Environmen
- 本文实例讲述了Python实现从订阅源下载图片的方法。分享给大家供大家参考。具体如下:这段代码是基于python 3.4实现的,和pytho
- 自定义模板403<!DOCTYPE html><html lang="en"><head&
- 网站发布到IIS后,发现网站使用的Bootstrap框架所引用的woff字体无法正常显示。于是跟踪http请求,发现woff字体请求出现GE
- 前言本方法基于web2py框架,使用web2py的完整网站数据包创建简单网站。web2py 是一个为Python语言提供的全功能Web应用框
- 关于《回访确认》这件事情,在我blog和5Gme的回复里普遍出现这么几个疑问:1、下了订单却放弃支付的心理因素是什么?2、回访电话可以促进订
- 一个网站空间,但是却可以实现多个域名的访问的一段ASP代码:<%if Request.ServerVariables("SE
- 1.连接测试连接是否成功:import redisr = redis.Redis(host='192.168.136.102'