惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V2EX - 技术
V2EX - 技术
Security Archives - TechRepublic
Security Archives - TechRepublic
Cisco Talos Blog
Cisco Talos Blog
T
Tor Project blog
博客园 - 司徒正美
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
宝玉的分享
宝玉的分享
小众软件
小众软件
博客园_首页
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Project Zero
Project Zero
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Spread Privacy
Spread Privacy
I
InfoQ
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
M
MIT News - Artificial intelligence
爱范儿
爱范儿
The Cloudflare Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Tenable Blog
S
Securelist
N
News and Events Feed by Topic
Simon Willison's Weblog
Simon Willison's Weblog
Webroot Blog
Webroot Blog
The Hacker News
The Hacker News
O
OpenAI News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes
PCI Perspectives
PCI Perspectives
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
L
LINUX DO - 热门话题
I
Intezer
Scott Helme
Scott Helme
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
Cybersecurity and Infrastructure Security Agency CISA
Google Online Security Blog
Google Online Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Security Affairs
AI
AI
AWS News Blog
AWS News Blog
Security Latest
Security Latest

少数派

派早报:Google 发布 Fitbit Air 等 - 少数派 「新人报到」確認需求,再開始 - 少数派 从 SOLO 独立开发者社区,我看到了越来越多开发者开始做自己的产品 - 少数派 我怎么管理那些"不常做,但总会忘"的生活事项 - 少数派 人形机器人量产元年,数据才是具身智能的“生死线” - 少数派 BuhoLaunchpad 高度还原 Mac 启动台:开发历程与思考 - 少数派 五年陪伴依然不舍,DIY 换壳后让罗技 MX Master 3 继续服役 - 少数派 新玩意 240|少数派的编辑们最近买了啥? - 少数派 一日一技|为什么你应该关闭 iOS 的键盘声音 - 少数派 我做了个插件和 Skills,一键提取任何网站的设计规范 Design.md - 少数派 住在三四线城市的你,该开始录播客了 - 少数派 甘南秘境,大白高国 - 少数派 AI的审美:谁让把我变成川内倫子 - 少数派 返工怎能不烦恼,打工人片单总有一部是你的「嘴替」 - 少数派 为了让「上厕所」更健康,我做了一个小工具 - 少数派 AI + Skill,能够让生成的文章去除 AI 味吗? - 少数派 新玩意|韶音OpenDots ONE 耳夹式耳机 - 少数派 《美满》| 在每一个春天的晚上相爱(362) - 少数派 新玩意|优篮子 PS01 MagSnap 磁吸支架 - 少数派 自我整合手记 | 我开始早睡了:用稳定规则,为自由托底 - 少数派 用龙虾(OpenClaw)两个多月,我最深的12个体会 - 少数派 听歌时间到,12 张你可能错过的 2025 华语乐坛好专辑 - 少数派 承诺能追吗 - 少数派 macOS 26启动台没了? 我做了个不一样的App启动器 - Keboard - 少数派 《四海为家的人》| INTJ对话INTJ(361) - 少数派 你发过的那些黑历史,是时候一次清干净了 - 少数派 新玩意:安安静静玩,越玩越专注:计客密码机 - 少数派 iPad 用户首次体验 Android 平板:vivo Pad6 Pro - 少数派 数据逻辑强 - 少数派 极北行+ | 一路向北,探访日本至北之地 | 001 - 少数派 万字剖析:千问App深度体验报告(2026) - 少数派 在2026年,如何真正防止别人抄袭你的作品 - 少数派 怎么用 50 块搭个 AI 语音助手?我踩了 3 天坑 - 少数派 YeeroAI:让 AI 对话真正成为知识管理的一部分 - 少数派 爬泰山 - 少数派 「旅图显影」 App 更新:这次,我们补上了一点「手感」 - 少数派 假期出门太折磨?我的 23 条经验帮你规划惬意旅行 - 少数派 工作流会变吗 - 少数派 Claude Opus 4.6 怎么用最省钱?我测了 5 种方案 - 少数派 GPT Image 2 让图文并茂不再稀罕 - 少数派 用户侧出发——什么是AI,我要不要学习? - 少数派 找片、转存、整理、播放一条龙!让你的付费网盘值回票价 - 少数派 欢迎试用!日课一问2.0插件 - 少数派 自己做的MDeditor,原本想购买 Typora 试了两次支付不成功,干脆自己做一个 - 少数派 vibe coding了一个 3MB 的小工具,让 ~/Downloads 彻底告别混乱 - 少数派 因为受不了 Mac 的风扇策略,我做了一个风扇控制工具 - 少数派 别只怪模型 - 少数派 Warp 终端的 AI 功能怎么用?我测了一周的体验 - 少数派 AI 写代码老是出 bug?这 5 个配置我后悔没早知道 - 少数派 「新玩意」苹果出相机可能就这样:Sigma BF + 45mm F2.8 DG Contemporary - 少数派 一个面向2030年的AI操作系统是什么样子的:浅谈cola这款有灵魂的Agent - 少数派 别只看写代码 - 少数派 每天解决10个问题,还是一口气攻坚解决400个? - 少数派 AI 交易机器人怎么搭?我用 Claude 跑了一周实盘 - 少数派 Maptoposter Online:把你爱的城市画成艺术海报 - 少数派 Function Calling 怎么用?我测了 3 个模型发现差距真大 - 少数派 Legend Talk:我做了个 AI 圆桌,让 160 位思想家围着你的问题转 - 少数派 如何找到自己的蓝方?在小县城寻找压力测试 - 少数派 语音输入与软件接口|2026年聊AI时,我们都聊些什么(上) - 少数派 混动已经卖爆,纯电又来补刀——钛7闪充版简直“不讲武德” - 少数派 本月玩什么|朋友收藏、识质存在、沙罗周期 - 少数派 为什么要每天坚持输出? - 少数派 Claude API 挂了好几个小时,你的项目有备用方案吗? - 少数派 Function Calling 没你想的复杂——我用它做了个有点用的工具 - 少数派 登录系统立即播放视频或者图片音乐的软件 - 少数派 我为什么创建 FlipHTML5 下载工具 - 少数派 残局没电?多品牌外设电量统一管理软件EasyBluetooth已支持RTSS游戏内显示以及AIDA64 - 少数派 前往通义路的路 - 少数派 太好看了,媲美Sun的个人导航页,NAS部署星云门户 - 少数派 乌黑嘴唇“一键检测”上线了 - 少数派 派早报:Claude AI 接入多个创意软件生态、FILCO 生产方接手品牌等 - 少数派 【更新】BearCLI、Claude 连接器与 MCP 服务器 - 少数派 记了上千条流水,还是看不懂财务?我做了一个让 AI 读懂账本的工作台 - 少数派 MINI R56 升级原厂 Sport 模式 - 少数派 新玩意 | 一棵柠檬树(仿真版) - 少数派 Momenta的“物理AI”野望,需迈过“含摩量”这道关 - 少数派 网页直接投屏控制手机!NAS一键部署PandaScrcpy,流畅丝滑可远程。 - 少数派 众测|邀你一同探索随身 AI 硬件入口 YoooClaw C·ONE - 少数派 2050大会:分享时间是真诚 参会记 - 少数派 iPad 赋能电影创作:国内首部宣纸手绘长片《燃比娃》的幕后故事 - 少数派 AI的审美:我用 8 个大模型给 100 张旅行照片打分 - 少数派 普通人如何破圈?去参加一个本地协会 - 少数派 把极空间的图标全换了,主题DIY全攻略打造你的专属NAS桌面 - 少数派 电子便签墙,帮你实现便签自由 - 少数派 我如何用三个 CLI 工具取代文档创建需求 - 少数派 原来真的有人可以玩一辈子 - 少数派 社区速递 139 | 派友热议三月买了啥、复古单反尼康 Df 体验 - 少数派 06 作品的赏析与评价 - 少数派 TDS REVIEW|索尼 WF-1000XM6 降噪真无线耳机体验 - 少数派 35.98万起售的第二代腾势D9,我看重的不是堆料,而是不凑合 - 少数派 鼠须管 Squirrel 皮肤配置指北 - 少数派 从watch ultra2换到redmi watch6 - 少数派 派早报:阿里巴巴发布视频生成模型 HappyHorse 1.0 等 - 少数派 别迷信1M - 少数派 家人们天塌了!网盘“大封杀”,多个渠道多条路,NAS部署PanHub - 少数派 AI与人勾心斗角!NAS一键部署AI狼人杀,假日休闲必备。 - 少数派 电商必备!Comfyui工作流批量生图插件,一次生成12张!支持Nano banana pro模型 - 少数派 Comfyui工作流配置Gpt-image-2模型教程,0.03/张 - 少数派 OpenClaw第三方APi怎么配置?可使用Gpt-image-2模型 - 少数派 会员社区话题精选 Ep. 103 - 少数派
6. 高阶函数 - 少数派
2023-08-03 · via 少数派

