函数

这篇笔记我们学习Python中函数的使用。

定义和使用函数

下面例子中我们定义了一个函数用来求向量的长度,函数接收2个float类型参数,返回float类型结果。

import math


def foo(x: float, y: float) -> float:
    """
    计算向量长度
    :param x: 向量x坐标
    :param y: 向量y坐标
    :return: 向量长度
    """
    return math.sqrt(x ** 2 + y ** 2)


# 调用函数
result: float = foo(1, 1)
print(result)

代码中对于参数和返回值的类型我们使用了类型标注,它们是可以省略的,这里写类型标注的目的是为了提升代码可读性。

此外我们还对函数使用了DocString注释,这是一种特殊的注释,注释中以约定格式编写了参数和返回值的说明信息,这些注释可以被IDE识别解析,也可以被sphinx等文档工具用来生成HTML格式的文档。Python中DocString注释有多种风格,以上使用的风格是reStructuredText(reST)。当然,DocString注释也是可选的,但是添加注释能让我们的代码可读性更好。

参数默认值

Python的函数参数支持默认值,如果有默认值的参数未被设置则会使用默认值。

def foo(a: int, b: int = 0):
    print(f"foo({a}, {b})")


foo(1)
foo(1, 2)

可变参数

可变参数可以传多个参数值,它们实际将以元组形式传入函数。

def foo(*nums: int):
    print(nums)


foo(1)
foo(1, 2, 3)

关键字参数

Python中在调用函数时,实际上有位置参数和关键字参数两种传参方式,前面我们使用的都是位置参数,而按关键字传递参数的做法叫传递关键字参数,下面是一个例子。

def foo(message: str, loop: int):
    for i in range(loop):
        print(message)


foo(loop=3, message='Hello, world!')

代码中,foo()函数接收两个参数,分别是messageloop,我们调用该函数时使用了关键字参数,它们的顺序和默认的位置参数是不同的。

注意:调用函数时如果混用位置参数和关键字参数,关键字参数必须放在位置参数之后。

可变关键字参数

可变关键字参数和可变参数类似,可变关键字参数使用两个星号**定义,它常用于动态的函数参数扩展,下面是一个例子。

def insert_student(name, age, **kw):
    print(f'name: {name}, age: {age}, kw: {kw}')


insert_student('Tom', 20, a=1, b=2)

代码中,传入的kw参数实际上是字典类型,它包含两个元素,分别是键为a1和键为b2

上面代码输出内容如下。

name: Tom, age: 20, kw: {'a': 1, 'b': 2}

命名关键字参数

命名关键字参数是一种特殊的关键字参数,它要求在调用时某些参数必须使用参数名来传递。合理使用命名关键字参数可以使函数调用传参更加清晰,避免位置参数的歧义。命名关键字参数通过在函数定义的参数列表中使用一个星号*后跟参数名来实现。星号*表示后面的参数必须使用关键字的形式来传递。

def foo(name, age, *, city, country):
    print(f"{name} is {age} years old, lives in {city}, {country}.")


foo("Tom", 18, city="New York", country="USA")

上面代码中citycountry是命名关键字参数,调用时必须显式地使用关键字传参,否则会报错。

返回多个值

前面我们介绍过,Python的函数可以返回多个值,它的本质上是一个元组,下面是一个例子。

def get_numbers() -> tuple[int, int, int]:
    return 1, 2, 3


x, y, z = get_numbers()

函数返回多个值时,我们可以直接用逗号分隔的多个变量来接收返回值。

函数对象

Python中函数本身也是一种对象,它可以被赋值到变量、存储到容器类型、作为函数参数以及作为函数返回值等,这类似于C语言的函数指针。下面代码我们将函数foo本身赋值给了变量v

def foo(a: int, b: int) -> int:
    print(f'{a} {b}')
    return a + b


v = foo

如果你希望给v加上类型标注,它实际上是这样的。

v: Callable[[int, int], int] = foo

高阶函数

如果一个函数接受一个或多个函数作为参数或者返回一个函数对象作为返回值,这个函数就被称为高阶函数

函数作为参数

下面例子代码我们将函数foo作为参数传入了bar函数中。

from typing import Callable


def foo(a: int, b: int) -> int:
    print(f'{a} {b}')
    return a + b


def bar(callback: Callable[[int, int], int]) -> int:
    return callback(1, 2)


print(bar(foo))

代码中,bar()函数就是一个高阶函数,它的参数callback接收函数类型,我们用可选的类型标注限制了它的参数和返回值类型。

函数作为返回值

下面例子函数get_foo()返回了一个函数对象foo

from typing import Callable


def foo(a: int, b: int) -> int:
    print(f'{a} {b}')
    return a + b


def get_foo() -> Callable[[int, int], int]:
    return foo


get_foo()(1, 2)

代码中get_foo()也是一个高阶函数,它返回了另一个函数,返回值的类型是Callable[[int, int], int]

lambda表达式

Python中我们可以创建lambda表达式,它可以理解为一个没有名字的函数(匿名函数)。相较于普通函数,Python的lambda表达式通常用于实现简短的、一次性的功能,下面是一个例子。

from typing import Callable


def bar(callback: Callable[[int, int], int]) -> int:
    return callback(1, 2)


result = bar(lambda a, b: a + b)
print(result)

代码中,bar()函数是一个高阶函数,它接收一个函数作为参数,但我们传入的是一个匿名函数(lambda表达式),它简短的表达了一个相加的逻辑。

注意:

  1. Python的lambda有一定局限性,它只能写单行的表达式,无法替代功能复杂的函数,这和很多其它编程语言不同。在复杂逻辑的情况下,使用普通的函数定义更合适。
  2. 大量lambda表达式可能导致代码难以理解,我们要确保在合适的场景下合理使用它。

闭包

Python中的闭包(closure)是一个函数对象,它可以捕获并记住其定义时所在的环境变量。简单来说,闭包是在其定义的作用域之外执行时仍然能够访问该作用域内的变量的函数。闭包通常用于创建具有特定上下文或状态的函数。

创建一个闭包需要满足以下三个条件:

  1. 函数嵌套:一个函数嵌套在另一个函数内部。
  2. 自由变量:内部函数引用了外部函数的变量。
  3. 返回内部函数:外部函数返回了内部函数。

下面是一个闭包的例子。

def outer_function(x):
    def inner_function(y):
        return x + y

    return inner_function


closure = outer_function(10)
result = closure(5)
print(result)

在这个例子中closure是一个闭包对象,它“记住”了外部函数outer_function中的变量x的值为10。当调用closure(5)时,它会将y的值设置为5并计算x + y的结果。上述代码的输出值为15

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap