加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

深入解析Python中函数的参数与作用域

发布时间:2020-05-24 00:40:04 所属栏目:Python 来源:互联网
导读:传递参数函数传递参数时的一些简要的关键点:参数的传递是通过自动将对象赋值给本地变量名来实现的。所有的参数实际上都是通过指针进行传递的,作为参数被传递的对象从来不自动拷贝。

传递参数

函数传递参数时的一些简要的关键点:

  • 参数的传递是通过自动将对象赋值给本地变量名来实现的。所有的参数实际上都是通过指针进行传递的,作为参数被传递的对象从来不自动拷贝。
  • 在函数内部的参数名的赋值不会影响调用者。
  • 改变函数的可变对象参数的值会对调用者有影响。

实际上,Python的参数传递模型和C语言的相当相似:

不可变参数”通过值”进行传递。像整数和字符串这样的对象是通过对象引用而不是拷贝进行的,但是因为不论怎么样都不可能在原处改变不可变对象,实际的效果就很像创建了一份拷贝。
可变对象是通过”指针”进行传递的。这就意味着,可变对象能够在函数内部进行原处修改。
>>避免可变参数的修改
避免参数的修改有很多种方式:

传递参数时,传递一个拷贝:

L = [1,2]
changer(L[:])

函数内部进行拷贝

def changer(b):
 b=b[:]

将可变对象转化为不可变对象

L=[1,2]
changer(tuple(L))

>>对参数输出进行模拟
对于参数的返回值有一个小技巧:因为return能够返回任意种类的对象,如果这些值封装进一个元组或其他的集合类型,那么它也能够返回多个值。

def multiple(x,y):
 x = 2
 y = [2,4]
 return x,y #Return new values in a tuple

这段代码貌似返回了两个值,其实只有一个:一个包含了2个元素的元组,它的括号是可以省略的。

特定的参数匹配模型

>>基础知识
匹配模型的大纲:

  • 位置:从左至右进行匹配。
  • 关键字参数:通过参数名进行匹配。(调用者可以定义哪一个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。)
  • 默认参数:为没有传入值的参数定义参数值。
  • 可变参数:搜集任意多基于位置或关键字的参数。
  • 可变参数解包:传递任意多的基于位置或关键字的参数。
  • Keyword-only参数:参数必须按照名称传递。(只存在于Python3.0中)

>>匹配语法

相应的说明:

在函数的调用中(表中的前4行),简单的通过变量名位置进行匹配,但是使用name=value的形式告诉Python依照变量名进行匹配,这些叫做关键字参数。在调用中使用*sequence或**dict允许我们在一个序列或字典中相应地封装任意多的位置相关或者关键字的对象,并且在将他们传递给函数的时候,将它们解包为分开的、单个的参数。
在函数的头部,一个简单的变量名时通过位置或变量名进行匹配的(取决于调用者是如何传递给它参数的),但是name=value的形式定义了默认的参数值。*name的形式收集了任意的额外不匹配的参数到元组中,并且**name的形式将会手机额外的关键字参数到字典中。在Python3.0及其以后的版本中,跟在*name或一个单独的*之后的、任何正式的或默认的参数名称,都是keyword-only参数,并且必须在调用时按照关键字传递。
>>细节
在使用混合的参数模型的时候,Python将会遵循下面有关顺序的法则。

在函数调用中,参数必须以此顺序出现:任何位置参数(value),后面跟着任何关键字参数(name=value)和*sequence形式的组合,后面跟着**dict形式。
在函数头部,参数必须以此顺序出现:任何一般参数(name),紧跟着任何默认参数(name=value),后面是name(在Python3.0中是)形式,后面跟着任何name或name=value keyword-only参数(Python3.0中),后面跟着**name形式。
在调用和函数头部中,如果出现**arg形式的话,都必须出现在最后。

Python内部是使用以下的步骤来在赋值前进行参数匹配的:

  • 通过位置分配非关键字参数。
  • 通过匹配变量名分配关键字参数。
  • 其他额外的非关键字分配到*name元组中。
  • 其他额外的关键字参数分配到**name字典中。
  • 用默认值分配给在头部未得到分配的参数。
  • 在这之后,Python会进行检测,确保每个参数只传入了一个值。如果不是这样的话,将会发生错误。当所有匹配都完成了,Python把传递给参数名的对象赋值给它们。

>>关键字参数和默认参数的实例
如果没有使用任何特殊的匹配语法,Python默认会通过位置从左至右匹配变量名。

def f(a,b,c):
 print(a,c)

f(1,2,3)   #Prints 1,3

关键字参数

关键字参数允许通过变量名进行匹配,而不是通过位置。

f(c=3,b=2,a=1) #Prints 1,3

默认参数

默认参数允许创建函数可选的参数。如果没有传入值的话,在函数运行前,参数就被赋了默认值。

def f(a,c=3):
 print(a,c)

f(1)    #Prints 1,3
f(1,4)   #Prints 1,4,c=6)   #Prints 1,6

关键字参数和默认参数的混合

