【2.0】流程控制工具
前言
除了上一部分使用的的 while
语句,Python 中也会使用其他语言中常见的流程控制语句,只是稍有变化。
流程控制语句
while
语句
语句格式如下所示:
1 | # 这是基本语法格式 |
注:执行过程语句的缩进是必要的且要缩进一致
if
语句
这是编程中最基本也是最常用的语句之一了,代码原型:
1 | # 最基本的用法 |
可以有零个或多个 elif
1部分,以及一个可选的 else
部分。
注:python 并不支持 else if 全称写法
for
语句
Python 的for
语句和其他编程语言不太相同,Python 并不是设置步长,循环条件来完成for
循环。反而是对任意序列进行迭代(例如列表或字符串),条目的迭代顺序与它们在序列中出现的顺序一致。 代码示例:
需要注意的是在遍历列表的时候,不要对列表内容进行修改;如果一定需要,可以选择复制一份副本来进行操作,否则修改列表将会导致循环次数等出现不可预知的问题
break
语句和continue
语句
break
语句,和 C 中的类似,用于跳出最近的 for
或 while
循环。
continue
语句也是借鉴自 C 语言,表示继续循环中的下一次迭代。
pass
语句
pass
语句什么也不做。当语法上需要一个语句,但程序需要什么动作也不做时,可以使用它。例如:
1 | while True: |
注:执行这段代码会出现忙等情况或者出现死循环情况,使用Ctrl + C 来结束这种情况
这通常用于创建最小的类:
1 | class TempClass: |
相关常用函数
range()
函数
如果你确实需要遍历一个数字序列,内置函数 range()
会派上用场。它会生成一个整数型序列,代码示例:
当然,你也可以指定步长来生成序列,其语法格式为:range(初始值,最终值,步长)
,代码示例:
如果你只打印range()
函数,则会出现奇怪的结果:
1 | print(range(3)) |
这是因为range()
所返回的对象在许多方面表现得像一个列表,但实际上却并不是。此对象会在你迭代它时基于所希望的序列返回连续的项,但它没有真正生成列表,这样就能节省空间。
我们称这样对象为 iterable2,也就是说,适合作为这样的目标对象:函数和结构期望从中获取连续的项直到所提供的项全部耗尽。 我们已经看到 for
语句就是这样一种结构,而接受可迭代对象的函数的一个例子是 sum()
:
1 | sum(range(3)) |
最后,也许你会很好奇如何从一个指定范围内获取一个列表。 以下是解决方案:
1 | list(range(3)) |
再后面的部分会详细说明
list()
函数的细节
自定义函数
我们在前面使用例如print()
函数是 Python 提供的基本库的函数,同样的我们也可以自定义我们自己的函数,现在我们来定义一个交互两个变量值的函数,代码示例:
1 | def ExchangeValue(a,b): |
运行结果:
在上面的示例中,使用关键字def
来表示定义一个函数,后面必须跟其函数名称和形式参数列表,格式例如:def 函数名称(形式参数1,形式参数2,...):
。其函数体的执行语句必须从下一行开始,需要缩进且一致相同缩进。
函数在执行的时候,优先使用的是函数内部的局部变量,例如我们提供的形式参数(以后简称形参),这就意味着如果形参和全局变量名称重复的时候,函数会优先使用形式参数的变量而不是全局变量。如果形参不存在则查找全局变量。
另外需要注意的是,形式参数是函数执行的时候的临时参数,在执行完成后只会对当时的形参做一定的操作,例如上述代码的交互函数,准确的来说它并不能实现相关功能,因为它只实现了形参变量的值交互,而实际上我们希望的变量a
和b
的值并没有交互,你可以尝试输出全局变量a
和b
的值来查看。
另外,函数也可以被其他变量指向并调用,代码示例:
1 | def OutPut(string): |
如果你学过其他语言,你可能会认为 OutPut
不是函数而是一个过程,因为它并不返回值。事实上,即使没有 return
语句的函数也会返回一个值,尽管它是一个相当无聊的值。这个值称为 None
。一般来说解释器不会打印出单独的返回值 None
,如果你真想看到它,你可以使用 print(函数调用)
来查看。
函数定义的更多形式
参数默认值
可以在创建函数的时候,给一定的形参设置默认值,这样在调用函数的时候,可以使用尽量少的参数调用函数,代码示例:
1 | def OutPut(a,b = "这是结束语句",c="这是开头语句"): |
这个简单的输出函数,可以通过如下几种方式调用:
- 只提供必要参数
a
,即OutPut("你好世界")
- 只提供必要参数和一个指定参数,即
OutPut("你好,世界","这是自定义结束语句")
- 也可以选择全部重新提供,即
OutPut("你好,世界","这是自定义结束语句","这是自定义开头语句")
需要注意的是,函数形参默认值是在定义处计算好的,也就是说对于下面的代码示例来说,其输出的值为5
而不是6
1 | i = 5 |
另外还需要注意的是,默认值只会执行一次。这条规则在默认值为可变对象(列表、字典以及大多数类实例)时很重要。比如,下面的函数会存储在后续调用中传递给它的参数:
1 | def f(a, L=[]): |
其输出结果为:
1 | [1] |
如果你不想要在后续调用之间共享默认值,你可以这样写这个函数:
1 | def f(a, L=None): |
关键字参数
在传递函数参数的时候,我们可以通过形如key = value
的方式来传递关键字,这种方法这使得我们可以指定参数的顺序,因为它们不再依赖于它们在函数定义中的位置。例如上面的代码:
1 | def OutPut(a,b = "这是结束语句",c="这是开头语句"): |
如果我们只希望传入参数a
和c
的值,我们可以直接使用关键字参数传递,而不用管形参c
在函数中的位置:
1 | OutPut("参数a",c="参数c") |
特殊参数
默认情况下,我们函数定义的参数传递是可以使用位置来传递,也可以使用关键字来传递,现在可以使用参数/
和*
来限定函数的参数传递是通过位置还是关键字来传递。代码示例:
1 | def Func(arg1,arg2,/,temp,*,kwd1,kwd2): |
其中我们在第一个**特殊参数/
前存在两个参数:arg1
和arg2
;在*
**后有两个参数:kwd1
和kwd2
。
仅限位置参数
使用特殊参数/
表示参数arg1
和arg2
只能通过他们位置的先后来传递参数,而不可以通过关键字传递,例如这样是非法的:Func(arg1="测试",arg2="test")
。
仅限位置形参要放在 /
(正斜杠) 之前。 这个 /
被用来从逻辑上分隔仅限位置形参和其它形参。 如果函数定义中没有 /
,则表示没有仅限位置形参。在 /
之后的形参可以为 位置或关键字 或 仅限关键字。
仅限关键字参数
使用特殊参数*
表示参数kwd1
和kwd2
只能通过关键字来传递参数,而不可以通过其形参的先后顺序来定义,例如这样是非法的:Func(省略前面的部分,"测试","test")
。
指明该形参必须以关键字参数的形式传入,**应在参数列表的第一个仅限关键字形参之前放置一个 *
**。
总结
- 如果你希望形参名称对用户来说不可用,则使用仅限位置形参。 这适用于形参名称没有实际意义,以及当你希望强制规定调用时的参数顺序,或是需要同时收受一些位置形参和任意关键字形参等情况。
- 当形参名称有实际意义,以及显式指定形参名称可使函数定义更易理解,或者当你想要防止用户过于依赖传入参数的位置时,则使用仅限关键字形参。
- 对于 API 来说,使用仅限位置形参可以防止形参名称在未来被修改时造成破坏性的 API 变动。
任意的参数列表
最不常用的选项是可以使用任意数量的参数调用函数。这些参数会被包含在一个元组里。在可变数量的参数之前,可能会出现零个或多个普通参数。
一般来说,这些 可变参数
将在形式参数列表的末尾,因为它们收集传递给函数的所有剩余输入参数。例如:
1 | def concat(*args, sep="/"): |
*
运算符:在函数定义时使用单个星号作为前缀,可以捕获任意数量的位置参数并将它们封装到一个元组中。
解包参数列表
Unpacking arguments list(解包参数列表)是指将一个可迭代对象(如列表、元组)作为函数参数,并使用 * 运算符将它们拆分为单独的位置参数。这种技巧允许我们在调用函数时更方便地传递多个参数。
例如:我们有一个函数 my_func()
,它接受三个参数:a
、b
和 c
。现在,如果我们有一个长度为 3 的元组 (1, 2, 3)
,我们可以使用解包参数列表来将该元组的值分别传递给函数参数:
1 | def my_func(a, b, c): |
在上面的代码中,我们首先定义了一个函数 my_func()
,它需要三个参数。然后,我们创建一个元组 params
并将其赋值为 (1, 2, 3)
。最后,我们调用函数 my_func()
并在元组前面使用 * 运算符以解包参数列表的形式传递参数。
这样,元组中的每个元素都被视为单独的位置参数,并传递给 my_func()
函数。因此,函数将输出以下内容:
1 | a = 1 |
这种方法非常方便,因为它允许我们在调用函数时轻松地传递多个参数,而不必手动将它们打包成列表、元组或字典。同时,在函数定义时使用默认参数和关键字参数的情况下,我们可以通过解包参数列表来跳过其中某些参数并只传递特定的参数。
Lambda 表达式
lambda 表达式是一种创建匿名函数的方式。它通常用于需要一个简单的函数来完成某些特定的任务,而不必定义一个完整的函数。
**lambda 表达式的语法很简单:lambda 参数列表: 表达式(返回值)
**,该表达式的结果就是这个匿名函数的返回值。代码示例:
1 | # 定义一个 lambda 表达式,将两个数字相加并返回 |
在这个例子中,我们定义了一个匿名函数 add
,它接受两个参数 x
和 y
,并返回这两个参数的和。然后我们调用这个函数来计算 3 和 5 的和,结果为 8。
注:lambda 表达式只能包含一条表达式,而不能包含多条语句。如果要编写更复杂的函数,还是需要使用普通的函数定义方式。
文档字符串
文档字符串(Docstrings)是一种注释格式,用于描述函数、类或模块的用途、参数、返回值等信息。文档字符串位于代码块的开头,紧跟在 def
语句或其他定义语句之后,并使用三个引号 """
包围。代码示例:
1 | def my_function(arg1, arg2): |
在这个例子中,我们使用了文档字符串来描述 my_function()
函数的作用、参数和返回值。文档字符串的格式通常包括以下几个部分:
- 第一行:简要介绍函数的作用。
- 空行:用于将第一行与后面的详细描述区分开来。
- 参数:列出该函数的所有参数及其说明。
- 返回值:描述函数的返回值类型及其意义。
文档字符串是编写可读性高、易于理解的代码的重要工具,也是编写文档的好习惯。
注:我们可以使用
help()
函数来查看对应函数的文档字符串
编写习惯
这是一个约定俗成的习惯,仅仅是一些建议。现在你将要写更长,更复杂的 Python 代码,是时候讨论一下代码习惯了。这些习惯是为了保持一种非常易读且令人赏心悦目的编码风格。每个Python开发人员都应该在某个时候阅读它;以下是最重要的几个要点:
使用4个空格缩进,不要使用制表符3。
4个空格是一个在小缩进(允许更大的嵌套深度)和大缩进(更容易阅读)的一种很好的折中方案。制表符会引入混乱,最好不要使用它。
换行,使一行不超过79个字符。
这有助于使用小型显示器的用户,并且可以在较大的显示器上并排放置多个代码文件。
使用空行分隔函数和类,以及函数内的较大的代码块。
如果可能,把注释放到单独的一行。
使用文档字符串。
在运算符前后和逗号后使用空格,但不能直接在括号内使用:
a = f(1, 2) + g(3, 4)
。以一致的规则为你的类和函数命名;按照惯例应使用
UpperCamelCase
来命名类,而以lowercase_with_underscores
来命名函数和方法。如果你的代码旨在用于国际环境,请不要使用花哨的编码。Python 默认的 UTF-8 或者纯 ASCII 在任何情况下都能有最好的表现。
同样,哪怕只有很小的可能,遇到说不同语言的人阅读或维护代码,也不要在标识符中使用非ASCII字符。
End
现在你已经掌握基本的语法和控制语句,后面会介绍一些其他的数据结构和更多的用法和拓展。