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中
  • 相关阅读:
    Girls and Boys
    Kindergarten
    codevs 2822 爱在心中
    Popular Cows
    QWQ
    2488 绿豆蛙的归宿(拓扑+dp)
    P1119 灾后重建
    Mr. Frog’s Game
    Basic Data Structure
    A strange lift
  • 原文地址:https://www.cnblogs.com/ohahastudy/p/8097458.html
Copyright © 2011-2022 走看看