4. 流程控制语句

4. 流程控制语句 

除了 while 语句,Python还拥有在其他语言里常见的控制语句,以及一些好玩的语句。.

4.1.if 语句 

也许最常见的语句类型是 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

可以有0个或多个 elif 块,,并且 else 块是可选的。关键字 ‘elif‘ 比 ‘else if’ 短,,并且可以避免过度缩进。一个if ... elif ... elif ...序列 是对其他语言的switch or case 语句的替代方案。

4.2. for 语句

Python 的 for 语句与你在 C 或者 Pascal 中使用的有一点区别。和常见的等差数列迭代(如 Pascal 中)或让用户能够自定义迭代步骤和停止条件(如 C)不一样,Python 的for 语句按照元素出现的顺序迭代任何序列(列表或字符串)例如(没有双关意):

>>>
>>> # 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']

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 through 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. breakcontinue语句,以及循环中else子句

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

循环语句可以有一个 else 子句;当(for)循环迭代完整个列表或(while)循环条件变为假,而非由break语句终止时,就会执行这个else语句。下面循环搜索质数的代码例示了这一点:

>>>
>>> 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 语句的 。)

与在if语句中的用法相比,循环中的else子句与try语句的else子句有更多的共同点:try语句的else子句在未出现异常时运行,循环的else子句在未出现break时运行。更多关于try语句和异常的内容,请参见 处理异常

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 语句 

pass 语句什么也不做。它用于语法上必须要有一条语句,但程序什么也不需要做的场合。例如:

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

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

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

另一个使用pass的地方是编写新代码时作为函数体或控制体的占位符,这让你在更抽象层次上思考。pass语句将被默默地忽略:

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

4.6. 定义函数 

我们可以创建一个生成任意上界斐波那契数列的函数:

>>>
>>> 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(更多关于 docstrings 的内容可以在 文档字符串一节中找到。)有工具使用 docstrings 自动生成在线的或可打印的文档,或者让用户在代码中交互浏览;在您编写的代码中包含 docstrings 是很好的做法,所以让它成为习惯吧。

执行 一个函数会引入一个用于函数的局部变量的新符号表。更确切地说,函数中的所有的赋值都是将值存储在局部符号表;而变量引用首先查找局部符号表,然后是上层函数的局部符号表,然后是全局符号表,最后是内置名字表。然而,在函数内部无法给一个全局变量直接赋值(除非在一个 global 语句中命名),虽然可以引用它们。

函数调用的实际参数在函数被调用时引入被调函数的局部符号表;因此,参数的传递使用 传值调用 (这里的  始终是对象的 引用,不是对象的值)。[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 功能:

  • return 语句可以从函数中携带着返回值返回。return 语句不携带任何表达式参数时返回 None如果一直执行到整个函数结束还没有碰到 return 语句,也返回 None
  • 语句 result.append(a) 调用了列表result的一个 方法。A method is a function that ‘belongs’ to an object and is named obj.methodname, where obj is some object (this may be an expression), and methodname 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 method append() 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 to result = result + [a], but more efficient.

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. 关键字参数 

Functions can also be called using keyword arguments of the form 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) 和三个可选参数 (stateaction, and 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

在函数调用中,关键字参数必须跟随在位置参数的后面。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). 任何参数都不可以多次赋值。下面的示例由于这种限制将失败:

>>>
>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
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 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.) 例如,如果我们定义这样的函数:

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)
    keys = sorted(keywords.keys())
    for kw in keys:
        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.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

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). 在可变个数的参数之前,可以有零到多个普通的参数。

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

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.

>>>
>>> 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. 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:

>>>
>>> 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 解释器不会从多行的文档字符串中去除缩进,所以必要的时候处理文档字符串的工具应当自己清除缩进。这通过使用以下约定可以达到。第一行 之后 的第一个非空行字符串确定整个文档字符串的缩进的量。(我们不用第一行是因为它通常紧靠着字符串起始的引号,其缩进格式不明晰。)所有行起始的等于缩进量的空格都将被过滤掉。不应该存在缩进更少的行,但如果确实存在,应去除所有其前导空白。应该在展开制表符之后(展开后通常为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. 函数注释

函数注释 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. 下面的示例包含一个位置参数,一个关键字参数,和没有意义的返回值注释。

>>>
>>> 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]事实上,  按对象引用传递   可能是更恰当的说法,因为如果传递了一个可变对象,调用函数将看到任何被调用函数对该可变对象做出的改变(比如添加到列表中的元素)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值