当前位置: 首页 > 编程笔记 >

Python函数参数类型*、**的区别

璩无尘
2023-03-14
本文向大家介绍Python函数参数类型*、**的区别,包括了Python函数参数类型*、**的区别的使用技巧和注意事项,需要的朋友参考一下

刚开始学习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

 类似资料:
  • 问题内容: 我有一个变量f。如何确定其类型?这是我的代码,输入到python解释器中,显示出我在Google上发现的许多示例的成功模式都出错了。(提示:我是Python的新手。) 使用fa函数,我尝试使用u = str(type(f))将type(f)的返回值转换为字符串。但是当我尝试u.print()时,我收到一条错误消息。这给我提出了另一个问题。在Unix下,来自Python的错误消息会出现在

  • 我正在用Java构建一个库管理应用程序。 我有一个名为 的抽象类。它有一个称为 的抽象方法。 有一个名为< code>Newspaper的子类,它当然实现了< code>equals,其签名与< code>equals在< code>Material中的签名完全相同: Java无法解析< code > elementoacomparer 的任何方法。它们都存在于< code >报纸中,该报纸确实扩

  • OpenGL定义了C函数来管理资源。我编写了一个简单的包装器来以RAII的方式处理它们。函数对类似于和。但是,也有一些函数对适用于资源数组,例如和。对于前者,我编写了一个简单的类来完成这项工作,对于后者,我编写了另一个处理数组的类。然而,我注意到有时我只使用一个缓冲区或纹理,在那里我不必承担向量的费用,我想如果发布函数在开始时采用大小参数,我会专门化类析构函数,但是... 对于上述SSCCE g树

  • 我不想为每个类型T编写这个方法只是为了调用getMessage()并将其传递给下一个方法。 有可能写出这样的方法吗?我只想访问ConstraintViolation接口的方法,这些方法不依赖于类型T(如字符串getMessage())。

  • 一、泛型 Scala 支持类型参数化,使得我们能够编写泛型程序。 1.1 泛型类 Java 中使用 <> 符号来包含定义的类型参数,Scala 则使用 []。 class Pair[T, S](val first: T, val second: S) { override def toString: String = first + ":" + second } object ScalaAp

  • 流接口中的映射函数定义如下: 函数参数列表与函数接口自己的规范一致 方法是 这意味着它需要一个T,但返回一个R。但我正在运行这个代码 奇怪的是,为什么它能工作,但论点类型似乎不匹配?没有简单的方法来解释它,但我的理解是,一个函数应该接受类型T的输入并返回类型R。当它在流中使用时,它接受类型String的输入并返回类型int(即String.length())。但是小溪。map被键入以返回与流相同的