函数参数


# 函数参数

在 Python 中,函数的参数定义灵活度非常大,分为位置参数、默认参数、动态参数和关键字参数,这些都是形参的种类。

# 位置参数

也叫必传参数,顺序参数,是最重要的,也是必须在调用函数时明确提供的参数。位置参数必须按先后顺序,一一对应,个数不多不少的传递。

代码示例:

def add(a, b, c):
    return a + b + c

result = add(1, 2, 3)
1
2
3
4

注意: Python 在做函数参数传递的时候不会对数据类型进行检查,理论上你传什么类型都可以,但上面的 add 函数,如果你传递了一个字符串和两个数字,结果是弹出异常,因为字符串无法和数字相加。这就是 Python 的弱数据类型和动态语言的特点。在简单、方便的时候,需要你自己去实现数据类型检查。

# 默认参数

在函数定义时,如果给某个参数提供一个默认值,这个参数就变成了默认参数,不再是位置参数了。在调用函数的时候,我们可以给默认参数传递一个自定义的值,也可以使用默认值。

代码示例:

def power(x, n = 2):
    return x**n

ret1 = power(10)   # 使用默认的参数值 n = 2
ret2 = power(10, 4)  # 将 4 传给 n,实际计算 10**4 的值
1
2
3
4
5

在设置默认参数时,有几点要注意:

  • 默认参数必须在位置参数后面。

  • 当有多个默认参数的时候,通常将更常用的放在前面,变化较少的放后面。

  • 在调用函数的时候,尽量给实际参数提供默认参数名。

    • 一切没有提供参数名的实际参数,都会当做位置参数按顺序从参数列表的左边开头往右匹配。
  • 如果在位置参数传递时,给实参指定位置参数的参数名,那么位置参数也可以不按顺序调用。

  • 默认参数尽量指向不变的对象

    • 这个很重要,看如下代码,并分析下打印结果:
    def func(a=[]):
        a.append("A")
        return a
    
    print(func())
    print(func())
    print(func())
    
    1
    2
    3
    4
    5
    6
    7

    分析:Python 函数体在被读入内存的时候,默认参数 a 指向的空列表对象就会被创建,并放在内存里了。因为默认参数 a 本身也是一个变量,保存了指向对象 [] 的地址。每次调用该函数,往 a 指向的列表里添加一个 Aa 没有变,始终保存的是指向列表的地址,变的是列表内的数据。

    为了避免这个问题,需要使用不可变的数据类型作为默认值:

    def func(a=None):
        # 注意下面的if语句
        if a is None:
            a = []
        a.append("A")
        return a
    
    print(func())
    print(func())
    print(func())
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    将默认参数 a 设置为一个类似 None、数字或字符串之类的不可变对象。在函数内部,将它转换为可变的类型,比如空列表。这样一来,不管调用多少次,运行结果都是 ['A'] 了。

# 动态参数

顾名思义,动态参数就是传入的参数的个数是动态的,可以是 1 个、2 个到任意个,还可以是 0 个。

Python 的动态参数有两种,分别是 *args**kwargs,这里面的关键是一个和两个星号的区别,而不是 args 和 kwargs 在名字上的区别,实际上可以使用任何名字,但这似乎是大家默认的规范。

注意:动态参数,必须放在所有的位置参数和默认参数后面。

def func(name, age, sex='male', *args, **kwargs):
    pass
1
2

# *args

一个星号表示接收任意个参数。调用时,会将实际参数打包成一个元组传入形式参数。如果参数是个列表,会将整个列表当做一个参数传入。例如:

def func(*args):
    for arg in args:
        print(arg)

func('a', 'b', 'c')
func([1, 2, 3])
1
2
3
4
5
6

# **kwargs

两个星表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典。例如:

def func(**kwargs):
    print(kwargs)
    for kwg in kwargs:
        print(kwg, kwargs[kwg])

func(k1='v1', k2=[0, 1, 2])
1
2
3
4
5
6

# 万能参数

*args**kwargs 组合起来使用,理论上能接受任何形式和任意数量的参数,在很多代码中我们都能见到这种定义方式。需要注意的是,*args 必须出现在 **kwargs 之前。

# 关键字参数

关键字参数需要一个特殊分隔符 *(独占一个参数位),* 后面的参数被视为关键字参数。

在函数调用时,关键字参数必须传入参数名,否则调用将报错。同时,关键字参数没有默认值时必须传递,有默认值时可以不传递。

代码示例:

# 没有默认值时必须传递关键字参数
def student(name, age, *, sex):
    pass

student(name="jack", age=18, sex='male')

# 有默认值时可以不传递关键字参数
def student(name, age, *, sex='male'):
    pass

student(name="jack", age=18)
1
2
3
4
5
6
7
8
9
10
11

Python 的函数参数种类多样、形态多变,既可以实现简单的调用,又可以传入非常复杂的参数。需要我们多下功夫,多写实际代码,多做测试,逐步理清并熟练地使用参数。

(完)