在数学中,函数(Function)是一个将每个元素从一个集合(定义域)映射到另一个集合(值域)的关系。

Function,英/ˈfʌŋkʃn/ n. 功能;函数;作用; v. 起作用;运转;正常工作

1. 基础认知

编程中的函数大抵如此,描述了一种“输入-输出”的关系。

1.1 函数的概念

概念

函数是可以重复执行的语句块,可以重复调用

作用

用于封装语句块提高代码的重用性

定位

函数是面向过程编程的最小单位

1.2 函数的定义

使用 def 语句定义一个函数

语法


说明

  1. def:用来声明函数的关键字

  2. function_name:函数名,一个有效的标识符,规则和变量名一致。匈牙利命名法或驼峰法。

  3. parameters:形参,可以是0 ~ n 个,参数之间用逗号分隔。

  4. 函数体:定义函数执行的具体操作。

  5. return:指定函数的返回值,没有则返回None,默认返回None

  6. return之后的代码不会执行。

示例


在add函数的return后面的print输出没有执行

2. 内置函数

Python提供了大量的可直接使用的内置函数,主要执行一些常见的操作:数据处理、类型转换、数学计算、输入输出等。

官方地址:内置函数 — Python 3.13.2 文档

2.1 all()

如果可迭代对象中的所有元素都为 True返回 True,否则返回 False


2.2 sum()

返回可迭代对象中所有元素的总和。


分析:可以是负数,可以有小数,可以是元组,可以是列表,int float这样的类型才能进行sum函数的运算

2.3 sorted()

返回一个新列表,其中包含可迭代对象中的元素,按照升序排序。


分析:lambda的基本用法: lambda 参数1, 参数2, ... : 表达式

在 Python 中,lambda 是一个关键字,用于创建匿名函数。匿名函数是一种没有名称的函数,通常用于需要一个简单函数的一次性场景。lambda 函数的语法非常简洁,适合快速定义简单的操作。

# 定义一个 lambda 函数,计算两个数的和
add = lambda a, b: a + b
print(add(3, 5))  # 输出:8

所以上面的是读取字典中age键的值

2.4 reversed()

返回一个反向迭代器。


2.5 callable()

检查对象是否可以被调用(即是否是函数或方法)。

分析:自己定义的或者python自带的都可以调用callable的函数

2.6 zip()

将多个可迭代对象打包成一个元组,常用于并行遍历多个序列。

2.7 eval()

将字符串作为有效的 Python 表达式来执行,并返回结果。

eval(expression, globals=None, locals=None)


2.8 exec()

执行存储在字符串中的 Python 代码。

exec(object, globals=None, locals=None)


区别

返回值eval() 返回表达式的结果,而 exec() 不返回任何值(返回 None)。

用途eval() 用于计算表达式,而 exec() 用于执行代码块。

安全性eval()exec() 都存在安全隐患,因为它们会执行任意代码。如果代码来自不可信的来源,可能会导致安全问题。

2.9 globals()locals()

globals() 返回当前全局符号表(一个字典);locals() 返回当前局部符号表(也是字典)。

x = 10
print(globals())  # 输出: 包含全局变量的信息
print(locals())   # 输出: 包含局部变量的信息

2.10 filter()

从可迭代对象中过滤出符合条件的元素。

分析:filter的逻辑是,先通过形参1的函数来判断返回的结果是true还是false,如果是true,就将形参2的参数返回输出,传给新的对象x2.

2.11 map()

map(函数,可迭代对象) 传入两个参数: 函数和可迭代对象返回一个经过函数筛选之后的可迭代对象

分析:可以看出来,map函数里面有两个形参,第一个形参是函数,第二个是来迭代的对象,把第二个形参里面的参数,传入到第一个形参函数里面,返回一个结果,然后让map函数来返回,赋值给新的对象x2

3.12 reduce()

reduce 是 Python 中的一个函数,用于对一个序列进行累积计算。它属于 functools 模块,在 Python 3 中不再是一个内置函数,因此需要先导入该模块才能使用。

reduce 函数会对序列中的元素依次应用一个函数,并将结果累积起来,最终返回一个单一的值。

from functools import reduce

reduce(function, iterable, initializer=None) 注意,如过没有给initializer赋值的话,累积将从序列的第一个元素开始。

