4. 其他流程控制工具
*******************

除了刚刚介绍过的 "while" 语句，Python中也有其他语言常用的流程控制语句
，只是稍有不同。


4.1. "if" Statements
====================

可能最为人所熟知的编程语句就是 "if" 语句了。例如：

   >>> x = int(input("Please enter an integer: "))
   Please enter an integer: 42
   >>> if x < 0:
   ...     x = 0
   ...     print('Negative changed to zero')
   ... elif x == 0:
   ...     print('Zero')
   ... elif x == 1:
   ...     print('Single')
   ... else:
   ...     print('More')
   ...
   More

There can be zero or more "elif" parts, and the "else" part is
optional.  The keyword '"elif"' is short for 'else if', and is useful
to avoid excessive indentation.  An  "if" ... "elif" ... "elif" ...
sequence is a substitute for the "switch" or "case" statements found
in other languages.


4.2. "for" Statements
=====================

The "for" statement in Python differs a bit from what you may be used
to in C or Pascal.  Rather than always iterating over an arithmetic
progression of numbers (like in Pascal), or giving the user the
ability to define both the iteration step and halting condition (as
C), Python's "for" statement iterates over the items of any sequence
(a list or a string), in the order that they appear in the sequence.
For example (no pun intended):

   >>> # Measure some strings:
   ... words = ['cat', 'window', 'defenestrate']
   >>> for w in words:
   ...     print(w, len(w))
   ...
   cat 3
   window 6
   defenestrate 12

如果在循环内需要修改序列中的值（比如重复某些选中的元素），推荐你先拷贝
一份副本。对序列进行循环不代表制作了一个副本进行操作。切片操作使这件事
非常简单：

   >>> for w in words[:]:  # Loop over a slice copy of the entire list.
   ...     if len(w) > 6:
   ...         words.insert(0, w)
   ...
   >>> words
   ['defenestrate', 'cat', 'window', 'defenestrate']

如果写成 "for w in words:"，这个示例就会创建无限长的列表，一次又一次重
复地插入 "defenestrate"。


4.3. "range()" 函数
===================

如果你确实需要遍历一个数字序列，内置函数 "range()"  会派上用场。它生成
算术级数:

   >>> for i in range(5):
   ...     print(i)
   ...
   0
   1
   2
   3
   4

给定的终止数值并不在要生成的序列里；"range(10)" 会生成10个值，并且是以
合法的索引生成一个长度为10的序列。range也可以以另一个数字开头，或者以
指定的幅度增加（甚至是负数；有时这也被叫做 '步进'）

   range(5, 10)
      5, 6, 7, 8, 9

   range(0, 10, 3)
      0, 3, 6, 9

   range(-10, -100, -30)
     -10, -40, -70

要以序列的索引来迭代，您可以将 "range()" 和 "len()" 组合如下:

   >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
   >>> for i in range(len(a)):
   ...     print(i, a[i])
   ...
   0 Mary
   1 had
   2 a
   3 little
   4 lamb

然而，在大多数这类情况下，使用 "enumerate()" 函数比较方便，请参见 循环
的技巧 。

如果你只打印 range，会出现奇怪的结果:

   >>> print(range(10))
   range(0, 10)

"range()" 所返回的对象在许多方面表现得像一个列表，但实际上却并不是。此
对象会在你迭代它时基于所希望的序列返回连续的项，但它没有真正生成列表，
这样就能节省空间。

我们说这样的对象是 *可迭代的* ，也就是说，适合作为函数和结构体的参数，
这些函数和结构体期望在迭代结束之前可以从中获取连续的元素。我们已经看到
"for" 语句就是这样一个迭代器。函数 "list()" 是另外一个；它从可迭代对象
中创建列表。

   >>> list(range(5))
   [0, 1, 2, 3, 4]

后面，我们会看到更多返回可迭代对象的函数，和以可迭代对象作为参数的函数
。


4.4. "break" and "continue" Statements, and "else" Clauses on Loops
===================================================================

"break" 语句，和 C 中的类似，用于跳出最近的 "for" 或 "while" 循环.

