首页/新闻资讯/正文详情

Python函数:def定义、参数体系、返回值与作用域

发布时间:2026/11/5 1:21:02 来源:云帆数科 栏目:资讯中心
Python函数:def定义、参数体系、返回值与作用域
函数是把一个流程打包成块的操作, 这个包是能够被多次重复使用的。更新时间是 2026-09-04。在上一篇的时候你已经掌握了关于流程控制的知识点, 具体来说, 那就是分支的情况以及循环的情况。如果发现某一段业务逻辑需要被重复地去进行使用, 或者是这段逻辑本身已经复杂到让人觉得有必要把它拆分成为多个更小的一些部分的话, 那么就应该采取一种办法, 就是将它打包成一个函数的形式呈现出来。这篇文章把def这个定义说清楚了, 然后也解释了参数是怎么传递的, 其中包括位置参数, 关键字参数, 默认参数, *args, 还有, 接着讲了返回值, 同时也说明了函数里面的变量作用是干什么的, 最后呢, 又从函数调用的开销这个角度, 还有局部变量为什么更快的这两个视角, 把本站的性能主线给接上了。相关的5个例子是在demos/-/这个目录下面的, 所有的输出都是在7或者3.6.8的版本里头真正运行测试过的, 结果是等于0的。一、定义与调用函数就是用 def 这个关键字来给一段逻辑操作起一个名字, 这样做的话以后就可以反复去调用它了, 具体的例子就像 demo 中展示的那样。def greet(name): # def 函数名(参数): 下一行缩进是函数体return f你好, {name} # return 把结果交回调用处print(greet(世界)) # 你好, 世界print(greet(alice)) # 你好, alice要点f greet # 赋值不加括号拿到函数本身print(f(bob)) # 你好, bobdef apply_twice(func, x): # 函数当参数传进去return func(func(x))print(apply_twice(greet, 嵌套)) # 你好, 你好, 嵌套「函数可以当作参数来传递」这个事实, 正是因为后面关于装饰器的基础内容——你要先记住这样的一个直觉感觉才行。二、参数, 它这个内容包含了那个位置方面的事, 还有关键字, 同时也包括了所谓的默认值。在demo这个例子里面, 参数的传递方式是相当灵活的。def sub(a, b):return a - bsub(10, 3) # 位置参数按顺序a10, b3 → 7sub(b3, a10) # 关键字参数按名字传顺序可乱 → 7def power(base, exp2): # exp 有默认值return base ** exppower(5) # 不传 exp用默认 2 → 25power(5, 3) # 传了就用 3 → 125最大的一个陷阱是, 千万不要使用那些能够被修改的对象作为默认的参数。def add_bad(item, lst[]): # [] 在函数定义时只创建一次lst.append(item)return lstprint(add_bad(1)) # [1]print(add_bad(2)) # [1, 2] —— 两次调用共享同一个列表这个默认值是在函数定义的时候就被创建好的, 然后在接下来每次调用的时候, 都是去复用同一个对象。如果把可变对象, 也就是列表或者字典当作默认值来用, 那么在多次调用的过程中, 它们之间会出现互相影响、互相污染的情况。正确的写法应该是先把默认值设成“无”或者说是没有具体东西的空状态符号None, 然后再进入这个函数体内部之后再去进行新的创建操作:def add_good(item, lstNone):if lst is None:lst []lst.append(item)return lstprint(add_good(1)) # [1]print(add_good(2)) # [2] —— 各自独立三、任意数量参数*args 与 **如果想让函数的接受的参数数量是不固定的, 那么就可以使用星号*以及双星号, 具体的演示代码在下面。def total(*nums): # *nums 把多余的位置参数收集成一个元组print(nums) # (1, 2, 3)s 0for n in nums:s nreturn stotal(1, 2, 3) # 6total(10, 20) # 30def show_info(name, **extra): # **extra 把多余的关键字参数收集成字典print(name, extra)show_info(alice, age30, city北京) # alice {age: 30, city: 北京}反过来, 在调用的那个时候呢, 也是能够把双星号这个符号给用上, 通过这个双星号, 就可以把那个列表啊或者是字典呢给拆开来, 以展开的方式传递进去去的。def sub2(a, b):return a - bargs [3, 5]sub2(*args) # 等价 sub2(3, 5) → -2def power2(base, exp2):return base ** exppower2(2, **{exp: 3}) # 等价 power2(2, exp3) → 8四、这个返回值是和函数里面使用的那个变量有关联的。然后是第4.1章节, 讲的是如何一次返回多个值, 这背后其实是一个元组的东西在起作用。def stats(a, b):return a b, a * b # 逗号分隔自动打包成元组s, p stats(3, 4) # 调用处解包print(s, p) # 7 124.其实传的是对象引用, 可变对象会被改。当函数的参数传入的是对象的引用的时候, 那么如果在函数的内部进行了修改可变对象的动作, 外部是能够看得见的如果是不可以变对象重新绑定的话, 那么就对外部不起任何影响demodef append_to(lst):lst.append(99) # 改的是同一个列表对象data [1, 2]append_to(data)print(data) # [1, 2, 99] —— 外部被改了def rebind(n):n n 1 # 不可变局部名 n 指向新对象外部无感x 10rebind(x)print(x) # 10这个情况和对象模型这一篇章节当中所讲述的内容, 也就是关于「」这个方法在处理可变类型数据的时候是直接进行原地修改, 而在处理不可变类型数据的时候则会重新建立一个新的对象, 这种情况其实是完全相同的本质。4.在函数里面进行的赋值操作, 就意味着这是局部变量。当函数里面给某一个名字赋值的时候, 它默认情况下就是局部变量了, 绝对不会去触动外面的全局变量。g_counter 100def use_local():g_counter 999 # 这是新建的局部变量不改全局return g_counterprint(use_local(), g_counter) # 999 100查找名字的规则, 现在的人还是按照 LEGB 这个顺序来理解, 也就是先从局部开始找, 然后找外层的, 接着找全局的, 最后才去内置那里看。如果确实想在函数内部去修改全局变量的值, 必须在代码里声明一下变量名字, 但是刚开始学编程的朋友们, 最好还是尽量少用全局变量, 多靠参数传递和返回值返回数据, 这样写出来的代码, 读起来会更清晰明白一些。五、从性能的视角来看, 调用操作是要花费时间成本的, 而使用局部变量其运行速度往往更快。函数这一手段能够使得代码变得更加清晰, 然而在某些特定的环境或者框架里面, 它并不是那种不需要付出代价就可以获取的东西。5.当执行函数调用的时候, 其实所做的操作就等同于去创建一个新的函数栈帧。每次函数调用的时候, 解释器都需要去新建一个栈帧对象, 把参数传进去, 等到执行完了以后再进行销毁操作并且返回结果。这种机制在C语言里面其实对应的是压栈行为, 压栈的开销很轻。但是在这里是在内存堆上面去建立对象, 所以产生的开销是非常明显的。对于同样的一些计算任务, 如果把它们放到热循环里面去反复调用函数的话, 和直接采用内联展开的方式来进行对比的话, 在实测过程中是可以观察到存在一定差距的, 具体的数据可以参考相关的演示材料进行的200万次的测试统计。text结果一致: True函数调用: 0.482s 内联: 0.326s 调用慢 1.48x全局查找: 0.201s 局部绑定: 0.153s 局部快 1.32x请你要清楚, 这不是告诉你去写函数还是不写函数的问题。实际上, 函数能够提供的清晰性、复用性和可测试性, 比那么一点点开销要重要得多。所以, 正确的说法应该是: 绝大多数的代码都可以放心地使用函数。只有那些真正属于热点内层循环的部分, 也就是已经确认过的极小一段代码, 才是考虑把极小的而且频繁发生的计算过程, 内联处理或者下沉到C语言层面的时候。这个结论也呼应了流程控制篇中提到的关于热循环下沉到C的观点。5.在计算机里面, 那个局部变量呢, 它是确实比全局变量要快一些这个情况的。在函数内部的时候, 对于局部的这种变量来说, 使用的是通过下标的方式去直接进行的访问操作, 而如果是关于全局变量的情形, 则是需要去借助字典按照名字来查寻。因此, 在对于那些频繁出现的热点循环场景进行处理的环节中, 应当把那些被非常反复地使用到的、并且属于那种全局性质的名称这里面包含了比如数学模块里面的开根号方法 sqrt , 以及一些内置函数的相关名称先提前绑定到局部变量的这种位置上, 这样一来速度就会变得更加的快速一些, 实际测试出来的结果是说局部变量的访问速度相比于其他方式要快大约 1.32 倍的幅度。import mathdef use_local():sqrt math.sqrt # 先绑到局部tot 0.0for i in range(1_000_000):tot sqrt(i 1) # 局部查找return tot这正是 01 对象模型篇提到的 LEGB 规则以及所谓的局部绑定微优化措施的实测版本。如果想要从字节码的底层层面去查看相关的区别, 建议大家去阅读一下关于字节码以及求值循环的内容。六、本部分的概念与咱们这里网站的性能主线之间的关系, 对应着这个主线衔接文档里面的内容。通过调用函数的方式创建新的堆栈帧时, 会存在成本损耗。解释器里面的那个栈帧, 还有呢就是C语言原本的那种栈。字节码和求值循环, 它们是有关联的两件事。局部变量 比全局快对名字进行解析, 并且做一点点的微优化。对象模型, 以及语法的基础部分。函数是一等对象, 可以把这个函数作为一个参数进行传递。高阶函数、装饰器就是那个生成器, 还有那个迭代器, 以及那个装饰器。传对象引用、可变对象被改引用语义、别名上下文管理器 with七、常见坑 一句话总结先是用 def 这个关键字把那一套逻辑给打包起来, 然后再弄成一个函数, 接着把这东西的结果给交出去然后参数可以按照位置来传也可以按照关键字来传还可以带上默认值再去传, 要是参数的个数变得不是固定的话那就用 *args , 这个东西是个元组 , 再用 / args 这种东西去装字典还有那个函数它本身就是一个一等的对象所以它也能够被当做参数去进行传递你传递的时候传的实际上是对象的引用那么当你去改变一个可变对象的时候外面的代码是能够看到这种变化的当你在调用函数的时刻确实是需要构建出一个栈帧的并且会伴随有一些开销, 另外局部变量的读取速度比起全局变量来会更加快速一点所以说对于那些普通的代码片段你可以尽管放心地去做抽象化的处理至于那些热点部分最后再进行内联操作和下沉的处理。代码位置: 配套 5 个纯标准库 demo 在 demos/-//~, 7。3.6.8, 实测等于0, bash .sh。一键复跑。动手进行练习。① 要求写一个函数, 这个函数的参数是 n, 作用是去判断数字是不是质数, 具体实现的时候需要复用前面流程控制章节里提到的循环 else 结构, 函数最终要返回 True 或者 False。② 要求写一个叫 avg 的函数, 这个函数带有一个用星号表示的可变数量参数 args, 意思是能接收任意多个数字, 然后要求计算并返回这些数字的平均值。③ 要求写一个函数, 用来接收一个列表类型的参数, 然后返回这个列表里的最大值和最小值, 这里需要注意处理两个返回值解包的问题。④ 要求故意写一个带有默认参数 x 的 def f 函数定义, 通过两次调用该函数来观察它们之间存在的共享现象, 然后把这个代码改成使用 None 作为默认值的版本进行对比。继续推进, 接下来的主题是关于字符串以及文本数据的处理技巧, 这里面包含了像f- 格式化这样的高级写法, 还有关于数据不可变性以及与编码相关的知识如果你迫切想要跳过基础, 直接去学习和掌握可读性更强的生成器、迭代器机制以及装饰器的运用方法, 那么你可以重点去阅读那篇介绍「如何将函数当作参数来传递」的文章, 看看它是怎样一步步进化形成如今的装饰器模式的, 同时也可以了解yield这个关键字是如何让函数具备惰性特征, 从而能够一串串地产生出数值的。