分析:可以理解x1 + x2 ,这个函数是每次按顺序调用序列里的数据,经过形参一的函数计算返回一个新的值,作为下一次的x1加上序列表里第三个参数这个参数是x2,它们相加获得新的数作为x1加序列表里的第四个参数,依次类推,注意上述代码里是+,在实际情况也可能是乘法等自己定义函数方法,就把上面+的逻辑换成乘。

总结:前两个参数进行函数运算后得到结果,往后移动一位,得到的结果和新的一位进行函数运算再返回一个结果,再后移一位,依次类推

3. 函数的调用

函数名后面跟上 小括号() 表示函数的执行。

3.1 基本用法

语法

函数名(实际调用传递参数)

说明

函数调用是一个表达式,它可以参与运算

示例


3.2 调用传参

函数调用时传递参数的方式有多种,包括位置传参、关键词传参、多个参数解包、参数默认值等。

3.2.1 位置传参

最常见的传参方式,参数按定义的顺序依次传入函数,从左到右

示例:


3.2.2 关键词传参

通过指定参数的名称来传值,无顺序限制,代码可读性较高。

示例:


分析:因为是关键词的传参,顺序可以改变

3.2.3 参数默认值

定义函数时可为某些参数指定默认值,如果不传参则使用默认值。默认值参数必须位于无默认值参数的后面。

示例:


分析:按照形参顺序,依次传入数据,后面的有默认值的参数,可以不写

3.2.4 可变位置参数

使用 args可让函数接受任意数量的位置参数。args 会将多余的位置参数收集成一个元组

示例:


结合使用 *args 和 **kwargs

在同一个函数中,可以同时使用 *args**kwargs,但它们必须按照顺序出现,即 *args 在前,**kwargs 在后。

3.2.5 可变关键词参数

使用 kwargs 可以让函数接受任意数量的关键词参数。kwargs会将多余的关键词参数收集成一个字典。

示例:


分析:a传入的是位置参数,*是用来声明后面的都是关键词参数,它不是一个参数

3.2.6 多参数解包

Python 允许在调用函数时解包序列或字典,使其作为位置参数或关键词参数传递给函数字符串也能解包,因为字符串是字符的序列

3.2.6.1 解包位置参数

使用 * 解包

3.2.6.2 解包关键词参数

使用 ** 解包

分析:用解包的方式,可以极大的简化代码,提高可读性

3.2.6.3 结合位置参数和关键词参数解包

分析:解包获得username,email键的值,刚好对应函数的参数名。解包完后剩下的键值对,放在**kwargs里面。就像是**user_info先执行解包的操作,得到username="alic123",email="alice@example.com" 这些

3.2.6.4 解包字典创建新字典

注意事项

  1. 字典的键必须是字符串:因为函数的参数名是字符串,所以字典的键必须是字符串类型。

  2. 参数名必须匹配:解包的字典中的键必须与函数的参数名匹配,否则会报 TypeError

  3. 默认参数:如果字典中没有提供某些参数,函数会使用默认值(如果有的话)。

3.2.7 参数混合使用

在函数调用时,可以混合使用位置参数和关键词参数,但位置参数必须放在关键词参数前面

示例:


 注意,如果要在一个函数里面同时传入很多参数的时候,有些是默认参数,在声明函数的时候,有默认参数的放在没有默认参数的后面,这样可以实现只给一部分参数传入参数,比如

跳过对b的传参

4. 可变和不可变参数

在 Python 中,实参可以是可变类型或不可变类型。它们的区别主要体现在值传递引用传递的行为上。

4.1 不可变类型

不可变类型包括:int、float、str、tuple、frozenset 等。

传递方式是值传递: 传递给函数的是该对象的值,函数内部修改该值不会影响外部变量的值。

示例:


分析:在代码里,def的函数,是不会自己运行的,需要通过代码来调用,modify(a),在全局变量里声明了a=5,所以在调用函数里面,形参x的初始值就是a=5,改成10后,只是开辟了一个新的空间,存储了10这个int变量,然后让对象x指向它,5其实还在,a还是指向的5这个变量位置

4.2 可变类型

可变类型包括:list、dict、set 等。这些类型的对象可以在原地修改。

