zoukankan      html  css  js  c++  java
  • python中函数参数问题小究

    python3中函数参数有位置参数、命名关键字参数、默认参数、非关键字可变长参数、关键字可变长参数几种。

    一、位置参数

    位置参数是函数的标准参数,是最常用的一种参数格式。定义函数时,由于每个参数都有自己的位置,如果不加以特殊说明,调用函数时,函数就会根据所赋参数的位置来给函数内参数赋值。

    def s(x,y):
        print('x=',x)
        print('y=',y)
    
    a,b=1,0
    s(a,b)
    
    运行结果:
    x= 1
    y= 0

    调用时,函数会自动将处在x位置的a参数赋给x,把参数b赋给y。

    位置参数是必选参数,调用函数时,位置参数未赋值,函数将报错。

    二、命名关键字参数

    格式:
    def s(x,y,*,z,k)
      print('x=',x)
      print('y=',y)
      print('z=',z)
      print('k=',k)
    a,b,c,d=4,3,2,1
    s(a,b,k=c,z=d)

    输出
    x= 4
    y= 3
    z= 1
    k= 2

    此时,位置参数必须在最前,关键字参数在后,关键字参数之间可以不按顺序。

    定义命名关键字参数时,需要使用*分割符,*后面的参数都是命名关键字参数。命名关键字参数在使用时,必须使用‘形参=’这种格式,不然将会报错

    三、默认参数

    默认参数是指在定义函数时,赋给该参数一个值。调用该函数时,如果未给该参数赋值,则函数会自动将定义时的值拿来使用。

    
    
    def s(x,k='233'):
    print('x=',x)
    print('k=',k)

    a,b,c,d=4,3
    s(a)
    s(a,b)
    s(a,k=c)
    输出:
    x= 4
    k= 233
    x= 4
    k= 3
    x= 4
    k= 4
    可以看到,如果省略了k,则默认为是233,如果给k赋值,则以赋值为准。给k赋值时,可以采用位置参数的方式(不加想形参名字),也可以采取关键字参数的方式。
    默认参数在定义时的位置必须在位置参数之后。调用的时候,也必须在位置参数之后,否则会报错。这是因为函数在给参数赋值的时候,会优先将值赋给位置参数,剩下如果有多余的值会再赋给默认参数,如果没有就采用默认值。
     

     注意,定义默认参数一定是不可变参数。如果是可变参数,可能会出现一些错误。参考廖雪峰老师微博:

    先定义一个函数,传入一个list,添加一个END再返回:

    def add_end(L=[]):
        L.append('END')
        return L
    当你正常调用时,结果似乎不错:
    >>> add_end([1, 2, 3])
    [1, 2, 3, 'END']
    >>> add_end(['x', 'y', 'z'])
    ['x', 'y', 'z', 'END']
    

    当你使用默认参数调用时,一开始结果也是对的:

    >>> add_end()
    ['END']
    

    但是,再次调用add_end()时,结果就不对了:

    >>> add_end()
    ['END', 'END']
    >>> add_end()
    ['END', 'END', 'END']
    

    很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。

    原因解释如下:

    Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

     定义默认参数要牢记一点:默认参数必须指向不变对象!

    要修改上面的例子,我们可以用None这个不变对象来实现:

    def add_end(L=None):
        if L is None:
            L = []
        L.append('END')
        return L
    

    现在,无论调用多少次,都不会有问题:

    >>> add_end()
    ['END']
    >>> add_end()
    ['END']
    

    为什么要设计strNone这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。

     
    如果函数中既有命名关键字参数,又有默认参数,两个参数必须都在位置参数之后。如果赋值时,默认参数赋值不使用‘k=’这样的格式,则必须严格按照定义函数时的位置来传递参数。如果使用‘k=’这种格式,在这两只参数在传递时位置没有特殊的要求。
    但是在定义函数时,如果将默认参数放在命名关键参数之后,则表明这个参数既是默认参数,也是命名关键参数。
    def s(x,k=233,*,y,z,j=111):
        print('x=',x)
        print('y=',y)
        print('z=',z)
        print('k=',k)
        print('j=',j)
    
    a,b,c,d=4,3,2,1
    s(a,z=c,y=b,k=d)
    输出:
    x= 4
    y= 3
    z= 2
    k= 1
    j=111

    四、非关键字可变长参数

    非关键字可变长参数用 *变量名  来表示,定义函数时,一般用 *args 来表示。主要用来处理一些不能确定有多少个参数的问题。

    函数将多余的不带关键字的参数打包成为一个元组,进行处理。*args后面的参数必须使用关键字,不然全都会被当成可变长参数,被打包到元组中。

    但是,*args与默认参数之间顺序的问题也需要注意。

    
    
    def s(x,y=233,*args):
    print('x =',x)
    print('y =',y)
    for index,i in enumerate(args):
    print('args[%s] = %s'%(index,i))
    print(type(args))

    s(1,2,3,4,5)

    输出:

    x = 1
    y = 2
    args[0] = 3
    args[1] = 4
    args[2] = 5
    <class 'tuple'>



    可以看到,该函数第一个值赋给位置参数,第二个值赋给默认参数,后面的值全都打包成为一个元组,通过for循环来显示。只要传递的参数传递的参数大于两个,就会自动给默认参数赋值,此时默认参数基本上失去了默认的意义。 

     也可以将默认参数放在*args的后面,但是此时给默认参数赋值时,需要加上关键字,不然就会被打包为元组,赋值给args。

    def s(x,*args,y=233):
        print('x =',x)
        print('y =',y)
        for index,i in enumerate(args):
            print('args[%s] = %s'%(index,i))
    
    s(1,2,3,4,5)
    输出:
    x = 1
    y = 233
    args[0] = 2
    args[1] = 3
    args[2] = 4
    args[3] = 5

    s(1,2,3,4,y=5) 输出: x = 1 y = 5 args[0] = 2 args[1] = 3 args[2] = 4

    列表或元组可以以作为参数传入*args,如果不想被二次打包,需要在列表或者元组前面加 * 。

    def s(x,*args,y=233):
        print('x =',x)
        print('y =',y)
        for index,i in enumerate(args):
            print('args[%s] = %s'%(index,i))
    
    s(1,2,*[4,5,6])
    
    输出:
    x = 1
    y = 233
    args[0] = 2
    args[1] = 4
    args[2] = 5
    args[3] = 6

    五、可变长的关键字参数

    可变长关键字参数用 **变量名  来表示,定义函数时,一般用 **kwargs  来表示。主要用来处理一些不能确定有多少个关键字参数的问题。

    函数将多余的带关键字的参数打包成为一个字典,关键字为字典的key,进行处理。**kwargs必须是放在函数形参的最后。

    def s(x,*args,y=233,**kwargs):
        print('x =',x)
        print('y =',y)
        for index,i in enumerate(args):
            print('args[%s] = %s'%(index,i))
        for i in kwargs:
            print(i,'   ',kwargs[i])
        print('kwargs type is ',type(kwargs))
    
    s(1,2,a=3,b=4)
    
    输出结果:
    x = 1
    y = 233
    args[0] = 2
    a     3
    b     4
    kwargs type is  <class 'dict'>

    同样,字典也可以作为参数传入**kwargs,需要在字典前加 **

    可以对比下列代码的执行结果:

    def s(x,*args,y=233,**kwargs):
        print('x =',x)
        print('y =',y)
        for index,i in enumerate(args):
            print('args[%s] = %s'%(index,i))
        for i in kwargs:
            print(i,'   ',kwargs[i])
    
    s(1,2,{'a':5})
    
    输出:
    x = 1
    y = 233
    args[0] = 2
    args[1] = {'a': 5}   #由于该字典不带关键字,会被当成一个非关键字参数打包入args。
    s(1,2,a={'a':5})
    输出:
    x
    = 1
    y = 233
    args[0]
    = 2 a {'a': 5} # 此处,该字典带了关键字,会将该字典连同关键字一起进行打包,变为一个嵌套的字典。

    s(
    1,2,**{'a':5})
    输出:
    x
    = 1
    y
    = 233
    args[0]
    = 2
    a
    5 #由于字典前加上** ,该字典直接被加入kwargs中
  • 相关阅读:
    linux 解压tgz 文件指令
    shell 脚本没有执行权限 报错 bash: ./myshell.sh: Permission denied
    linux 启动solr 报错 Your Max Processes Limit is currently 31202. It should be set to 65000 to avoid operational disruption.
    远程查询批量导入数据
    修改 MZTreeView 赋权节点父节点选中子节点自动选中的问题
    关于乱码的问题解决记录
    我的网站优化之路
    对设计及重构的一点反思
    我的五年岁月
    奔三的路上
  • 原文地址:https://www.cnblogs.com/ohahastudy/p/8097458.html
Copyright © 2011-2022 走看看