当前位置: 首页 > ds >正文

python23-函数返回值和参数处理,变量作用域

课程:B站大学
记录python学习,直到学会基本的爬虫,使用python搭建接口自动化测试就算学会了,在进阶webui自动化,app自动化

函数返回值和参数处理,变量作用域

  • 函数返回值与参数处理
  • 函数思想
    • 函数高内聚
    • 函数低耦合
    • 高内聚低耦合优点
  • 函数返回值
  • 参数传递
    • 位置参数
    • 关键字参数
    • 默认值参数
    • 可变参数
    • 可变位置参数
    • 可变关键字参数
    • 混合参数
  • 变量作用域
    • 局部变量
    • 全局变量
    • 局部变量的优点:
    • 局部变量的缺点:
  • 实践是检验真理的唯一标准


函数返回值与参数处理

简介
在前面讲解了函数的基本定义和使用,通过一段时间的使用,对函数的使用有了一定的了解。

但是实际函数使用时,基本的定义和调用还远远没有达到函数的使用要求。

函数的主要作用是实现模块化代码,减少代码在程序中的冗余,提高代码的复用率。

函数通过接收一定的数据,根据业务逻辑进行相应的处理,然后将处理结果返回给调用者。

函数思想

实现函数要基于高内聚,低耦合的思想。

函数高内聚

高内聚低耦合指的是函数内部的代码要有高内聚性,而函数之间要保持低耦合性。

高内聚性指的是函数内部的代码逻辑和功能彼此相关,实现的是一个具体的功能或目标,各个部分之间密切配合。

一个高内聚的函数通常只完成一个明确的任务,并且其内部的代码逻辑清晰、简洁。

函数低耦合

低耦合性指的是函数之间的依赖关系较弱,相对独立,一个函数的改动不会对其他函数造成过多的影响。

低耦合的函数在设计上应该尽量减少使用全局变量或直接修改其他函数的参数,而是通过参数传递和返回值来进行数据交互。

高内聚低耦合优点

通过遵循函数的高内聚低耦合原则,可以带来以下优点:

  • 可维护性:函数内部的代码高度相关和一致,使得函数更易于理解、调试和修改,减少了不相关的代码耦合。
  • 可测试性:函数的高内聚性使得它们更易于独立地进行单元测试,因为它们具有清晰的功能和输入输出。
  • 可复用性:函数之间的低耦合性使得它们可以在不同的上下文中被重复使用,提高了代码的复用性。
  • 灵活性:函数之间相互独立且功能明确,使得系统更容易进行扩展和修改,不会对其他函数产生很大的影响。

在设计函数时,应该关注其功能的内聚性,确保函数只完成一个特定的任务,并且将其功能按照模块化的方式进行拆分。同时,尽量减少函数之间的直接依赖和共享状态,通过参数传递和返回值来进行数据交互,以减少函数之间的耦合度。

通过保持函数的高内聚低耦合,能够提高代码的可维护性、可测试性和可复用性,同时使得系统更具灵活性和可扩展性。

函数返回值

在设计一个函数时,可以通过函数的返回值,将函数功能的处理结果,返回给调用者。

而一个真正完整的函数,需要返回值将结果返回给调用者,而不是直接通过 print 函数将其输出。

比如,存在一个可以查找列表中最大数值的函数,而刚好调用者有需求在一个列表中要找到最大值,并且进行后续操作。

如果此时查找最大值函数没有返回结果而是直接输出的话,对于调用者来说,该函数没有任何意义。

Python 中使用 return 返回函数的结果,同时,return 也具有结束函数的作用。

def show():print("循环前输出内容")for i in range(10):print(i)if i == 2:returnprint("循环后输出内容")print("函数调用前输出内容")
show()
print("函数调用后输出内容")

上面的代码中,在循环变量 i 值为 2 时,执行了 return ,此时函数会立即结束,返回到函数调用语句处继续向后继续执行。

当通过 return 结束函数执行时,实际默认返回了一个 None 值。

函数只能返回一个值
一个函数只能返回一个结果值,当需要返回多个值时,需要进行组包处理。

def getString():s = input("请输入一个字符串:")return sresult = getString()
print(result.upper())

此函数用来从键盘获取一个字符串,并返回给了调用者。调用者得到结果后进行了大写处理。

如果有两个或更多的值想返回,都写在 return 后行不行呢?

def getTwoNum():a = int(input("请输入第一个数字:"))b = int(input("请输入第二个数字:"))return a, bm, n = getTwoNum()
print(m, n)

