Skip to content

函数的参数

形参和实参

形式参数与实际参数

函数的声明可以包含一个 [形参列表],而函数调用则通过传递 [实参列表] ,以允许函数体中的代码引用这些参数变量。声明函数时所声明的参数即为形式参数,简称形参;调用函数时提供函数所需要的参数的值即为实际参数,简称实参。实际参数值默认按位置顺序依次传递给形式参数。如果参数个数不对,将会产生错误。

案例:形式参数和实际参数示例

python
def my_max1(a, b):
    if a > b:
        return f'{a} > {b}'
    elif a == b:
        return f'{a} == {b}'
    else:
        return f'{a} < {b}'


x = 4
y = 5
print(my_max1(x, y))  # 4.5
print(my_max1(5, 5))  # 5.0
print(my_max1(5))  # 报错

声明函数时声明的形式参数等同于函数体中的局部变量,在函数体中的任何位置都可以使用。

局部变量和形式参数变量的区别在于局部变量在函数体中绑定到某个对象,而形式参数变量则绑定到函数调用代码传递的对应实际参数对象。Python参数传递方法是传递对象引用,而不是传递对象的值。

传递不可变对象

在调用函数时,若传递的是不可变对象(例如 intfloatstrbool 对象)的引用,则如果函数体中修改对象的值,其结果实际上是创建了一个新的对象。

案例:传递不可变对象的引用示例:错误的递增函数。

python
num = 100


def increment(arg_num, arg_n):
    arg_num += arg_n


increment(num, 10)
print(num)

在本例中,num 的初值为 100,当调用函数 increment(num,10) 后,在函数体内执行了 "arg_num+=10" 语句,函数体内的 arg_num 变成了 110 。但是,当函数调用完毕返回主程序时 num 的值仍然为 100,因为整数 num 是不可变对象,而在 Python 语言中一个函数不能改变一个不可变对象(例如整数、浮点数、布尔值或字符串)的值(即函数无法产生副作用)。

案例:传递不可变对象的引用示例:正确的递增函数

python
num = 100
arr = [num]


def inc(arg_arr, arg_n):
    arg_arr[0] += arg_n


increment(arr, 10)
print(arr)

在本例中,arr 的初值为 [100],当使用表达式 increment(arr, 10) 调用函数 increment(arr, 10) 后,在函数体内执行了 arg_arr[0] += arg_n 语句,函数体内的i变成了 110,并且函数返回了 110。当函数调用完毕返回主程序时 arr 被赋值为 [110]

传递可变对象

在调用函数时,如果传递的是可变对象(例如 list 对象)的引用,则在函数体中可以直接修改对象的值。

案例:定义一个可以交换给定列表中两个指定下标的元素值的函数。

python
def exchange(arr, i, j):
    temp = arr[i]
    arr[i] = arr[j]
    arr[j] = temp

函数的参数

位置参数

Python 处理参数的方式要比其他语言更加灵活。其中,最熟悉的参数类型是位置参数,在函数调用时,实参默认按位置顺序传递形参。下面创建一个带有位置参数的函数:

案例:y = k * x +bkb 也不固定。

python
def f(x, k, b):
    y = k * x + b
    print(y)


f(5, 5, 6)

尽管这种方式很常见,但是位置参数的一个弊端是必须熟记每个位置的参数的含义。在调用函数 f() 时误把最后一个参数当作第一个参数,会得到完全不同的结果:

python
def f(x, k, b):
    print("x:{} k:{} b:{}".format(x, k, b))
    y = k * x + b
    return (y)


f(5, 5, 6)

关键字参数

在函数调用时,也可以通过名称(关键字)指定传入的参数,例如 my_max1(a=1,b=2) 或者 my_max1(b=2,a=1)

为了避免位置参数带来的混乱,通过“键=值”形式加以指定。调用参数时可以指定对应参数的名字,甚至可以采用与函数定义不同的顺序调用:

python
def f(x, k, b):
    print("x:{} k:{} b:{}".format(x, k, b))
    y = k * x + b
    return (y)


f(x=5, k=5, b=6)

你也可以把位置参数和关键字参数混合起来。首先,实例化参数 ,然后对参数使用关键字参数的方式:

python
def f(x, k, b):
    print("x:{} k:{} b:{}".format(x, k, b))
    y = k * x + b
    return (y)


f(5, k=5, b=6)

如果同时出现两种参数形式,首先应该考虑的是位置参数。

默认参数

当调用方没有提供对应的参数值时,你可以指定默认参数值。这个听起来很普通的特性实际上特别有用,以之前的例子为例:

python
def f(x, k=5, b=6):
    print("x:{} k:{} b:{}".format(x, k, b))
    y = k * x + b
    return y