Hi,大家好。 我是茶桁。

本节课,我们来学习一下Python中的「高阶函数」。

递归函数

让我们先来了解一下,什么是递归函数。

递归函数就是定义一个函数,然后在此函数内,自己调用自己。

既然是自己调用自己,那这个函数必须要有一个结束才行,否则会一直重复的调用下去,直到调用层数越来越多,最终会导致栈溢出。

让我们先写一个雏形:

# 初步认识一下递归函数
def recursion(num):
    print(num)
    recursion(num - 1)

recursion(3)

# 执行结果
3 2 1 0 -1 -2 -3 -4 -5 -6 -7 ....

RecursionError: maximum recursion depth exceeded while calling a Python object

最后,导致栈溢出,程序报错。

那么这个程序到底做了什么?

首先,我们定义了一个函数,然后执行,执行的时候给了一个参数3

进入程序之后,先将3打印了一遍,然后在函数内部,又调用了一遍自己,参数为3-1,也就是传了一个参数2,在进入函数之后,打了了2, 继续自己调用自己,传参2-11-1, 0-1, ...就这样一直循环下去。

那么我们怎么样让这个程序停下来?就是在函数自己调用自己之前,加上一个限制条件:

# 初步认识一下递归函数 3 2 1 0
def recursion(num):
    print(num)
    # 检测当前值是否到0
    if num > 0:
        # 调用函数本身
        recursion(num - 1)
recursion(3)

# 执行结果
3
2
1
0

我们给调用之前加了一个条件,如果num > 0才允许继续执行,这样,当程序传递了1-1之后,执行了最后一次打印,然后就不向下执行了。

不过不要以为程序到这里就结束了,我们多加一行代码试试看:

# 初步认识一下递归函数 3 2 1 0
def recursion(num):
    print(num, end=" ")
    # 检测当前值是否到0
    if num > 0:
        # 调用函数本身
        recursion(num - 1)
    print(num, end=" ") # 又加了一个print函数
recursion(3)

# 执行结果
3 2 1 0 0 1 2 3

如果你不知道程序做了什么,我们稍微分析一下:

解析当前递归函数的执行过程:
```
recursion(3) ==> 3 
    recursion(3-1) ==> 2
        recursion(2-1) ==> 1
            recursion(1-1) ==> 0
            recursion(0) ==> 0
        recursion(1) ==> 1
    recursion(2) ==>2
recursion(3) ==> 3
```

也就是,在递归函数中,程序是一层一层的进入,然后再一层一层的返回。

这就好像是, 我们在上学的时候,你坐在最后一排,但是你有个心仪的女孩坐在最前面。你想要对方电话,这个时候你传递一个纸条给前面的同学,前面的同学再往前传,一直往前传到女孩手里。女孩看完之后,写完回复再一次次的传回来。最后你满怀期待的打开一看:“滚。”

当然,我们的递归函数和这个不同的地方是最后不会多加那个“滚”字。

回调函数

什么是回调函数呢?

我们首先来思考一个问题:

def func(a):
    print(a)

func(a)

在这个简单的函数中,我们已经学会了传值a给到func(),那么参数到底可以传一些什么进去?a可以是什么?能不能是一个函数呢?

这就引出了我们现在的内容:

# 带有回调函数参数的函数
def func(obj):
    print(obj, type(obj))
    # 并且在函数中调用传递进来的形参函数
    obj()

def _self():
    print("i am _self")

func(_self)

# 执行结果
<function _self at 0x111ed4280> <class 'function'>
i am _self

可以看到,我们选择执行的是func函数,但是最后打印出了_self函数中语句。原因就是我们在执行func函数的时候,将_self函数作为参数传递给了func的形参obj, 我们在其中打印了obj以及obj的类型,并且最后执行了一下obj, 实际上也就是执行了一遍_self函数。

如果在一个函数中要求传递的参数是一个函数作为参数,并且在函数中使用了传递进来的函数,那么这个函数我们就可以称为是一个回调函数

我们拿系统内部的一个现成的函数来重新封装一个新的函数来试试:

# 做一个数学计算的函数
def func(x, y, obj):
    """
    此函数用来整合其他的数学运算
    在当前函数中,需要接收三个参数,前两个为数值,最后一个为函数
    x, y: int
    f: function
    :return:
    """
    print(obj(x, y))

func(2, 3, pow)

# 执行结果
8

在日后使用这个函数的时候,就可以传入数值和要做什么计算的方法,就可以了。

当然,这个函数写的并不完善,比如,我们在执行func(2, 3, sum)的时候就会报错,原因是因为sum()函数内部是要进行迭代的的,然而int类型中没有魔法方法__iter__, 所以无法迭代。所以,要想这个函数具有通用性,还需要在内部完成很多工作。

闭包函数

之前我们在回调函数中将函数作为参数进行了传递,那么问题来了,既然函数能作为参数进行传递,那能不能作为参数被return呢?

def person():
    money = 0
    def work():
        print(money)
    return work

person()

# 执行结果
<function __main__.person.<locals>.work()>

我们可以看到,work函数被成功返回出来了。但是并未继续执行, 因为其内部的print()没起作用。

我们用一个变量来接收这个返回的函数:

def person():
    money = 0
    def work():
        print(money)
    return work

res = person()
res()

# 执行结果
0

说明res接收到返回的work()函数,并且最后执行成功了。

好了,让我们继续为这个函数做一点什么,看看有什么变化。

def person():
    money = 0
    def work():
        nonlocal money
        money += 100
        print(money)
    return work

res = person()
res()

# 执行结果
100

这个结合前几节所讲的内容就很好理解了对吧? nonlocal关键字拿到上一层函数定义的变量,然后在内层函数中进行使用,最后打印出来。

那我们继续执行会如何?让我们多执行几次:

# 定义一个函数
def person():
    money = 0  # 函数中定义了一个局部变量
    # 定义内函数
    def work():
        nonlocal money   # 在内函数中使用了外函数的临时变量
        money += 100
        print(money)
    # 在外函数中返回了内函数,这个内函数就是闭包函数
    return work

res = person() # return work  res = work
res() # res() == work()
res()
res()
res()
res()
# 此时 就不能够在全局中对money这个局部变量进行任何操作了,
# 闭包的作用:保护了函数中的变量不受外部的影响,但是又能够不影响使用

你会不会认为会一直打印100? 让我们看看执行结果到底是怎样的:

100
200
300
400
500

怎么样,是不是完全没想到?这个就是闭包函数的特点。

在一个函数内返回了一个内函数,并且这个返回的内函数还使用了外函数中局部变量,这个就是闭包函数。其特点为:

  • 在外函数中定义了局部变量,并且在内部函数中使用了这个局部变量
  • 在外函数中返回了内函数,返回的内函数就是闭包函数
  • ⚠️ 主要在于保护了外函数中的局部变量,既可以被使用,又不会被破坏。
  • 检测一个函数是否为闭包函数,可以使用func.__closure__,如果是闭包函数返回cell

匿名函数 -- lambda表达式

首先,我们先弄清楚什么是匿名函数: 匿名函数的意思就是说可以不使用def来定义,并且这个函数也没有名字。

在Python中,我们可以使用lambda表达式来定义匿名函数。我们需要注意,lambda仅仅是一个表达式,并不是一个代码块,所以lambda又称为一行代码的函数。

lambda表达式中,也有形参,并且不能够访问除了自己的形参之外的任何数据,包括全局变量。其语法如下:

# 语法:
lambda [参数列表]:返回值

让我们来尝试写写看,我们先来定义一个普通的加法运算的函数:

# 封装一个函数做加法运算
# 普通函数
def sum(x, y):
    return x+y

print(sum(2, 3))

毫无疑问,执行结果为5。那么接下来,用lambda该怎么写呢?

# 改成lambda表达式来封装
res = lambda x, y:x+y
print(res(4, 4))

# 执行结果
8

结合闭包函数的讲解,这里就应该很容易看懂了吧?一样的地方就是,使用了一个变量res来接收这个返回的函数,然后执行res函数。

让我们再来一段:

res = lambda sex:"很man" if sex=='male' else '很nice'
print(res('female'))

# 执行结果
很nice

只看结果的话,我们很清楚这段函数最后执行到了else语句内。但是是如何进入的呢?让我们将这段代码用普通函数的写法展开来看看:

def func(sex):
    if sex == 'male':
        return '很man'
    else:
        return '很nice'

func('female')

这样是不是很清晰了?回过头来让我们看刚才那段lambda表达式,我们可以这样去看:

#  lambda 参数列表: 真区间 if 表达式判断 else 假区间
lambda sex: '很man' if sex=='male' else '很nice'

# 然后用一个变量接收函数
res = lambda sex: '很man' if sex=='male' else '很nice'

所以可以看出来,其实lambda十分的方便,并且并不难理解,当你习惯了lambda之后,会非常便捷。

迭代器

迭代器是一个很有意思的功能,可以说是Python中最具特色的功能之一,它是访问集合元素的一种方式。

迭代器是一个可以记住访问遍历的位置的对象。从集合的第一个元素开始访问,直到集合中的所有元素被访问完毕。

迭代器只能是从前往后一个一个的遍历,不能后退。

我们把之前一直使用的range()拿过来看:

# range(10, 3, -1) 返回一个可迭代的对象
for i in range(10, 3, -1):
    print(i, end=" ")

# 执行结果
10 9 8 7 6 5 4 

表面上来看,似乎range()本身就是一个迭代器,可是我们来尝试做个实验:

x = range(5)
print(next(x))

# 执行结果
TypeError: 'range' object is not an iterator

当我们尝试调用next()函数的时候报错了,被告知range不是一个迭代器。

那么,range不是迭代器,究竟是什么呢?这里我们就要先深入研究下迭代器的特性:

严格来说,迭代器是指实现了迭代协议的对象,迭代协议是指实现了iter方法并返回一个实现了next方法的迭代器对象,并通过StopIterator一场标识迭代完成。

iter()

iter()能把迭代的对象,转为一个迭代器对象,其参数为可迭代的对象(str, list, tuple, dict), 返回值为迭代器对象。其中需要注意的一点是:迭代器一定是一个可以迭代的对象,但是可迭代的对象并不一定是迭代器。

我们在迭代器上使用iter会得到相同的对象:

i = iter([1, 2, 3])
print(iter(i) is i)
print(id(iter(i)))
print(id(i))

# 执行结果
True
4425502096
4425502096

基于此,我们可以这样实现:

i = iter([1, 2, 3, 4])
list(zip(i, i))

# 执行结果
[(1, 2), (3, 4)]

next()

next()函数可以去调用迭代器,并返回迭代器中的下一个数据。

我们使用iter函数可以从任何可迭代对象中获取一个迭代器:

a = iter([1, 2, 3])
print(a)
print(next(a))
print(1 in a)

# 执行结果
<list_iterator object at 0x124111a50>
1
False

可以看到,我们使用iter函数可以从任何可迭代对象中获取一个迭代器。而且迭代器有个特点,即每次用完一个元素即消耗掉该元素,不会保留在迭代器中,也就是说,是一次性的。

res = iter([1, 2, 3, 4])
print(next(res), end=" ")
print(next(res), end=" ")
print(next(res), end=" ")
print(next(res), end=" ")
# print(next(res))
r = list(res)
print(r)

# 执行结果
1 2 3 4 []

可以看到,list里面已经空了。

我们用for来取值:

res = iter([1, 2, 3, 4])
for i in res:
    print(i, end=" ")

r = list(res)
print(r)

# 执行结果
1 2 3 4 []

for直接将迭代内的元素全部取完了,所以最后打印下一个值的时候也显示空了。所以我们可以得到迭代器的取值方案:

迭代器的取值方案

  • next():调用一次获取一次,直到数据被取完。
  • list():使用list函数直接取出迭代器中的所有数据。
  • for:使用for循环遍历迭代器的数据

总结一下:

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter()next()

字符串,列表或元组对象都可用于创建迭代器:

那么,再回过头来看看range

range可以像任何其他可迭代对象一样循环使用,但是它并不具备迭代器中的一些特性,比如,我们之前实验过,range并不能使用next方法,而我们可以从range中得到一个迭代器:

iter(range(3))

# 执行结果
<range_iterator at 0x124184570>

我们在迭代器中使用元素就会消耗掉该元素,但是我们遍历一个range对象并不消耗它, 比如:

image-20230803185018932

很明显我们可以重复使用。

来一个更直接的,我们之前用for获取了迭代里的值,我们对range()也来使用一下看看会不会有不同的结果:

res = range(1,5)
for i in res:
    print(i, end=" ")

r = list(res)
print(r)

# 执行结果
1 2 3 4 [1, 2, 3, 4]

一样的代码,对象不同。我们可以明显看到区别,range拿到最后里面的元素并没有减少。这也说明了,range并不是迭代器。

实际上,range的迭代是通过iter协议来实现的,只是一种类似迭代器的鸭子类型,并非真正的迭代器。

其实,有一种可以直接检测迭代器和可迭代对象的方法:

# 检测迭代器和可迭代对象
from collections.abc import Iterator, Iterable 

varstr = '123456'
res = iter(varstr)
r = range(1, 5)

# isinstance() 检测一个数据是不是一个指定的类型
# Iterable: 迭代对象,Iterator: 迭代器
r1 = isinstance(varstr, Iterable) 
r2 = isinstance(varstr, Iterator)
r3 = isinstance(res, Iterable)
r4 = isinstance(res, Iterator) 
r5 = isinstance(r, Iterable) 
r6 = isinstance(r, Iterator)

print(f'varstr 是迭代对象:{r1}, \t varstr 是迭代器: {r2}')
print(f'res 是迭代对象:{r3}, \t res 是迭代器: {r4}')
print(f'r 是迭代对象:{r5}, \t r 是迭代器: {r6}')


# 执行结果
varstr 是迭代对象:True,      varstr 是迭代器: False
res 是迭代对象:True,      res 是迭代器: True
r 是迭代对象:True,      r 是迭代器: False

今天的知识点讲到这就结束了,接下来,让我们来做两个小练习。

练习题

递归查询斐波那契数列位数

还记得我们之前讲过的斐波那契数列吗?不记得没关系,我们来复习一下:

# 斐波那契数列: 0, 1, 1, 2, 3, 5, 8, 13...

我们这次来实现一个函数,用于查询斐波那契数列中当前位置的数值是多少。

# 递归实现斐波那契数列
def fibonacci(n):
    if n == 1:
        return 0
    elif n == 2 or n == 3:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

res = fibonacci(6)
print(res)

# 执行结果
5

我为大家画了张图,来看看程序内部到底做了些什么:

画板

从这张图中,我们可以看到递归的步骤和返回的结果。

递归实现阶乘

什么是阶乘?比如我们实现7的阶乘,那么就是
$$
1\times2\times3\times4\times5\times6\times7
$$
让我们来试着实现一下:

# 实现阶乘
def factorial(n):
    if n == 1:
        return 1
    else:
        return n*factorial(n-1)

res = factorial(7)
print(res)

# 执行结果
5040

验证一下看看:

print(1*2*3*4*5*6*7)

# 执行结果
5040

看来结果没问题,那让我们来看看程序内发生了什么:

'''
factorial(7) => 
7 * factorial(6) => 
    6 * factorial(5) => 
        5 * factorial(4) => 
            4 * factorial(3) => 
                3 * factorial(2) => 
                    2 * factorial(1) => 
                        factorial(1) = 1
                    2 * 1 = 2
                3 * 2 = 6
            4 * 6 = 24
        5 * 24 = 120
    6 * 120 = 720
7 * 720 = 5040
'''

虽然实现了,最后还是不得不说几点注意事项:

递归函数的效率并不高,所以尽量能不用就不要用。

一个函数如果调用后没有结束,那么栈空间中就一直存在,直到这个函数运算结束才会销毁。

好了,今天的课程到此结束。大家课后记得多练习。下课!