def func(spam,eggs,totast=0,ham=0):
 print((spam,ham=0))
func(1,2)     #Ouput:(1,0)
func(1,ham=1,eggs=0)  #Ouput:(1,1)
func(spam=1,eggs=0)   #Ouput:(1,0)
func(toast=1,eggs=2,spam=3) #Ouput:(3,1,3,4)    #Ouput:(1,4)

>>任意参数的实例
最后两种匹配扩展,*和**,是让函数支持接收任意数目的参数的。

收集参数

在函数定义中,在元组中收集不匹配的位置参数。

def f(*args):print(args)

当这个函数调用时,Python将所有位置相关的参数收集到一个新的元组中,并将这个元组赋值给变量args。因此它是一个一般的元组对象,能够进行索引或迭代。

**特性类似,但是它只对关键字参数有效。将这些关键字参数传递给一个新的字典,这个字典之后将能够通过一般的字典工具进行处理。在这种情况下,**允许将关键字参数转化为字典,你能够在之后使用键调用进行步进或字典迭代。

def f(a,*pargs,**kargs):print(a,pargs,kargs)

f(1,x=1,y=2)  #Prints:1 (2,3) {'x':2,'y':1}

解包参数

在最新的Python版本中,我们在调用函数时能够使用*语法。在这种情况下,它与函数定义的意思相反。它会解包参数的集合,而不是创建参数的集合。

def func(a,c,d):print(a,d)
args=(1,2)
args+=(3,4)
func(*args)   #Prints 1,4

相似的,在函数调用时,**会以键/值对的形式解包一个字典,使其成为独立的关键字参数。

args={'a':1,'b':2,'c':3}
args['d']=4
func(**args)   #Prints 1,4

注意:别混淆函数头部和函数调用时*/**的语法:在头部,它意味着收集任意多的参数,而在调用时,它解包任意数量的参数。

应用函数通用性

if <test>:
 action,args=func1,(1,)
else:
 action,args=func2,3)
...

action(*args)

>>Python3.0 Keyword-Only参数
Python3.0把函数头部的排序规则通用化了,允许我们指定keyword-only参数――即必须只按照关键字传递并且不会由一个位置参数来填充的参数。

从语法上讲,keyword-only参数编码为命名的参数,出现在参数列表中的*args之后。所有这些参数都必须在调用中使用关键字语法来传递。

我们也可以在参数列表中使用一个*字符,来表示一个函数不会接受一个变量长度的参数列表,而是仍然期待跟在*后面的所有参数都作为关键字传递。

def kwonly(a,*,c)
kwonly(1,c=3,b=2) #Prints:1,3
kwonly(c=3,a=1) #Prints:1,3
kwonly(1,3)  #Error!

上述代码中,b和c必须按照关键字传递,不允许其他额外的位置传递。

另外,默认函数仍然对keyword-only参数有效,所以,实际上,带有默认值的keyword-only参数都是可选的,但是,那些没有默认值的keyword-only参数真正地变成了函数必需的keyword-only参数。

排序规则 最后,注意keyword-only参数必须在一个单个星号后指定,而不是两个星号――命名的参数不能出现在**args任意关键字形式的后面,并且一个**不能独自出现在参数列表中。这两种做法将产生错误。

def kwonly(a,**pargs,c)  #Error!
def kwonly(a,**,c)   #Error!

这就意味着,在一个函数的头部,keyword-only参数必须编写在**args任意关键字形式之前,且在*args任意位置形式之后。

实际上,在函数调用中,类似的排序规则也是成立的:当传递keyword-only参数的时候,它们必须出现在一个**args形式之前。keyword-only参数可以编写在*arg之前或者之后,并且可能包含在**args中:

def f(a,*b,c=6,**d):print(a,d)

f(1,*(2,3),**dict(x=4,y=5))  #Prints:1 (2,3) 6 {'x':4,'y':5}
f(1,y=5),c=7) #Error!
f(1,c=7,y=5)) #Prints:1 (2,3) 7 {'x':4,y=5,c=7)) #Prints:1 (2,'y':5}

Python作用域

在一个Python程序只用变量名时,Python创建、改变或查找变量名都是在所谓的命名空间(一个保存变量名的地方)中进行的。也就是说,在代码中变量名被赋值的位置决定了这个变量名能被访问到的范围,也即决定了它存在于哪个命名空间中。

除了打包程序之外,函数还为程序增加了一个额外的命名空间层:默认情况下,一个函数所有变量名都是与函数的命名空间相关联的。这意味着:

一个在def内的定义的变量能够在def内的代码使用,不能在函数的外部应用这样的变量名。
def之中的变量名与def之外的变量名并不冲突,一个在def之外被赋值的变量X与在这个def之中赋值的变量X是完全不同的变量。
>>作用域法则
在开始编写函数之前,我们编写的所有代码都是位于一个模块的顶层(也就是说,并不是嵌套在def之中),所以我们使用的变量名要么是存在于模块文件本身,要么就是Python内置预先定义好的。函数定义本地作用域,而模块定义的全局作用域。这两个作用域有如下关系:

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读