f(5)
f(x=5)
f(x=5, k=5)
f(x=5, k=5, b=6)

案例:基于期中成绩(40%)和期末成绩(60%),按照指定的权重计算总评成绩。

参考答案
python
# 期中成绩,期末成绩,期中成绩权重
def my_sum(mid_score, end_score, mid_rate=0.4):
    score = mid_score * mid_rate + end_score * (1 - mid_rate)
    return f'{score:.2f}'


print(my_sum(88, 79))  # 期中成绩默认为 40%
print(my_sum(88, 79, 0.3))  # 将期中成绩权重设置为 50%

print(my_sum(mid_score=88, end_score=79))
print(my_sum(end_score=79, mid_score=88))

*args

收集位置参数(了解)

在不清楚传入参数是多少个

python
# 用 * 收集位置参数
# int * 特殊符号 有特殊的作用, 在定义函数的括号里面,用于收集所有的位置参数
# 在输出 或者是运行代码的过程中 是解包 包:元组、列表、迭代器、生成器
def print_args(*args):
    # * 解包的标志
    print('位置参数的类型:', type(args))
    print('位置参数的内容:', args)

无参数调用函数,则什么也不会返回:

python
>>> def print_args(*args):
...     # * 解包的标志
...     print('位置参数的类型:', type(args))
...     print('位置参数的内容:', args)
...
>>> print_args()
位置参数的类型: <class 'tuple'>
位置参数的内容: ()

给函数传入的所有参数都会以元组的形式返回输出:

python
>>> print_args(3, 2, 1, 'wait!', 'uh...')
位置参数的类型: <class 'tuple'>
位置参数的内容: (3, 2, 1, 'wait!', 'uh...')

这样的技巧对于编写像 print() 一样接受可变数量的参数的函数是非常有用的。如果你函数同时有限定的位置参数,那么 *args 会收集剩下的参数:

python
>>> def print_args1(arg1, arg2, *args):
...     print('arg1:', arg1)
...     print('arg2:', arg2)
...     print('args:', args)
...
>>> print_args1(1,2,3,4,5,6)
arg1: 1
arg2: 2
args: (3, 4, 5, 6)

当使用 * 时不需要调用元组参数 args,不过这也是 Python 的一个常见做法。

案例: 编写函数,求若干数中的最大值。

求若干数中最大值的方法一般如下。

(1)将最大值的初值设为一个比较小的数,或者取第一个数为最大值的初值。

(2)利用循环将每个数与最大值比较,若此数大于最大值,则将此数设置为最大值。

参考答案
python
def my_max(a, b, *c):
    max_value = a
    if max_value < b:
        max_value = b
    for n in c:
        if max_value < n:
            max_value = n
    return max_value


print(my_max(1, 2))
print(my_max(1, 2, 11, 2, 5))

**kwargs

收集关键字参数

使用两个星号可以将参数收集到一个字典中,参数的名字是字典的键,对应参数的值是字典的值。下面的例子定义了函数 print_kwargs() ,然后打印输出它的关键字参数:

python
def print_kwargs(*arg, **kwargs):
    """ args为关键字元组  kwargs为双元关键字元组 """
    print('位置参数:', arg)
    print('关键字参数:', kwargs)

案例:

案例:假设 python 中的 print 不能一次性传入多个参数使用了,让我们自己实现 print 可以传递多个参数的功能。

python
# 假设 print 函数突然变成了一下这个样子
def print_(arg):
    print(arg, sep="", end="")

要求:自己编写一个 changed_print 函数实现原本 print 的功能。

例:原本函数的功能

python
>>> print(1)
1
>>> print(1, 2, 3, 4)
1 2 3 4
>>> print(1, 2, 3, 4, sep=',')
1,2,3,4
>>> print(1, 2, 3, 4, sep=',', end='我是结尾')
1,2,3,4我是结尾>>>

答案

python
def changed_print(*args, sep=' ', end='\n'):
    # print_ 只能接受一个参数
    for arg in args[:-1]:
        print_1(arg)
        # , 空格 #
        print_1(sep)

    print(args[-1])
    print_1('\n')

一等公民函数

在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

在 Python 中,整数、字符串和字典都是一等对象——没什么特别的。接下来的内容将重点讨论把函数作为对象的影响和实际应用。

比如说我要将之前函数修改一个名字,使用新的函数名去调用旧的函数

python
def f(x):
    y = 5 * x + 6
    # return 关键字 返回一个内容
    return y


d = f

print(d(x) + d(y))

把函数当参数传递

案例需求 f(1) + f(2) + f(3) + f(4) + f(5)

python
print(f(1) + f(2) + f(3) + f(4) + f(5))

# 把函数当参数传递
print(f(f(1)))