Python 2.3 函数的参数

函数的参数

定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者,只需知道如何传递正确的参数,以及函数将返回什么样的值就可以了。函数内部的复杂逻辑被封装起来,调用者无需了解。

Python函数的定义非常简单,但是灵活度非常大。除了正常定义的必选参数,还可以使用默认参数,可变参数和关键字参数。使得定义出来的函数接口不但能处理复杂的函数,还可以简化调用者的代码。

位置参数

我们先写一个计算x^2的函数:
def power(x):

    return x*x

对于power(x)函数,x就是一个位置参数。当我们调用power函数时,必须传入有且仅有一个参数x:
>>>power(12)

144

若将power(x)升级为可以计算n次方的函数该怎么办呢?

此时将power(x)升级为power(x,n)

def power(x,n):

    s =1

    while n >0:

       s = s*x

       n = n-1

    return s

>>>power(5,2)

25

修改后的power()函数有两个参数:x和n,这两个参数都是位置参数,调用函数时,传入的两个参数按位置顺序一次赋给参数x和n


默认参数

默认参数还是以上面的power(x,n)为例,假如我们需要经常计算2次方的函数,可以使用默认参数将power(x,n)改为:

def power(x,n =2):

    s =1

    while n >0:

       s = s*x

       n = n-1

    return s

这样,当计算2次方的时候,只需传入一个x参数就ok了。

>>>power(5)

25

>>>power(10)

100

从上面的例子中可以看出,默认参数可以简化函数调用。设置默认参数时需注意以下几点:
一是必选参数在前,默认参数在后。否则Python解释器会出错。

二是如何设置默认参数:
当函数有多个参数的时,把变化大的参数放在前,变化小的参数放在后面。变化小的参数可以作为默认参数。

使用默认参数的好处是可以降低调用函数的难度。

举个例子,我们做一个一年级小学生注册的函数,需要传入name和gender两个参数:
def enroll(name,gender):

    print('name:',name)

    print('gender:',gender)

这样调用enroll()函数只需传入两个参数

>>>enroll('Sarah','F')

name:Sarah

gender:F

如果继续传入年龄、城市等信息时,这样会使得调用函数的难度大大增加。我们可以使用默认参数:

def enroll(name,gender,age =6,city = 'BeiJing')

    print('name:',name)

    print('gender',gender)

    print('age:',age)

    print('city:'city)

>>>enroll('Tom','C')

name:Tom

gender:C

age:6

city:BeiJing

这样就简化了函数的调用。如果学生信息与默认参数不符,我们就可以直接输入参数:

>>>enroll('Jack','F',city ='TianJin')

name:Jack

gender:F

age:6

city:TianJin

默认参数很有用,但是使用不当也会出问题:
先定义一个函数,添加一个‘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()

['END','END']

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

要修改上边的例子,我们可以采用NONE这个不变参数来实现:
def add_end(L=None):

   if L is None:
        L =[]

   L.append('END')

    return L

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


可变参数

在Python函数中,还可以定义可变参数。顾名思义,可变参数就是传入的参数个数是可变的,可以是1个,2个,也可以是0个。

是用可变参数定义一个函数:

def calc(*numbers):
    s =0 

    for i in numbers:
        s = s + i*i

    return s

>>>calc(1,2,4)

21

如果已经有了一个list或者tuple,要调用一个可变参数怎么办?可以这样做:
>>>nums = [1,2,3]

>>>calc(*nums)

14

Python允许在list或者tuple前面加一个*,把list或者tuple的元素变成可变参数穿进去:
*nums表示把nums这个list中所有元素作为可变参数传进去,这种写法非常有用。

关键字参数

关键字参数允许你传入0个或者任意个含参数名的参数。这些关键字参数在函数内部自动组装成一个dict。

def person(name,age,**kw):

    print('name:',name,'age:',age,'other:',kw)

>>>person('Bob',20)

name:Bob age:20 other:{}