传递方式是引用传递 :传递给函数的是对象的引用(即内存地址),在函数内部修改该参数的内容会直接影响外部变量。

示例:

分析:可变类型,修改的时候,是在原来的数据上面进行修改,形参和全局变量都是指向的同一个地址,所以当在函数里面修改了lst对象,全局变量因为也是指向同一个地址,所以原始数据也变了

3.3 避免副作用


如果我真的想修改形参变量列表的值,但是又不影响全局变量的值,我应该怎么做呢?


通过复制对象来避免可变类型副作用:

列表:使用 lst.copy() 或切片 lst[:] 来创建一个副本。

字典:使用 dict.copy()copy.deepcopy() 来进行深拷贝。

元组:推荐用tuple 创建新的元组,虽然本身就是不可变。

示例:


分析:先通过copy的函数,来复制一个新的字典或者列表,就是开辟了一块新的空间,来存储与全局可变变量只有地址不一样,里面的变量参数完全一样的列表或者字典,在函数里面就调用新复制的副本,这样就避免了全局变量的改变

5. 匿名函数

匿名函数是没有名字的函数,通常用于需要一个简短的、临时的函数场景,它可以有任意数量的参数,但只能包含一个表达式,并返回该表达式的结果。

5.1 基本语法

lambda arguments: expression

arguments一个或多个输入参数,可以是位置参数关键词参数。

expression:一个单一的表达式,它的值将作为返回值返回

5.2 匿名函数定义

5.3 使用场景

lambda 函数常常与高阶函数如 map()filter()sorted() 等一起使用。

处理列表


分析:map函数是输出结果的,依次调用numbers这个列表里的数据,传递给lambda作为它的形参,执行表达式x**2,返回x**2这个结果,传递给squared_numbers列表

筛选列表


分析:filter函数只有当lambda返回的是true的时候,才输出x,当x%2!=0的时候,返回false,filter就不返回值

实现排序


分析:key是来排序的关键,可以是长度,大小,具体在上文中sorted函数有讲解

5.4 条件表达式

你可以在 lambda 函数中使用条件表达式(if-else)来做一些简单的决策。


分析:if - else 之间的表达式,是用来判断的,当返还值为true的时候,就输出if前面的结果,返回值为false的时候就输出else后面的结果 ,类似C语言里面 is_even=(x%2==0)?"Even":"Odd"

5.5 匿名函数优点

  1. 结构简单lambda 函数通常用在需要一个短小函数的地方,而普通函数则更适合复杂的逻辑。

  2. 匿名性lambda 函数是没有名字的,它们通常只在一个地方使用,并且不需要被重复调用。

  3. 功能限制lambda 函数只能包含一个表达式不可以包含多行语句,而普通函数可以包含多行代码、条件判断、循环等复杂逻辑。

6. 高阶函数

高阶函数是指可以接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。

6.1 常见高阶函数

比较常用,单独拎出来熟悉一下

6.1.1 map

map(function, iterable)

map函数:

接受一个函数和一个可迭代对象

将接受的函数应用到可迭代对象的每个元素上

返回一个包含结果的迭代器

numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
# squared现在包含[1, 4, 9, 16, 25]
6.1.2 filter

filter(function, iterable)

filter函数:

接受一个函数和一个可迭代对象

用接受的函数来筛选出可迭代对象中满足条件的元素

返回一个包含满足条件的元素的迭代器

numbers = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
# even_numbers现在包含[2, 4]
6.1.3 reduce

reduce(function, iterable[, initializer])

reduce函数:

reduce函数接受一个函数和一个可迭代对象

将接受的函数累积地应用到可迭代对象的元素上

可选的 $initializer$ 参数可以作为累积的初始值

from functools import reduce
​
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
# product现在包含120 (1 * 2 * 3 * 4 * 5)

6.2 作为参数

高阶函数可以接受其他函数作为参数,用于自定义行为。

7. 变量作用域

变量的作用域(Scope)是指在程序中某个变量的有效范围,也就是在代码的哪个部分可以访问或修改该变量。作用域定义了变量的可见性和生命周期

Python中的变量作用域遵循LEGB规则(Local, Enclosing, Global, Built-in),依次搜索变量的定义位置。

7.1 Local→局部作用域

