Web编程技术营地
研究、演示、创新

函数

撰写时间:2025-09-08

修订时间:2025-09-19

定义与调用

def sum(a, b): ''' Function sum(a, b) returns the sum of a and b. Parameters: a -- the first operand b -- the second operand Return: the sum of two operands ''' return a + b print(sum(3, 5)) print(sum.__doc__)

使用docstring后,可打印特定函数的__doc__属性值以输出docstring

可通过注解 (annotation) 来标注函数参数类型及其返回值类型:

def sum(a: int, b: int) -> int: # specify param types and return type using annotation return a + b print(sum(3, 5))

嵌套函数

Python支持嵌套函数。

def func(): data = [] def prepare_data(): for i in range(5): data.append(i * 2) prepare_data() return data value = func() print(value)

参数与调用

为最大化地方便客户端调用函数,Python对函数参数的类型与顺序作出了众多规定。在本文中,我们将一一剖析相关术语及其内在原理机制。

术语约定

下面是一个函数定义及及其函数调用:

def sum(a, b): # parameters print(a + b) num1 = 3 num2 = 5 sum(num1, num2) # arguments

通常,在英文环境中,在函数定义时所出现的函数参数,称为parameters;而在函数调用时所指定的参数,称为arguments

本文将parameters统一称谓为形参,将arguments统一称谓为实参

上面代码,在函数定义中,参数ab均为形参;而在函数调用时,变量num1num2均为实参

参考资源

  1. C Functions

位置形参

位置形参 (positional arguments) 是指在声明函数时按形参在参数列表中出现的先后顺序来确定实参形参映射的机制。

在定义函数时,将各个形参的名称以逗号,隔开即可。这是最常见的声明方式。

规则1:在调用时,各个实参依序赋值于各个位置形参

def demo_func(a, b, c): print(f'The result is: a = {a}, b = {b}, c = {c}') demo_func(5, 10, 15)

规则2:每个位置形参都要求提供相应的实参不能省略

def demo_func(a, b, c): print(f'The result is: a = {a}, b = {b}, c = {c}') demo_func(5)

规则3:在调用时,实参可以通过赋值语句的方式,显式地列出各个位置形参的名称。这种特性,使得Python的函数调用细节非常清晰。应用规则3时,同样受规则2的约束。

def demo_func(name, age, gender): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func(name = 'Mike', age = 25, gender = 'male')

规则4实参使用赋值语句方式时,实参次序可以与形参次序不一致。但依旧受规则2的约束。

def demo_func(name, age, gender): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func(age = 25, name = 'Mike', gender = 'male')

关键字形参

基本规则

在定义函数时,以赋值语句的方式来声明的形参,称为关键字形参 (keyword arguments )。

关键字形参的主要目的是为每个关键字形参均提供一个默认的初始值,以允许客户端在调用时,可以无须显式地提供相应的实参

规则1:调用时,可像位置形参一样,依序提供各个实参的数值。