通过代码发现,函数执行结果和前面介绍的理论并不符合,但实际上,该函数返回的依然是一个值,只是 Python 在执行时,自动做了组包和解包操作,将多个值默认包装成一个元组返回。

def getTwoNum():a = int(input("请输入第一个数字:"))b = int(input("请输入第二个数字:"))return a, bresult = getTwoNum()
print(result)
print(type(result))

通过代码执行结果发现,使用一个变量,也可以接收返回的两个数据,并且变成了一个元组。这就是组包操作。

组包与解包
组包是指将多个值赋值给一个变量时,会将多个值做为一个元组的元素,然后将元组赋值给变量。

解包是指将一个容器变量的数据赋值给多个变量时,Python 会将容器类型中的每个元素取出依次赋值给对应的变量。

解包时容器类型中的元素个数要与被赋值变量的个数相同。

示例:

# 组包
nums = 1,2,3,4,5
print(nums)
print(type(nums))# 解包
a,b,c,d,e = nums
print(a,b,c,d,e)

通过代码可以发现,当将多个值,同时赋给一个变量时,Python 会进行自动组包操作,将所有的数字组合成一个元组,再将元组赋值给变量。 当使用一个元组为多个变量进行赋值时,Python 会将元组中的元素值,依次赋值给变量,这称为解包操作。

由此可以看出,上一小节中的函数依然返回的是一个结果值,结果值的类型是一个元组。

多个return语句
在一个函数中,允许有多个 return 语句,可以实现在不同的条件下,返回不同的值,但同一时刻,只能有一个 return 语句被执行。

def multiReturn():return 1return 2return 3return 4return 5print(multiReturn())
print(multiReturn())
print(multiReturn())
print(multiReturn())
print(multiReturn())

通过代码发现,虽然函数提供了 5 条 return 语句,并返回了不同的结果值,在调用时,也调用了五次,但实际,5 次的调用结果都是相同的,都为 1 。

参数传递

参数是指函数在完成特定功能时,需要外部传递的数据。

比如,现实生活中去餐厅吃饭点的菜单,打车时告诉司机的目的地,就医时告诉医生的症状等这些都是参数。

回到程序中,前面用过很多次的 len() 方法,这个方法用来对指定的数据返回其长度或元素个数,那么这个函数的功能是用来获取长度,测谁的长度,该函数并不关心,只要调用者指定的数据可测,函数就返回一个长度,而函数并不关心被测试的数据是什么。

再回到打车的例子,出租车只负责将人从一个地方拉到另一个地方,司机只关心出发地和目的地信息,而具体是谁去坐车,坐车人什么身份,为什么在出发地,去目的地做什么,这些司机都不关系,司机只负责将人拉到目的地,任务就完成了。

在程序中,函数调用时指定数据的过程,称为参数传递,这时有四个概念,如下:

  • 主调函数:主动调用其它函数执行的称为主调函数。
  • 被调函数:被动调用执行的函数称为被调函数。
  • 实际参数:在调用函数的时候,函数名称后面括

号中的数据即为实际参数,简称实参,通俗讲就是实际值。
形式参数:在定义函数的时候,函数名称后面括号中的变量即为形式参数,称称形参,通俗讲就是一个记号。
在调用函数时,实参数据会依次传递给形参。

示例:

# name, age, gender 为形参
def info(name, age, gender):print(f"我叫{name}, 年龄{age}岁,性别{gender}")# 调用时的数据为实参
info("Tom", 22, "男")
info("Rose", 23, "女")

位置参数

位置参数也称为必备参数,必须按照正确的顺序传到函数中,即调用时的数量和位置必须 和定义时是一样的。

数量必须与定义时一致 在调用函数时,指定的实际参数的数量必须与形式参数的数量一致,否则将抛出 TypeError 异常,提示缺少必要的位置参数 missing x required positional argument。
位置必须与定义时一致 在调用函数时,指定的实际参数的位置必须与形式参数的位置一致,否则将抛出 TypeError 异常或者结果与预期不符的情况,例如入参的位置 1 需要一个 int 类型参数,而入参 2 位置需要一个 str 类型的参数,如果传递的位置不正确,那么 str 类型数据传递进去之后会报类型错误的异常
位置参数是会按照顺序将实际函数对应传递给形式参数。

示例:

def printMsg(n, msg):for i in range(n):print(f'第{i+1}次输出{msg}')# 正确使用位置参数
printMsg(5, "Hogworts")
# 错误使用位置参数
printMsg("Hogworts", 5)