指函数或方法内部定义的变量。

仅在函数内部有效,函数外部无法访问。

在函数调用时被创建,在函数调用后自动销毁。

示例:


分析:创建的局部变量x只能在func函数里面调用,在函数外面就找不到

7.2 Enclosing→嵌套作用域

指外层函数中的变量,在内层函数中可访问,但不可修改。

当一个函数嵌套在另一个函数内部时,外层函数的变量属于Enclosing作用域。

示例:


分析:虽然在全局变量里面声明了x = 10,输出还是 x = 20,因为访问顺序的原因

nonlocal:将局部作用域中变量声明为外部嵌套函数作用域中的变量


分析:在上述代码中,如果没有声明nonloacl a,虽然可以读取外部变量a,但当尝试改变外部变量a的时候,就会报错

7.3 Global→全局作用域

指模块级别定义的变量,整个模块都可以访问。

如果想在函数中修改全局变量,需要使用global关键字。

示例:如何用一个变量来记录一个函数调用的次数


7.4 Built-in→内建作用域

包含Python内建的函数、异常和常量,如print(), len(), int, Exception等。

这些变量可以在任何地方使用

示例:

print(len([1, 2, 3]))  # 使用内建函数len

7.5 变量查找顺序

作用域遵循LEGB规则(Local, Enclosing, Global, Built-in)

示例:


7.6 超越训练

分析:

题一:第一次返回的是fun(m,n=0),所以现在a的值是字典{"fun":lambda m:fun(m,0)},然后执行a["fun"](1)操作,可以得到,输出0,然后返回字典{"fun":lambda m:fun(m,1)},但是这个字典并没有转递给a,所以在后续的调用中,a还是={"fun":lambda m:fun(m,0)}*

 None,0,0,0。

题二:这个是链式调用,首先是 fun(0),得到b=字典{"fun":lambda m:fun(m,0)},然后)["fun"](1)调用,返回的值是{"fun":lambda m:fun(m,1)},注意这样一步不像题一,这一步会把返回的结果赋值给b,然后调用["fun"](2),输出1,返回{"fun":lambda m:fun(m,2)},最后一步同理

输出None,0,1,2

题三:题一,题二的结合

输出None,0,1,1

8. 函数内存分配

1、将函数的代码存储到代码区,函数体中的代码不执行。

2、调用函数时,在内存中开辟空间(栈帧),存储函数内部定义的变量。

3、函数调用后,栈帧立即被释放。

def func(a, b):
    a = 20
    b[0] = 20
​
a = 10
b = [100]
func(a, b)
print(a) # 10
print(b) # [20]

9. 递归函数

递归是指函数直接或间接调用自身的过程。递归通常用于解决分解成相似子问题的复杂问题。

9.1 函数结构

递归函数通常由两个部分组成:

  1. 终止条件:用于确保递归能够停止避免无限递归

  2. 递归调用:函数在其内部调用自身,传递简化或减少的参数,以解决更小的子问题。

9.2 基本步骤

  1. 定义递归函数:在函数体内调用函数自身。

  2. 设定终止条件:确保递归能终止。

  3. 逐步简化问题:通过传递较小的参数递归求解,直至终止条件满足。

9.3 递归示例

递归:先递进,再回归。

9.3.1 阶乘定义

n! = n * (n-1) * (n-2) * ... * 1

递归公式:n! = n * (n-1)!,终止条件:0! = 1

示例代码:

def factorial(n):
    # 终止条件:n为0或1时,返回1
    if n == 0 or n == 1:
        return 1
    # 递归调用:n * (n-1)!
    return n * factorial(n - 1)
​
# 测试
print(factorial(6))  # 输出 720

执行过程:

递归函数执行展开的过程 先递进,后回归

9.3.2 斐波那契数列

斐波那契数列(Fibonacci sequence)是一种经典的数列,它的特点是从第 3 项开始,每一项都等于前两项之和。前两项通常定义为:

F(0) = 0

F(1) = 1

从 F(2) 开始:

F(2) = F(1) + F(0) = 1 + 0 = 1

F(3) = F(2) + F(1) = 1 + 1 = 2

使用递归的思想实现斐波那契数列

参考代码:

def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)
​
​
print(fibonacci(8))

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