def demo_func(name = 'Tom', age = 10, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func('Joan', 18, 'female')

规则2:应用规则1时,可从任意位置的形参开始,省略后续所有实参

def demo_func(name = 'Tom', age = 10, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func('Smith')

规则3:每个关键字形参可以无须提供相应的实参

def demo_func(name = 'Tom', age = 10, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func()

规则4:以赋值语句形式提供实参时,无须考虑关键字形参的声明顺序。

def demo_func(name = 'Tom', age = 10, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func(age = 23)

位置形参与关键字形参的混同

规则1:在定义函数时,如果函数参数既有位置形参,又有关键字形参,所有的位置形参须出现在所有的关键字形参的前面。

def demo_func(name = 'Mike', age, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.")

规则2:在调用函数时,必须为每个位置形参显式地提供相应的实参。如果以赋值语句方式为位置形参提供实参,则实参的次序不受约束。

def demo_func(name, age = 15, gender = 'male'): print(f"I'm {name}. I'm {age} yeas old. My gender is {gender}.") demo_func(age = 23, name = 'Mike')

可变长形参

可变长形参 (variadic arguments) 是可以接收任意数量的实参形参

基本用法

下面函数,不管客户端传入多少个数值,均能自动求和。

def sum_up(*args): print(f"type: {type(args)}, length: {len(args)}") _sum = 0 for arg in args: _sum += arg print(_sum) sum_up(1) sum_up(2, 3, 4) sum_up(5, 15, 25)

在函数定义中,在形参名称前面加上一个星号*,则该形参即为可变长形参

调用时,所有的实参都打包为一个tuple后,再赋值于形参args。因此,形参args的数据类型为tuple

对于可变长形参,如果实参为一个诸如list的集合,也会自动打包为tuple,而其元素类型为list

def demo_func(*args): print(f"type: {type(args)}, length: {len(args)}") demo_func([1, 2, 3])

此时,形参args实际上是一个二维数组。

def demo_func(*args): for tuple_element in args: for list_element in tuple_element: print(list_element) demo_func([1, 2, 3])

可变长形参与位置形参的混同

当函数参数中同时有位置形参可变长形参时,由于可变长形参用于接收数量不固定的所有后续的实参,因此可变长形参须排在位置形参之后。

def demo_func(operator, *operands): result = 0 match(operator): case 'sum': for operand in operands: result += operand print(result) demo_func('sum', 1, 2, 3)

可变长形参与关键字形参的混同

可变长形参实际上也是一种位置形参,当它与关键字形参混同使用时,可暴露出其原形。

def demo_func(operator = 'sum', *operands): result = 0 match(operator): case 'sum': for operand in operands: result += operand print(result) demo_func(operator='sum', 1, 2, 3)

参数operator关键字形参,参数operands可变长形参,当我们将可变长形参排在关键字形参之后时,将出现编译错误:位置实参位于关键字实参之后

上面代码,如果没有调用函数,则不会报错;只有当调用函数时,才会导致报错。

因此,在定义函数时,参数的正确顺序应为:先是可变长形参, 然后才到各个关键字形参

def demo_func(*operands, operator = 'sum', is_print = False): result = 0 match(operator): case 'sum': for operand in operands: result += operand if is_print: print(result) demo_func(1, 2, 3, operator='sum', is_print = True)

在调用时,即使省略个别或所有关键字实参,均不影响正常编译。

def demo_func(*operands, operator = 'sum', is_print = False): result = 0 match(operator): case 'sum': for operand in operands: result += operand if is_print: print(result) demo_func(1, 2, 3, is_print = True) demo_func(1, 2, 3)

不同于位置形参与关键字形参的混同中的规则2,我们不能为可变长形参以赋值语句的方式来提供实参

def demo_func(*operands, operator = 'sum', is_print = False): result = 0 match(operator): case 'sum': for operand in operands: result += operand if is_print: print(result) demo_func(operands = 1, operator = 'sum', is_print = True)

此时,Python编译器将实参operands识别为关键字实参,从而报错。

鉴于此,当调用函数时,我们也应老老实实地将可变长实参排在关键字实参之前,且不使用赋值语句的方式。

不知Python的后续版本能否统一应对这种情况?

序列的解包

可以在序列之前添加一个*号,则序列的值将被解包为各个独立的个体,可用以投喂可变长形参

def demo_func(*args): print(args) nums = [1, 2, 3] demo_func(*nums)

dict形参

需求

在一个普通的函数中,我们可以声明一个形参,用以接收类型为dict实参

def process_dict(a_dict): print(a_dict) a_dict = {'name': 'Mike', 'age': 25} process_dict(a_dict)

这种方式,由调用者负责来构建dict

也可以使用另一种方式,调用者只需提供各个键值对作为实参,由函数将这些有键值对格式的所有实参自动打包为一个类型为dict形参,以供函数体使用。

方法是在声明函数参数时,使用**param_name的语法来声明一个形参。本文将这种类型的形参称之为dict形参

def demo_func(**kargs): print(type(kargs)) print(kargs) for karg in kargs: print(karg) demo_func(name='Mike', age=25)

调用函数时,共有2个键值对,形参**kargs将它们打包为一个dict

这种效果等同于:

def demo_func(name, age): print(name, age) demo_func(name='Mike', age=25)

但上述代码的局限性在于,我们必须事先定义好nameage这两个形参

而使用自动打包为dict形参的好处是,只需定义一个函数,却可以处理任意数量、任意键值对类型的数据。

基本规则

规则1:在函数调用时,对应于dict形参实参可以省略。

def demo_func(**kargs): print(kargs) demo_func() demo_func(name = 'Mike', age = 25)

当省略对应实参时,在函数体内将打包为一个空的dict对象。

与其他形参的混同

dict形参排在最后

在与其他形参进行混同时,在函数定义中,dict形参均须排在最后面。

def demo_func(posarg, *vargs, **kargs): print(posarg) print(vargs) print(kargs) demo_func(1, 2, 3, 4, 5, name = 'Mike', age = 25)

dict形参与关键字形参的识别

由于关键字形参在函数定义中已明确键名,因此,在调用函数时,当实参中出现了不在上述键名之列的的键名,都可被识别为dict实参。故此,在函数调用时,dict实参的顺序可随意排列。

def demo_func(name = 'Mike', age = 25, **kargs): print(name) print(age) print(kargs) demo_func(gender = 'male', name = 'Mike', age = 35, phone = 123456)

dict解包

def demo_func(**kargs): print(kargs) person = { 'name': 'Mike', 'age': 25 } demo_func(gender='male', **person)

在调用函数时,**person称为dict解包,将变量person的各个键值对都分离为独立的键值对;然后,连同gender='male'在内,一起打包为一个新的dict,再传送给demo_funckargs参数。

快速识别参数类别

定义

如上所述,位置实参既可以位置实参的方式,也可以关键字实参的方式来投喂位置形参

def demo_func(name, age): print(name, age) demo_func('Mike', 15) demo_func(name = 'Mike', age = 25) demo_func(age = 35, name = 'Mike')

为规范函数调用顺序,同时也为了更好地识别函数各种参数的类别,Python定义了以下语法:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2): ---------- ---------- ---------- | | | | | |-- Keyword only | | | |-- Positional or keyword | |-- Positional only

使用分隔符/*来隔开各种类别的参数。

  • 位于/之前的,只能以位置实参的方式来投喂;
  • 位于/*之间的,既可以位置实参,也可以关键字实参来投喂;
  • 位于*之后的,只能以关键字实参来投喂。

分隔符/*是可选的,可以不使用。因此上面所有的代码均为这种方式。当不提供/*时,实参可以同时使用位置实参关键字实参的方式向形参传递数值。

应用

限定只能以位置实参的方式来传递参数数值。

def demo_func(name, age, /, gender): print(name, age, gender) demo_func(name = 'Mike', 25, gender = 'male')

在函数定义中,形参nameage的后面有/字符,则要求相应的实参须以位置实参的方式来投喂。而上面代码实参name却以关键字实参的方式来投喂,因此出错。修正如下:

def demo_func(name, age, /, gender): print(name, age, gender) demo_func('Mike', 25, gender = 'male')

形参gender由于位于/之后,因此它可以关键字实参的方式来投喂。

限定只能以关键字实参的方式来传递参数数值。

def demo_func(*, name, age, gender): print(name, age, gender) demo_func(gender = 'male', name = 'Mike', age = 25)

由于出现了*号,则位于它之后的3形参,要求它们对应的实参必须以关键字实参的方式来投喂。实参顺序无关紧要。

函数设计技巧

定义一个fib函数:

def fib(n): result = [1, 1] while len(result) < n: result.append(result[-2] + result[-1]) return result[:n] print(fib(5))

根据变量result的不断变化的长度来决定迭代次数。

result的初始元素数量为2个,使用参数n进行切割,以确定返回的result的长度(同时应对当n值为1时的特殊情况)。

一些常用库函数

input

num = int(input('Please input a number:')) print(num + 1)

当在 Web 端使用时,input函数将导致弹出一个提示框窗口。

input函数所返回的数据类型为字符串。

参考资源

Python Docs

  1. Defining Functions
  2. Documentation Strings
  3. PEP 257 – Docstring Conventions
  4. Python Built-in Functions