关键字参数

关键字参数是指使用形式参数的名字来确定输入的参数值,通过该方式指定实际参数时,不再需要与形式参数的位置完全一致,只要将参数名写正确即可,这样可以避免用户需要牢记参数位置的麻烦,使得函数的调用和参数传递更加灵活方便。

示例:

def printMsg(n, msg):for i in range(n):print(f'第{i+1}次输出{msg}')# 关键字参数
printMsg(n=5, msg="Hogworts")
printMsg(msg="Hogworts", n=5)

默认值参数

在定义函数时,形参可以定义变量一样进行赋值,这个值就是默认该参数的默认值。

调用函数时,如果指定了该参数的数据,则使用指定的数据,如果没有指定该参数的数据,则使用默认值的数据。

示例:

# 实现一个乘方函数
def my_power(m, n=2):return m ** n# 使用指定的参数
print(my_power(2, 3))
# 使用默认值参数
print(my_power(2))

需要注意的是,指定默认值的形式参数必须放在所有未指定默认值参数的后面,否则会产生语法错误

def show(a, b=2, c):print(a, b, c)

可变参数

在程序运行过程中,所定义的函数,可能不能确定需要接收多少参数,有可能很多,有可能很少甚至没有。

此时,就不能通过定义固定个数的参数来接收数据,这会产生两个问题。

如果实参比形参多,则没有足够的形参支接收保存实参数据,如果实参比形参少,则形参接收不到数据无法进行函数的数据处理。

Python 中提供了可变参数的概念用来解决参数个数不确定的的情况。

可变位置参数

Python 使用 *args 参数做为形参,接收不确定个数的位置参数。
*args 将接收到的任意多个实际参数放到一个元组中。
示例:

# 不确定个数数字求和
def my_sum(*args):print(args)print(*args)print(type(args))s = 0for i in args:s += iprint(s)print("*" * 10)my_sum(1,2,3)
my_sum(1,2,3,4)
my_sum(1,2,3,4,5)
my_sum(1,2,3,4,5,6)

可变关键字参数

Python 使用 **kwargs 参数做为形参,接收不确定个数的关键字参数。
**kwargs 将接收到的任意多个实际参数放到一个字典中。
示例:

# 定义可变关键字参数
def print_info(**kwargs):print(kwargs)print(type(kwargs))for k,v in kwargs.items():print(k, v)print("*" * 10)print_info(Tom=18, Jim=20, Lily=12)
print_info(name="tom",age=22,gender="male",address="BeiJing")

混合参数

当定义函数时,参数列表中出现了多种类型的参数,定义时需要注意参数的定义顺序,如果顺序使用不正确,在调用函数时,可能会报错。

正确顺序的定义格式为:

def funcname(位置参数,可变位置参数,默认值参数,可变关键字参数):pass

示例:

def info(name1, name2, *args, age1=18, age2=21, **kwargs):print("Positional Arguments:")print("name1:", name1)print("name2:", name2)print("args:", args)print("Keyword Arguments:")print("age1:", age1)print("age2:", age2)print("kwargs:", kwargs)info("Alice", "Bob", "Charlie", "Dave", age2=30, occupation="Engineer", city="New York")
info("Alice", "Bob", "Charlie", "Dave", age1=25, age2=30, occupation="Engineer", city="New York")

一般函数在不确定参数的情况下,会将上面的形式简化定义,用来接收任意数量的参数。

def funcname(*args, **kwargs):pass

在此定义形式中,使用 *args 接收所有的位置参数,使用 **kwargs 接收所有的关键字参数。

变量作用域

作用域就是一个 Python 程序可以直接访问命名空间的正文区域。

在一个 Python 程序中,直接访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有 4 种,分别是:

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量,一般在闭包中出现。
  • G(Global):当前脚本的最外层,比如当前模块的全局变量。
  • B(Built-in): 包含了内建的变量/关键字等,最后被搜索。
  • 查找规则顺序: L –> E –> G –> B。

在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。

根据作用域的不同,变量可分为局部变量和全局全量。

局部变量

局部变量是指在函数内部定义并使用的变量,它只在函数内部有效,即函数内部的名字只在函数运行时才会创建,在函数运行之前或者运行完毕之后,所有的名字就都不存在了。

所以,如果在函数外部使用函数内部定义的变量,就会抛出 NameError 异常。

示例:

def show():# 局部变量a = 10print(a)# 在函数外无法访问局部变量a
# print(a)
# 只能通过调用函数使用局部变量
show()
# 函数外无法访问局部变量a
# print(a)