也可以传入人一个关键字参数:
>>>person('Bob',20,city = 'ShangHai')

name:Bob age:20 other:{'city':'ShangHai'}

关键字的作用是扩展函数的功能。比如在person()函数中,我们能保证接收到name,age参数,但是如果调用者想提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必选外,其他都是可选项,利用关键字参数就可以实现这个功能。

和可变参数类似,也可以先组装一个dict,然后把dict转换为关键字参数传入:
>>>extra ={'city':'BeiJing','job':'Engineer'}

>>>person('Jack','24',**extra)

name:Jack age:24 other:{'city':'BeiJing','job':'Engineer'}

**kw表示把extra这个dict的所有的key-value用关键字参数传入到**kw参数,kw将获得一个dict,注意,kw获得的dict只是extra的一份拷贝,对kw的更改不会影响到extra。


命名关键字参数

对于关键字参数,函数的调用者可以传输任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内通过kw检查。

仍以person()函数为例,我们希望检察是否有city和job参数:

def person(name,age,**kw):
    if 'city' in kw:
       pass

   if 'job' in kw:
        pass

print('name:',name,'age:',age,'other:',kw)

但是调用者让可以传入不受限制的关键字参数:

>>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接受city和job参数:
def person(name,age,*,city,job)

    print(name,age,city,job)

命名关键字需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。

命名关键字必须传入参数名,这和位置参数不同。如果没有传入参数名将会报错:

>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>TypeError: person() takes 2 positional arguments but 4 were given

由于调用时缺少参数city和job,Python解释器把这4个参数均视为位置参数,但person()函数仅接受2个位置参数。

命名关键字参数可以有缺省。从而简化调用:
def person(name,age,*,city ='BeiJing',job)

    print(name,age,city,job)

调用时可以不传入city参数,从而简化调用。

是用命名关键字参数时要特别注意,*不是参数,二是特殊分隔符。如果且少*,Python解释器将无法识别位置参数和明明关键字参数。


参数组合

在Python中定义函数,可以使用必选参数、默认参数、可变参数、关键字参数和命名关键字参数。这5中参数可以组合使用,除了可变参数无法和命名关键字参数组合。

注意:参数定义的顺序是:必选参数、默认参数、可变参数/命名关键字参数、关键字参数。

比如定义一个函数,包含上述若干种参数:
def f1 =(a,b,c=0,*args,**kw):

    print('a:',a,'b:',b,'c:',c,'args:',args,'kw:',kw)

def f2 =(a,b,c=0,*,d,**kw)

    print('a:'a,'b:',b,'c:'c,'d:'d,'kw:'kw)


在函数调用的时候,Pyhton解释器会自动按照参数位置和参数名把对应的参数传进去。

>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

最神奇的是可以通过一个tuple和dict调用上述函数:

>>> args = (1, 2, 3, 4)>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

所以,对于任意的函数,都可以通过类似fun(*args,**kw)的形式调用。无论它的参数是如何定义的。


小结:

Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。

默认参数一定使用不可变对象,如果是可变对象,程序运行起来会有逻辑错误。

要注意可变参数和关键字参数的语法:
*args是一个可变参数,args接收的是一个tuple

**kw是一个关键字参数,kw接受的是一个dict

以及调用时如何传入可变参数和关键字参数的语法:

可变参数可以直接传入:fun(1,2,3),又可以先组装list/tuple,再通过*args传入:fun(*(1,2,3))

关键字参数既可以直接传入:fun(a=1,b=2),又可以先组装dict,再通过**kw传入:fun(**{'a':1,'b':2})

是用*args和**kw是Python的习惯用法,当然也可以使用其他参数名,但最好使用习惯用法。

命名的关键字是为了限制调用者可以传入的参数名,同时可以提供默认值。

定义明明关键字参数不要忘记特殊分隔符*,否则定义的是位置参数。


你可能感兴趣的:(python,函数参数)