# 可变参数

## args

如果想让一个函数能接受任意个参数，我们就可以定义一个可变参数：

```
def fn(*args):
    print args
```

可变参数的名字前面有个`*`号，我们可以传入0个、1个或多个参数给可变参数。

```
>>> fn()
()
>>> fn(1)
(1,)
>>> fn(1, 2)
(1, 2)
>>> fn((1, 2), "one")
((1, 2), "one")
```

可变参数也不是很神秘，Python解释器会把传入的实参组装成一个`tuple`传递给`args`。因此，在函数内部，直接把变量`args`看成一个`tuple` 就好了。实参可以是任意类型，比如上面`fn((1, 2), "one")`中，`(1, 2)`本身就是一个`tuple`类型。

当然，我们也可以把`args`当作实参继续往下传，比如：

```
def callee(one, two):
    print one, two

def fn(*args):
    callee(*args)

caller(1, 2)
```

上面中，`callee(*args)`这行代码，Python会把`*args`拆成两个实参传给`callee`函数。需要注意的是，我们在调用`fn`函数时只能传两个参数，如果传三个参数`fn(1, 2, 3)`则会报错：

```
TypeError: callee() takes exactly 2 arguments (3 given)
```

这里我们可能会想到，既然`args`是一个`tuple`，那么下面的代码是否可以正常运行呢？答案是：可以正常运行，输出为`3`

```
def test(a, b):
    return a + b

tup = (1, 2)
print test(*tup)
```

## kwargs

在上面中，函数`def fn(*args)`的定义中，`args`形参前面只有一个`*`号，此时在函数`fn`中就可以把`args`当成是一个`tuple`。我们也可以定义如下一个函数：

```
def fn(**kwagrs):
    print kwargs
```

该函数的形参带有两个`*`号，在函数内部，`kwargs`就是一个`dictionary`类型。如下：

```
>>> fn()
{}
>>> fn(a=1)
{"a": 1}
>>> fn(a=1, b:"2")
{"a": 1, "b": "2"}
>>> fn(a=1, b:("one", "two"))
{"a": 1，"b": ("one", "two")}
```

但是在传实参时，我们需要注意，实参必须为`key=value`，且key必须为变量的命名规则（字母、数字、下划线），不能加引号，而且`=`不能换成`:`，value可以为任意类型的值。

当然，我们也可以把`kwargs`当作实参继续往下传。如果直接把`kwargs`当作实参传，那么它就是一个`dictionary`；但是我们还可以这样子用，把`**kwargs`当作实参传给形参带有默认值的函数：

```
def callee(a=0, b="one"):
    print a, b

def fn(**kwargs):
    callee(**kwargs)
```

```
>>> fn()
0 one
>>> fn(a=1)
1 one
>>> fn(a=1, b="two")
1 two
```

这里我们可有会想到，那我自已定义一个`dictionary`，然后把`**dictionary`当作实参传给形参带有默认值的函数是否可以，答案是可以的，如下：

```
def callee(a=0, b="one"):
    print a, b

dict = {"a": 1, "b": "two"}
callee(**dict)
```

代码的输出为`1 two`。

**但是要注意的是，`dict`的key一定要与函数`callee`的形参名称对应起来**

比如下面的代码是会报错的，因为key`c`与形参`b`名称不一样

```
def callee(a=0, b="one"):
    print a, b

dict = {"a": 1, "c": "two"}
callee(**dict)
```

## 混合使用

当我们定义一个函数同时拥有形参`*args`与`**kwargs`时，`*args`必须放在前面。如下：

```
def fn(*args, **kwargs)
    print args, kwargs
```

而当把`args`与`kwargs`当作实参时，`args`也必须放在前面，这是因为，带有默认值的形参必须放在函数形参列表的最后面。如下：

```
def callee(a, b="zero"):
    print a, b

def fn(*args, **kwargs):
    callee(*args, **kwargs)
```

```
>>> fn(1, b="one")
1 one
```

## Reference

* <https://www.programiz.com/python-programming/args-and-kwargs>
