Skip to content

03 函数

写了重复的代码?把它抽成函数。逻辑太长看不清?把它拆成函数。函数是组织代码的基本方式,Python的函数定义简单,但参数机制很灵活。

一、定义函数

1.1 基本语法

def定义函数:

python
>>> def fib(n):
...     """输出小于n的斐波那契数列"""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

def后面跟函数名和参数列表,冒号开头,缩进的是函数体。第一行字符串是文档字符串(docstring),用help()时会显示。

1.2 return语句

python
>>> def fib_list(n):
...     """返回小于n的斐波那契数列"""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)
...         a, b = b, a+b
...     return result
...
>>> fib_list(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

没有return语句,或者return后面没有值,函数返回None

二、默认参数

2.1 基本用法

python
>>> def ask_ok(prompt, retries=4, reminder='请重试!'):
...     while True:
...         reply = input(prompt)
...         if reply in ('y', 'ye', 'yes'):
...             return True
...         if reply in ('n', 'no', 'nop', 'nope'):
...             return False
...         retries -= 1
...         if retries < 0:
...             raise ValueError('无效的用户响应')
...         print(reminder)

调用方式:

python
ask_ok('确定吗?')                    # 只传必填参数
ask_ok('确定吗?', 2)                 # 重试2次
ask_ok('确定吗?', 2, '再来一次!')     # 全部指定

2.2 默认值只计算一次

python
>>> def f(a, L=[]):
...     L.append(a)
...     return L
...
>>> print(f(1))
[1]
>>> print(f(2))
[1, 2]
>>> print(f(3))
[1, 2, 3]

默认值在def时计算一次,之后每次调用共享同一个对象。如果默认值是可变对象(列表、字典等),就会出现上面的意外行为。

2.3 避免可变默认值陷阱

python
>>> def f(a, L=None):
...     if L is None:
...         L = []
...     L.append(a)
...     return L
...
>>> print(f(1))
[1]
>>> print(f(2))
[2]

None作为默认值,函数体内再判断,就不会有共享问题了。

三、关键字参数

3.1 基本用法

python
>>> def parrot(voltage, state='a stiff', action='voom'):
...     print(f"这只鹦鹉 {action}")
...     print(f"电压: {voltage}")
...     print(f"状态: {state}")
...
>>> parrot(1000)
这只鹦鹉 voom
电压: 1000
状态: a stiff

>>> parrot(voltage=1000000, action='VOOOOOM')
这只鹦鹉 VOOOOOM
电压: 1000000
状态: a stiff

>>> parrot('a million', 'bereft of life', 'jump')
这只鹦鹉 jump
电压: a million
状态: bereft of life

关键字参数必须在位置参数后面。

3.2 *args和**kwargs

python
>>> def cheeseshop(kind, *args, **kwargs):
...     print(f"奶酪: {kind}")
...     print(f"位置参数: {args}")
...     print(f"关键字参数: {kwargs}")
...
>>> cheeseshop("Brie", "one", "two", shop="cheese shop", client="Mr. Cheese")
奶酪: Brie
位置参数: ('one', 'two')
关键字参数: {'shop': 'cheese shop', 'client': 'Mr. Cheese'}

*args收集多余的位置参数为元组,**kwargs收集多余的关键字参数为字典。

四、特殊参数

4.1 仅限位置参数

/前面的参数只能用位置传入:

python
>>> def standard_arg(arg1, arg2=None):
...     print(arg1, arg2)
...
>>> standard_arg(1, 2)        # OK
>>> standard_arg(arg1=1)      # OK

>>> def pos_only_arg(arg1, arg2=None, /):
...     print(arg1, arg2)
...
>>> pos_only_arg(1, 2)        # OK
>>> pos_only_arg(arg1=1)      # 报错!arg1是仅限位置参数

4.2 仅限关键字参数

*后面的参数只能用关键字传入:

python
>>> def kwd_only_arg(*, arg1, arg2=None):
...     print(arg1, arg2)
...
>>> kwd_only_arg(arg1=1)      # OK
>>> kwd_only_arg(1)           # 报错!arg1是仅限关键字参数

4.3 组合使用

python
>>> def combined(pos_only, /, standard, *, kwd_only):
...     print(pos_only, standard, kwd_only)
...
>>> combined(1, 2, kwd_only=3)       # OK
>>> combined(1, standard=2, kwd_only=3)  # OK
>>> combined(pos_only=1, standard=2, kwd_only=3)  # 报错

五、lambda表达式

5.1 基本用法

lambda创建匿名小函数:

python
>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

5.2 配合排序

python
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]

lambda只能有一个表达式,不能有语句、注释或复杂逻辑。需要复杂逻辑就用def

六、文档字符串

6.1 写法约定

python
>>> def my_function():
...     """这里写函数的简短说明。
...
...     这里写更详细的说明,可以写多行。
...     说明函数的功能、参数、返回值等。
...     """
...     pass
...
>>> help(my_function)

第一行简短说明,空一行后写详细说明。help()函数会显示文档字符串。

6.2 类的文档字符串

python
class MyClass:
    """类的简短说明。

    详细说明写在这里。
    """

    def my_method(self):
        """方法的简短说明。"""
        pass

七、函数注解(3.0+)

7.1 基本注解

python
>>> def f(ham: str, eggs: str = 'eggs') -> str:
...     print("Annotations:", f.__annotations__)
...     print("Arguments:", ham, eggs)
...     return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
Arguments: spam eggs
'spam and eggs'

注解是可选的元信息,不影响运行,但可以被IDE和类型检查工具使用。

八、总结

特性说明
def定义函数
return返回值,没有则返回None
默认参数def f(a, b=1)
*args收集位置参数为元组
**kwargs收集关键字参数为字典
/前面的参数仅限位置
*后面的参数仅限关键字
lambda匿名小函数
文档字符串函数第一行字符串
注解def f(a: int) -> str

默认参数只计算一次,避免用可变对象(列表、字典)做默认值。lambda适合简单表达式,复杂逻辑用def