定义在函数内部的局部变量,只在函数内部可以被访问。局部变量在函数被调用时被创建,在函数执行结束后被销毁。

全局变量

定义在函数外之外的变量为全局变量,全局变量可以在当前程序文件的任何位置进行访问 。

示例:

# 全局变量定义
m = 20
def show1():# 使用全局变量print("show1:", m)def show2():# 使用全局变量print("show2:", m)# 使用全局变量
print(m)
show1()
show2()
# 使用全局变量
print(m)

当全局变量和局部变量同名时,根据作用域的查找规则顺序,内部变量会屏蔽全局变量。

示例:

# 全局变量定义
m = 10
def show():# 局部变量与全局变量同名m = "ABC"# 使用局部变量print("show:", m)# 使用全局变量
print(m)
show()
# 使用全局变量
print(m)

从上面的代码中可以看出,如果想在函数内部修改全局变量时,会被解释成同名变量屏蔽,此时,需要使用 global 关键字

示例:

# 全局变量定义
m = 10
def show():# 声明此变量是函数外定义的全局变量global m# 修改全局变量的值m = "ABC"# 使用全局变量print("show:", m)# 使用全局变量
print(m)
show()
# 使用全局变量
print(m)

全局变量和局部变量的优缺点
全局变量的优点:

全局变量在整个程序中都可访问,方便在不同的函数或模块之间共享数据。
全局变量可以保存程序运行期间的状态,比如计数器或状态标志。
全局变量的缺点:

全局变量的作用范围很大,容易被误修改,导致程序出现错误。
在多个函数中使用同名的全局变量时,很容易出现混淆和命名冲突的问题。
全局变量的使用不宜过多,因为它们会占用内存资源。如果程序中有太多的全局变量,会增加维护和调试的难度。

局部变量的优点:

局部变量的作用范围限于所在的函数或代码块中,不会对其他函数或模块造成影响。这样可以防止变量的误修改或命名冲突。
局部变量仅在需要时才会被创建和销毁,节省了内存资源。

局部变量的缺点:

局部变量的作用范围有限,不能在其定义范围外直接访问。如果需要在多个函数之间共享数据,就不能使用局部变量,需要采用其他的方式来传递数据。

当函数中定义了较多的局部变量时,可能会使代码显得冗长和难以阅读,增加了编写和维护的复杂性。

如果需要在多个函数中共享数据,可以选择使用全局变量;如果只在某个函数内部使用的变量,可以选择使用局部变量。
在这里插入图片描述

实践是检验真理的唯一标准

http://www.xdnf.cn/news/4736.html

相关文章:

  • 记录学习的第三十五天
  • 2025-05-08-如何在一次 cmd 会话中批量设置多个 API key?
  • 英文论文查重笔记
  • 用3D slicer 去掉影像中的干扰体素而还原干净影像(脱敏切脸处理同)
  • 按拼音首字母进行排序组成新的数组(vue)
  • 强人工智能是否会诞生于现在的AI之中
  • 第二章 MySql
  • lc3341. 到达最后一个房间的最少时间 Ⅰ 算法解析
  • Red Hat linux环境openssh升级到openssh-10.0p1
  • FileInputStream
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(15):何と どういう
  • process-exporter服务安装并启动
  • 【C++游戏引擎开发】第32篇:物理引擎(Bullet)—约束系统
  • ollama+deepseek+openwebui安装
  • OrangePi Zero 3学习笔记(Android篇)2 - 第一个C程序
  • 创建需求跟踪矩阵5大常见步骤(附注意事项)
  • linux - shell脚本编程
  • 解锁 AI 生产力:Google 四大免费工具全面解析20250507
  • vue3+ts的watch全解!
  • 登顶中国:基于 Trae AI与 EdgeOne MCP 的全国各省最高峰攀登攻略博客构建实践
  • 比较入站和出站防火墙规则
  • 使用Apache Spark在Java中进行日志分析
  • 如何快速获取旺店通奇门原始数据至本地
  • 掌握Multi-Agent实践(二):基于AgentScope构建支持@机制的群聊系统,实现定向对话机制
  • LeetCode 热题 100 322. 零钱兑换
  • CATIA高效工作指南——零件建模篇(二)
  • 多边形生成立面点云
  • Python理财应用-- A股指标对比 | AKShare【未完待续】
  • 【视觉基础模型-SAM系列-1】Segment Anything
  • std::atomic<bool>与bool的区别