4. 流程控制语句
除了 while
语句,Python还拥有在其他语言里常见的控制语句,以及一些好玩的语句。.
4.1.if
语句
也许最常见的语句类型是 if
语句.例如:
可以有0个或多个 elif
块,,并且 else
块是可选的。关键字 ‘elif
‘ 比 ‘else if’ 短,,并且可以避免过度缩进。一个if
... elif
... elif
...序列 是对其他语言的switch
or case
语句的替代方案。
4.2. for
语句
Python 的 for
语句与你在 C 或者 Pascal 中使用的有一点区别。和常见的等差数列迭代(如 Pascal 中)或让用户能够自定义迭代步骤和停止条件(如 C)不一样,Python 的for
语句按照元素出现的顺序迭代任何序列(列表或字符串)例如(没有双关意):
如果要在循环内修改正在迭代的序列(例如,复制所选的项目),建议首先制作副本。迭代序列不会隐式地创建副本。 使用切片就可以很容易地做到:
4.3. range()
函数
如果你确实需要遍历一个数字序列,内置函数 range()
会派上用场。它会生成等差序列:
给定的结束点永远不会是生成序列的一部分;range(10)
生成 10 个值,一个序列的长度 10 的索引。也可以让 range 函数从另一个数值开始,或者可以指定一个不同的步进值(甚至是负数,有时这也被称为‘步长’):
遍历序列的范围,可以组合 range()
和 len()
,如下所示︰
然而,在大多数这类情况,一般使用 enumerate()
函数,请参见 循环技术。
如果你只打印 range,会出现奇怪的结果:
在很多方面由 range()
返回的对象的行为就像它是一个列表,但事实上它不是。它是一个对象,返回所需的序列连续项,当您遍历它,但它并不真的使列表中,从而节省了空间。
我们把这样的对象称为可迭代的,也就是说,它们适合被一些函数和构造器所利用,这些函数和构造器期望可以从可迭代对象中获取连续的元素,直到没有新的元素为止。 我们已经看到 for
语句是这种 迭代器。list ()
函数是另一个;它从可迭代量创建列表︰
后面我们会看到更多返回可迭代对象和以可迭代对象作为参数的函数。
4.4. break
和continue
语句,以及循环中else
子句
break
语句和 C 中的类似,用于跳出最近的 for
或 while
循环。
循环语句可以有一个 else
子句;当(for
)循环迭代完整个列表或(while
)循环条件变为假,而非由break
语句终止时,就会执行这个else语句。下面循环搜索质数的代码例示了这一点:
(是的,这是正确的代码。仔细看︰ else
子句属于 for
循环,不 是 if
语句的 。)
与在if
语句中的用法相比,循环中的else
子句与try
语句的else
子句有更多的共同点:try
语句的else
子句在未出现异常时运行,循环的else
子句在未出现break
时运行。更多关于try
语句和异常的内容,请参见 处理异常。
continue
语句,也是从C语言借来的,表示继续下一次迭代:
4.5.pass
语句
pass
语句什么也不做。它用于语法上必须要有一条语句,但程序什么也不需要做的场合。例如:
它通常用于创建最小的类:
另一个使用pass
的地方是编写新代码时作为函数体或控制体的占位符,这让你在更抽象层次上思考。pass
语句将被默默地忽略:
4.6. 定义函数
我们可以创建一个生成任意上界斐波那契数列的函数:
关键字 def
引入函数的定义。其后必须跟有函数名和以括号标明的形式参数列表。组成函数体的语句从下一行开始,且必须缩进。
函数体的第一行可以是一个可选的字符串文本;此字符串是该函数的文档字符串,或称为docstring。(更多关于 docstrings 的内容可以在 文档字符串一节中找到。)有工具使用 docstrings 自动生成在线的或可打印的文档,或者让用户在代码中交互浏览;在您编写的代码中包含 docstrings 是很好的做法,所以让它成为习惯吧。
执行 一个函数会引入一个用于函数的局部变量的新符号表。更确切地说,函数中的所有的赋值都是将值存储在局部符号表;而变量引用首先查找局部符号表,然后是上层函数的局部符号表,然后是全局符号表,最后是内置名字表。然而,在函数内部无法给一个全局变量直接赋值(除非在一个 global
语句中命名),虽然可以引用它们。
函数调用的实际参数在函数被调用时引入被调函数的局部符号表;因此,参数的传递使用 传值调用 (这里的 值 始终是对象的 引用,不是对象的值)。[1]一个函数调用另一个函数时,会为本次调用创建一个新的局部符号表。
函数定义会在当前符号表内引入函数名。函数名对应的值的类型是解释器可识别的用户自定义函数。此值可以分配给另一个名称,然后该名称也可作为函数。这是通用的重命名机制:
如果你使用过其他语言,你可能会反对说:fib
不是一个函数,而是一个过程(子程序),因为它并不返回任何值。事实上,没有return
语句的函数也返回一个值,尽管是一个很无聊的值。此值被称为 None
(它是一个内置的名称)。如果 None
只是唯一的输出,解释器通常不会打印出来。如果你真的想看到这个值,可以使用 print
语句:
写一个函数返回菲波那契数列的列表,而不是打印出来,非常简单:
此示例中,像往常一样,演示了一些新的 Python 功能:
return
语句可以从函数中携带着返回值返回。return
语句不携带任何表达式参数时返回None
。如果一直执行到整个函数结束还没有碰到 return 语句,也返回None
。- 语句
result.append(a)
调用了列表result
的一个 方法。A method is a function that ‘belongs’ to an object and is namedobj.methodname
, whereobj
is some object (this may be an expression), andmethodname
is the name of a method that is defined by the object’s type. 不同类型定义了不同的方法。不同类型的方法可能具有相同的名称,而不会引起歧义。(It is possible to define your own object types and methods, using classes, see Classes) The methodappend()
shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent toresult = result + [a]
, but more efficient.
4.7. 更多关于定义函数
可以定义具有可变数目的参数的函数。有三种函数形式,可以结合使用。
4.7.1. 默认参数值
最有用的形式是指定一个或多个参数的默认值。这种方法创建的函数被调用时,可以带有比定义的要少的参数。例如:
这个函数可以通过几种方式调用:
- 只提供必须的参数:
ask_ok('Do you really want to quit?')
- 提供可选参数中的一个:
ask_ok('OK to overwrite the file?', 2)
- 或者提供所有参数:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
上面示例中的in
关键字,它测试一个序列是否包含特定的值。
默认值在定义时段中执行函数定义时计算,因此:
会打印 5
.
重要的警告︰默认值是只计算一次。这在默认值是列表、字典或大部分类的实例等易变的对象的时候又有所不同。例如,下面的函数在后续调用过程中会累积传给它的参数:
这将会打印
如果你不想默认值在随后的调用中共享,可以像这样编写函数:
4.7.2. 关键字参数
Functions can also be called using keyword arguments of the form kwarg=value
. 例如,下面的函数:
接受一个必需的参数 (voltage
) 和三个可选参数 (state
, action
, and type
)。可以用下列任意一种方式调用这个函数:
但下面的所有调用将无效:
在函数调用中,关键字参数必须跟随在位置参数的后面。All the keyword arguments passed must match one of the arguments accepted by the function (e.g. actor
is not a valid argument for the parrot
function), 他们的顺序并不重要.This also includes non-optional arguments (e.g. parrot(voltage=1000)
is valid too). 任何参数都不可以多次赋值。下面的示例由于这种限制将失败:
When a final formal parameter of the form **name
is present, it receives a dictionary (see Mapping Types — dict) containing all keyword arguments except for those corresponding to a formal parameter. This may be combined with a formal parameter of the form *name
(described in the next subsection) which receives a tuple containing the positional arguments beyond the formal parameter list. (*name
must occur before **name
.) 例如,如果我们定义这样的函数:
它可以这样被调用:
并且当然它会打印:
Note that the list of keyword argument names is created by sorting the result of the keywords dictionary’s keys()
method before printing its contents; if this is not done, the order in which the arguments are printed is undefined.
4.7.3. Arbitrary Argument Lists
最后,最不常用的场景是指明某个函数可以被可变个数的参数调用。These arguments will be wrapped up in a tuple (see Tuples and Sequences). 在可变个数的参数之前,可以有零到多个普通的参数。
Normally, these variadic
arguments will be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function. Any formal parameters which occur after the *args
parameter are ‘keyword-only’ arguments, meaning that they can only be used as keywords rather than positional arguments.
4.7.4. Unpacking Argument Lists
当传递的参数已经是一个列表或元组时,情况与之前相反,你要分拆这些参数,因为函数调用要求独立的位置参数。For instance, the built-in range()
function expects separatestart and stop arguments. If they are not available separately, write the function call with the *
-operator to unpack the arguments out of a list or tuple:
In the same fashion, dictionaries can deliver keyword arguments with the **
-operator:
4.7.5. Lambda 表达式
可以使用 lambda
关键字创建小的匿名函数。此函数会返回两个参数的总和: lambda a, b: a+b
.。Lambda 函数可以用于任何需要函数对象的地方。在语法上,它们被局限于只能有一个单独的表达式。在语义上,他们只是普通函数定义的语法糖。像嵌套的函数定义,lambda 函数可以从其被包含的范围中引用变量:
上面的示例使用 lambda 表达式返回一个函数。 另一种用法是将一个小函数作为参数传递:
4.7.6. 文档字符串
下面是一些关于文档字符串内容和格式的惯例。
第一行永远应该是对象用途的简短、精确的总述。为了简单起见,不应该明确的陈述对象的名字或类型,因为这些信息可以从别的途径了解到(除非这个名字碰巧就是描述这个函数操作的动词)。这一行应该以大写字母开头,并以句号结尾。
如果在文档字符串中有更多的行,第二行应该是空白,在视觉上把摘要与剩余的描述分离开来。以下各行应该是一段或多段描述对象的调用约定、 其副作用等。
Python 解释器不会从多行的文档字符串中去除缩进,所以必要的时候处理文档字符串的工具应当自己清除缩进。这通过使用以下约定可以达到。第一行 之后 的第一个非空行字符串确定整个文档字符串的缩进的量。(我们不用第一行是因为它通常紧靠着字符串起始的引号,其缩进格式不明晰。)所有行起始的等于缩进量的空格都将被过滤掉。不应该存在缩进更少的行,但如果确实存在,应去除所有其前导空白。应该在展开制表符之后(展开后通常为8个空格)再去测试留白的长度。
这里是一个多行文档字符串的示例:
4.7.7. 函数注释
函数注释 are completely optional metadata information about the types used by user-defined functions (see PEP 484 for more information).
注释作为字典被储存在__annotations__
函数的属性中而且对函数任何其他部分的功能没有任何影响.参数注释用参数名后的冒号定义 , 冒号后面紧跟着一个用于计算注释的表达式.Return annotations are defined by a literal ->
, followed by an expression, between the parameter list and the colon denoting the end of the def
statement. 下面的示例包含一个位置参数,一个关键字参数,和没有意义的返回值注释。
4.8. 插曲:代码风格
若要编写更长更复杂的 Python 代码,是时候谈一谈 编码风格了 。大部分语言都可以有多种(比如更简洁,更格式化)写法,有些写法可以更易读。让你的代码更具可读性是个好主意,而良好的编码风格对此有很大的帮助。
对Python, PEP 8 已经成为多数项目遵循的代码风格指南;它推动了一种非常易于阅读且赏心悦目的编码风格。每个Python开发者都应该找个时间读一下; 以下是从中提取出来的最重要的一些点:
-
使用 4 个空格的缩进,不要使用制表符。
4 个空格是小缩进(允许更深的嵌套)和大缩进(易于阅读)之间很好的折衷。制表符会引起混乱,最好弃用。
-
折行以确保其不会超过 79 个字符。
这有助于小显示器用户阅读,也可以让大显示器能并排显示几个代码文件。
-
使用空行分隔函数和类,以及函数内的大块代码。
-
如果可能,注释独占一行。
-
使用文档字符串。
-
在操作符两边和逗号之后加空格, 但不要直接在左括号后和右括号前加:
a = f(1, 2) + g(3, 4)
. -
类和函数的命名风格要一致;传统上使用
CamelCase
驼峰风格命名类 而用lower_case_with_underscores
(小写字母加下划线)命名函数和方法。方法的第一个参数名称应为self
(查看 初识类 以获得更多有关类和方法的规则)。 -
如果您的代码要在国际环境中使用,不要使用花哨的编码。Python 默认的 UTF-8 或者 ASCII 在任何时候都是最好的选择。
-
同样,只要存在哪怕一丁点可能有使用另一种不同语言的人会阅读或维护你的代码,就不要在标识符中使用非 ASCII 字符。
脚注
[1] | 事实上, 按对象引用传递 可能是更恰当的说法,因为如果传递了一个可变对象,调用函数将看到任何被调用函数对该可变对象做出的改变(比如添加到列表中的元素) |