浅析 Python 的参数

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

位置参数

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
    def power(x):
return x * x
```

对于power(x)函数,参数x就是一个位置参数。

当我们调用power函数时,必须传入有且仅有的一个参数x:

```python
>>> power(5)
25
>>> power(15)
225

默认参数

1
2
3
4
5
6
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s

可以在定义函数时,默认给上一个参数 n=2

使用默认参数能够最大的好处是能降低调用函数的难度。

默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下:

1
2
3
4
5
6
7
8
9
10
def add_end(L=[]):

L.append("END")
return L

print(add_end([1,2,3]))
print(add_end(['x','y','z']))
print(add_end())
print(add_end())
print(add_end())

会输出

1
2
3
4
5
[1, 2, 3, 'END']
['x', 'y', 'z', 'END']
['END']
['END', 'END']
['END', 'END', 'END']

我们可以看到,第一次和第二次输出都没有问题,因为我们传了参数进去,第三次我们输入,会返回默认的 ‘END’,但是第四次和第五次就有问题了。

原因解释如下:

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

我们要修改下该函数

1
2
3
4
5
def add_end(L=None):
if L is None:
L =[]
L.append("END")
return L

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

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

可变参数

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

要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来,这样,函数可以定义如下:

1
2
3
4
5
def calc(num):
sum = 0
for n in num:
sum = sum +n * n
return sum

但是调用的时候,需要先组装出一个list或tuple:

1
2
3
print(calc([1,2,3]))

print(calc([1,3,5,7]))

改为可变参数.

1
2
3
4
5
6
7
8
def calc(*num):
sum = 0
for n in num:
sum = sum +n * n
return sum


print(calc(1,2,3))

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数

那如果要传入一个 list 或 tuple 呢

1
2
3
4
5
6
7
8
9
def calc(*num):
sum = 0
for n in num:
sum = sum +n * n
return sum

nums = [1,2,3]

print(calc(*nums))

*nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    def person(name,age,**kw):

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


person('Michael',30)
person('Michael',30,city='Beijing')

```


输出

```python
name: Michael age: 30 other: {}
name: Michael age: 30 other: {'city': 'Beijing'}
```

关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。


传入一个字典

```python
extra = {'city':'Beijing','Job':'Engineer'}

person('zhangsan',30,**extra)
```


# 命名关键字参数

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

如需限制传入参数:

```python
def person(name,age,*,city,job):
print(name,age,city,job)


person('zhangsan',30,city='hangzhou',job='Techs')

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    def person(name,age,*,city='beijing',job):
print(name,age,city,job)

person('zhangsan',30,job='Techs')
```

# 参数组合

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

```python

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)


print(f1(1,2))
print(f1(1,2,c=3))
print(f1(1,2,3,'a','b'))
print(f1(1,2,3,'a','b','c',d=11))

```

输出

```python
a= 1 b= 2 c= 0 args= () kw= {}
None
a= 1 b= 2 c= 3 args= () kw= {}
None
a= 1 b= 2 c= 3 args= ('a', 'b') kw= {}
None
a= 1 b= 2 c= 3 args= ('a', 'b', 'c') kw= {'d': 11}
None

传入tuple 和 dict

args = (1,2,3,4,5)
kw = {'d':99,'x':44}

print(f1(*args,**kw))

*args是可变参数,args接收的是一个tuple;

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

学习笔记来自廖雪峰的 python