相关推荐

7 分钟产出可引导 OpenCore EFI:OpCore Simplify 完整上手指南
7 分钟产出可引导 OpenCore EFI:OpCore Simplify 完整上手指南

7 分钟产出可引导 OpenCore EFI:OpCore Simplify 完整上手指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore Simplify 读取一台 … · 2026/11/5 8:39:12

爆肝一个月,Java 从入门到精通25 - JavaBean
爆肝一个月,Java 从入门到精通25 - JavaBean

在Java中,有很多class的定义都符合这样的规范: 若干private实例字段;通过public方法来读写实例字段。 例如: public class Person {private String name;private int age;public String getName() { return this.name; }public… · 2026/11/3 11:28:15

给游戏加功能又怕弄坏文件?BepInEx 安装教程与排坑指南
给游戏加功能又怕弄坏文件?BepInEx 安装教程与排坑指南

给游戏加功能又怕弄坏文件?BepInEx 安装教程与排坑指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 想改游戏,又怕改坏?BepInEx 插件框架的思… · 2026/11/3 13:35:14

学术论文写作工具全解析:从LaTeX到Word公式编辑
学术论文写作工具全解析:从LaTeX到Word公式编辑

1. 论文写作工具全景概览学术写作从来不是单打独斗的过程。记得我博士期间为了赶一篇JCR一区论文,连续72小时对着Word文档调整公式格式的经历,让我深刻意识到专业工具的重要性。如今市面上涌现的论文辅助工具,早已从单纯的文字处理进化到涵盖… · 2026/11/5 8:38:17

美股量化分析:历史数据接入与处理实战
美股量化分析:历史数据接入与处理实战

1. 美股数据分析的基础:历史数据接入实战做美股量化分析的朋友都知道,历史行情数据是策略开发的基石。无论是技术指标回测、波动率统计,还是机器学习特征工程,数据质量直接决定了分析结果的可靠性。去年我在搭建美股量化分析系统时… · 2026/11/5 8:38:17

自动驾驶控制系统:PID与MPC的工程实践
自动驾驶控制系统:PID与MPC的工程实践

1. 自动驾驶控制系统的核心架构设计在自动驾驶车辆的运动控制中,横向和纵向控制是两个最基础也最关键的子系统。我参与的这套控制系统采用了经典的分层设计思路:纵向控制负责车辆的前后运动(加速/减速),横向控制则掌管… · 2026/11/5 8:38:17

ag-grid-community安装配置避坑:模块注册、样式与容器高度
ag-grid-community安装配置避坑:模块注册、样式与容器高度

简介:面向前端开发者与数据密集型后台项目维护者的 ag-Grid Community 开源数据网格资源包,适合需要表格排序、过滤、分组、分页、单元格编辑与自定义渲染的 JavaScript、React、AngularJS 及 Web Components 项目参考。压缩包共 2000 个文件&#xff0c… · 2026/11/5 8:38:17

自动驾驶横纵向混合控制架构与PID+MPC实现
自动驾驶横纵向混合控制架构与PID+MPC实现

1. 自动驾驶控制系统的核心架构在自动驾驶领域,横纵向控制是车辆运动控制的基础模块。纵向控制负责车辆的前后运动(加速/减速),横向控制则决定车辆的左右运动(转向)。这套系统相当于人类驾驶员手脚的配合—… · 2026/11/5 8:38:17

中望3D草图歪斜全解析:视图与几何偏移的区分与修复
中望3D草图歪斜全解析:视图与几何偏移的区分与修复

1. 草图视图歪了到底歪的是什么很多人第一次遇到这个问题时的反应是“视图转一下不就行了”,结果鼠标中键按住转了三百六十度,草图还是斜的,标注尺寸也跟着歪,最后画出来的线全是斜的。这里得先把一个概念掰清楚:中望3… · 2026/11/5 8:37:17

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

多智能体协作系统架构设计与实践优化
多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39

大模型系统性入门实战指南:从原理到部署微调的全链路路线
大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39

鸿蒙文件系统沙箱机制解析与开发实践
鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码