Loop statements may have an "else" clause; it is executed when the
loop terminates through exhaustion of the list (with "for") or when
the condition becomes false (with "while"), but not when the loop is
terminated by a "break" statement.  This is exemplified by the
following loop, which searches for prime numbers:

   >>> for n in range(2, 10):
   ...     for x in range(2, n):
   ...         if n % x == 0:
   ...             print(n, 'equals', x, '*', n//x)
   ...             break
   ...     else:
   ...         # loop fell through without finding a factor
   ...         print(n, 'is a prime number')
   ...
   2 is a prime number
   3 is a prime number
   4 equals 2 * 2
   5 is a prime number
   6 equals 2 * 3
   7 is a prime number
   8 equals 2 * 4
   9 equals 3 * 3

（是的，这是正确的代码。仔细看： "else" 子句属于 "for" 循环， **不属于
** "if" 语句。）

When used with a loop, the "else" clause has more in common with the
"else" clause of a "try" statement than it does that of "if"
statements: a "try" statement's "else" clause runs when no exception
occurs, and a loop's "else" clause runs when no "break" occurs. For
more on the "try" statement and exceptions, see 处理异常.

"continue" 语句也是借鉴自 C 语言，表示继续循环中的下一次迭代:

   >>> for num in range(2, 10):
   ...     if num % 2 == 0:
   ...         print("Found an even number", num)
   ...         continue
   ...     print("Found a number", num)
   Found an even number 2
   Found a number 3
   Found an even number 4
   Found a number 5
   Found an even number 6
   Found a number 7
   Found an even number 8
   Found a number 9


4.5. "pass" Statements
======================

"pass" 语句什么也不做。当语法上需要一个语句，但程序需要什么动作也不做
时，可以使用它。例如:

   >>> while True:
   ...     pass  # Busy-wait for keyboard interrupt (Ctrl+C)
   ...

这通常用于创建最小的类:

   >>> class MyEmptyClass:
   ...     pass
   ...

Another place "pass" can be used is as a place-holder for a function
or conditional body when you are working on new code, allowing you to
keep thinking at a more abstract level.  The "pass" is silently
ignored:

   >>> def initlog(*args):
   ...     pass   # Remember to implement this!
   ...


4.6. 定义函数
=============

我们可以创建一个输出任意范围内 Fibonacci 数列的函数:

   >>> def fib(n):    # write Fibonacci series up to n
   ...     """Print a Fibonacci series up to n."""
   ...     a, b = 0, 1
   ...     while a < n:
   ...         print(a, end=' ')
   ...         a, b = b, a+b
   ...     print()
   ...
   >>> # Now call the function we just defined:
   ... fib(2000)
   0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

关键字 "def" 引入一个函数 *定义*。它必须后跟函数名称和带括号的形式参数
列表。构成函数体的语句从下一行开始，并且必须缩进。

函数体的第一个语句可以（可选的）是字符串文字；这个字符串文字是函数的文
档字符串或 *docstring* 。（有关文档字符串的更多信息，请参阅  文档字符
串 部分）有些工具使用文档字符串自动生成在线或印刷文档，或者让用户以交
互式的形式浏览代码；在你编写的代码中包含文档字符串是一种很好的做法，所
以要养成习惯。

The *execution* of a function introduces a new symbol table used for
the local variables of the function.  More precisely, all variable
assignments in a function store the value in the local symbol table;
whereas variable references first look in the local symbol table, then
in the local symbol tables of enclosing functions, then in the global
symbol table, and finally in the table of built-in names. Thus, global
variables cannot be directly assigned a value within a function
(unless named in a "global" statement), although they may be
referenced.

在函数被调用时，实际参数（实参）会被引入被调用函数的本地符号表中；因此
，实参是通过 *按值调用* 传递的（其中 *值* 始终是对象 *引用* 而不是对象
的值）。[1] 当一个函数调用另外一个函数时，将会为该调用创建一个新的本地
符号表。

函数定义会把函数名引入当前的符号表中。函数名称的值具有解释器将其识别为
用户定义函数的类型。这个值可以分配给另一个名称，该名称也可以作为一个函
数使用。这用作一般的重命名机制:

   >>> fib
   <function fib at 10042ed0>
   >>> f = fib
   >>> f(100)
   0 1 1 2 3 5 8 13 21 34 55 89

如果你学过其他语言，你可能会认为 "fib" 不是函数而是一个过程，因为它并
不返回值。事实上，即使没有 "return" 语句的函数也会返回一个值，尽管它是
一个相当无聊的值。这个值称为 "None" （它是内置名称）。一般来说解释器不
会打印出单独的返回值 "None" ，如果你真想看到它，你可以使用 "print()"

   >>> fib(0)
   >>> print(fib(0))
   None

写一个返回斐波那契数列的列表，而不是打印出来的函数，非常简单:

   >>> def fib2(n):  # return Fibonacci series up to n
   ...     """Return a list containing the Fibonacci series up to n."""
   ...     result = []
   ...     a, b = 0, 1
   ...     while a < n:
   ...         result.append(a)    # see below
   ...         a, b = b, a+b
   ...     return result
   ...
   >>> f100 = fib2(100)    # call it
   >>> f100                # write the result
   [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

此示例中，像往常一样，演示了一些新的 Python 功能:

* The "return" statement returns with a value from a function.
  "return" without an expression argument returns "None". Falling off
  the end of a function also returns "None".

* "result.append(a)" 语句调用了列表对象 "result" 的* 。方法是 '属于'
  一个对象的函数，它被命名为 "obj.methodname" ，其中 "obj" 是某个对象
  （也可能是一个表达式）， "methodname" 是由对象类型中定义的方法的名称
  。不同的类型可以定义不同的方法。不同类型的方法可以有相同的名称而不会
  引起歧义。（可以使用 *类* 定义自己的对象类型和方法，请参阅 类 ）示例
  中的方法 "append()" 是为列表对象定义的；它会在列表的最后添加一个新的
  元素。在这个示例中它相当于 "result = result + [a]" ，但更高效。


4.7. 函数定义的更多形式
=======================

给函数定义有可变数目的参数也是可行的。这里有三种形式，可以组合使用。


4.7.1. 参数默认值
-----------------

最有用的形式是对一个或多个参数指定一个默认值。这样创建的函数，可以用比
定义时允许的更少的参数调用，比如:

   def ask_ok(prompt, retries=4, reminder='Please try again!'):
       while True:
           ok = input(prompt)
           if ok in ('y', 'ye', 'yes'):
               return True
           if ok in ('n', 'no', 'nop', 'nope'):
               return False
           retries = retries - 1
           if retries < 0:
               raise ValueError('invalid user response')
           print(reminder)

这个函数可以通过几种方式调用:

* 只给出必需的参数："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" 关键字。它可以测试一个序列是否包含某个值。

默认值是在 *定义过程* 中在函数定义处计算的，所以

   i = 5

   def f(arg=i):
       print(arg)

   i = 6
   f()

会打印 "5"。

**重要警告：** 默认值只会执行一次。这条规则在默认值为可变对象（列表、
字典以及大多数类实例）时很重要。比如，下面的函数会存储在后续调用中传递
给它的参数:

   def f(a, L=[]):
       L.append(a)
       return L

   print(f(1))
   print(f(2))
   print(f(3))

这将打印出

   [1]
   [1, 2]
   [1, 2, 3]

如果你不想要在后续调用之间共享默认值，你可以这样写这个函数:

   def f(a, L=None):
       if L is None:
           L = []
       L.append(a)
       return L


4.7.2. 关键字参数
-----------------

也可以使用形如 "kwarg=value" 的 *关键字参数* 来调用函数。例如下面的函
数:

   def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
       print("-- This parrot wouldn't", action, end=' ')
       print("if you put", voltage, "volts through it.")
       print("-- Lovely plumage, the", type)
       print("-- It's", state, "!")

接受一个必需的参数（"voltage"）和三个可选的参数（"state", "action"，和
"type"）。这个函数可以通过下面的任何一种方式调用:

   parrot(1000)                                          # 1 positional argument
   parrot(voltage=1000)                                  # 1 keyword argument
   parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
   parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
   parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
   parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

但下面的函数调用都是无效的:

   parrot()                     # required argument missing
   parrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argument
   parrot(110, voltage=220)     # duplicate value for the same argument
   parrot(actor='John Cleese')  # unknown keyword argument

在函数调用中，关键字参数必须跟随在位置参数的后面。传递的所有关键字参数
必须与函数接受的其中一个参数匹配（比如 "actor" 不是函数 "parrot" 的有
效参数），它们的顺序并不重要。这也包括非可选参数，（比如
"parrot(voltage=1000)" 也是有效的）。不能对同一个参数多次赋值。下面是
一个因为此限制而失败的例子:

   >>> def function(a):
   ...     pass
   ...
   >>> function(0, a=0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: function() got multiple values for keyword argument 'a'

When a final formal parameter of the form "**name" is present, it
receives a dictionary (see 映射类型 --- 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".) For example, if we define a function like
this:

   def cheeseshop(kind, *arguments, **keywords):
       print("-- Do you have any", kind, "?")
       print("-- I'm sorry, we're all out of", kind)
       for arg in arguments:
           print(arg)
       print("-" * 40)
       for kw in keywords:
           print(kw, ":", keywords[kw])

它可以像这样调用:

   cheeseshop("Limburger", "It's very runny, sir.",
              "It's really very, VERY runny, sir.",
              shopkeeper="Michael Palin",
              client="John Cleese",
              sketch="Cheese Shop Sketch")

当然它会打印:

   -- Do you have any Limburger ?
   -- I'm sorry, we're all out of Limburger
   It's very runny, sir.
   It's really very, VERY runny, sir.
   ----------------------------------------
   shopkeeper : Michael Palin
   client : John Cleese
   sketch : Cheese Shop Sketch

注意打印时关键字参数的顺序保证与调用函数时提供它们的顺序是相匹配的。


4.7.3. 任意的参数列表
---------------------

最后，最不常用的选项是可以使用任意数量的参数调用函数。这些参数会被包含
在一个元组里（参见 元组和序列 ）。在可变数量的参数之前，可能会出现零个
或多个普通参数。:

   def write_multiple_items(file, separator, *args):
       file.write(separator.join(args))

一般来说，这些 "可变参数" 将在形式参数列表的末尾，因为它们收集传递给函
数的所有剩余输入参数。出现在 "*args" 参数之后的任何形式参数都是 ‘仅关
键字参数’，也就是说它们只能作为关键字参数而不能是位置参数。:

   >>> def concat(*args, sep="/"):
   ...     return sep.join(args)
   ...
   >>> concat("earth", "mars", "venus")
   'earth/mars/venus'
   >>> concat("earth", "mars", "venus", sep=".")
   'earth.mars.venus'


4.7.4. 解包参数列表
-------------------

The reverse situation occurs when the arguments are already in a list
or tuple but need to be unpacked for a function call requiring
separate positional arguments.  For instance, the built-in "range()"
function expects separate *start* 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:

   >>> list(range(3, 6))            # normal call with separate arguments
   [3, 4, 5]
   >>> args = [3, 6]
   >>> list(range(*args))            # call with arguments unpacked from a list
   [3, 4, 5]

In the same fashion, dictionaries can deliver keyword arguments with
the "**"-operator:

   >>> def parrot(voltage, state='a stiff', action='voom'):
   ...     print("-- This parrot wouldn't", action, end=' ')
   ...     print("if you put", voltage, "volts through it.", end=' ')
   ...     print("E's", state, "!")
   ...
   >>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
   >>> parrot(**d)
   -- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !


4.7.5. Lambda 表达式
--------------------

可以用 "lambda" 关键字来创建一个小的匿名函数。这个函数返回两个参数的和
： "lambda a, b: a+b" 。Lambda函数可以在需要函数对象的任何地方使用。它
们在语法上限于单个表达式。从语义上来说，它们只是正常函数定义的语法糖。
与嵌套函数定义一样，lambda函数可以引用包含范围的变量:

   >>> def make_incrementor(n):
   ...     return lambda x: x + n
   ...
   >>> f = make_incrementor(42)
   >>> f(0)
   42
   >>> f(1)
   43

上面的例子使用一个lambda表达式来返回一个函数。另一个用法是传递一个小函
数作为参数:

   >>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
   >>> pairs.sort(key=lambda pair: pair[1])
   >>> pairs
   [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]


4.7.6. 文档字符串
-----------------

以下是有关文档字符串的内容和格式的一些约定。

第一行应该是对象目的的简要概述。为简洁起见，它不应显式声明对象的名称或
类型，因为这些可通过其他方式获得（除非名称恰好是描述函数操作的动词）。
这一行应以大写字母开头，以句点结尾。

如果文档字符串中有更多行，则第二行应为空白，从而在视觉上将摘要与其余描
述分开。后面几行应该是一个或多个段落，描述对象的调用约定，它的副作用等
。

Python解析器不会从Python中删除多行字符串文字的缩进，因此处理文档的工具
必须在需要时删除缩进。这是使用以下约定完成的。文档字符串第一行 *之后*
的第一个非空行确定整个文档字符串的缩进量。（我们不能使用第一行，因为它
通常与字符串的开头引号相邻，因此它的缩进在字符串文字中不明显。）然后从
字符串的所有行的开头剥离与该缩进 "等效" 的空格。 缩进的行不应该出现，
但是如果它们出现，则应该剥离它们的所有前导空格。应在扩展标签后测试空白
的等效性（通常为8个空格）。

下面是一个多行文档字符串的例子:

   >>> def my_function():
   ...     """Do nothing, but document it.
   ...
   ...     No, really, it doesn't do anything.
   ...     """
   ...     pass
   ...
   >>> print(my_function.__doc__)
   Do nothing, but document it.

       No, really, it doesn't do anything.


4.7.7. 函数标注
---------------

函数标注 是关于用户自定义函数中使用的类型的完全可选元数据信息（有关详
情请参阅 **PEP 3107** 和 **PEP 484** ）。

Annotations are stored in the "__annotations__" attribute of the
function as a dictionary and have no effect on any other part of the
function.  Parameter annotations are defined by a colon after the
parameter name, followed by an expression evaluating to the value of
the annotation.  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.  The following example has a
positional argument, a keyword argument, and the return value
annotated:

   >>> def f(ham: str, eggs: str = 'eggs') -> str:
   ...     print("Annotations:", f.__annotations__)
   ...     print("Arguments:", ham, eggs)
   ...     return ham + ' and ' + eggs
   ...
   >>> f('spam')
   Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
   Arguments: spam eggs
   'spam and eggs'


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] 实际上，*通过对象引用调用* 会是一个更好的表述，因为如果传递的
    是可 变对象，则调用者将看到被调用者对其做出的任何更改（插入到列表
    中的元 素）。
