Python 学习成长记(四)---- Python 流程控制

来源:互联网 发布:马云网络 编辑:程序博客网 时间:2024/06/03 23:02

  最近准备重新系统学习一下Python语言,着手研读Python 3.6.1官方教程文档,于是想着不如翻译整理出来 , 希望能帮到一起学习Python的同学们,与大家一起进步,由于水平有限,翻译若有不妥之处,请大家指正 。


本文翻译 Python 官方教程第四章:More Control Flow Tools


  
  除了《Python 学习成长记(三)—- Python 基本用法简介》介绍的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

  可以没有或多个elif部分,else部分是可选的。 关键字“elif”是“else if”的缩写,有助于避免过度缩进。 if… elif … elif …语法是替换其他语言中的switch或case语句。

4.2 for循环语句

  Python中的for语句可能与C或Pascal中的for语句使用有一点点不同。 它不是总是用迭代数字的算术过程(如Pascal),或者给予用户定义迭代步骤和停止条件(如C语言),Python的for语句可以迭代任何序列的item(List or String),按序列中顺序来执行。 例如: 

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

  如果您需要修改循环内的迭代顺序(例如复制所选项),建议您先复制一份。 迭代序列不会隐式地复制。 slice使得此功能更加方便:  

>>> 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: , 那么它将无限循环地往words这个list中插入defenestrate

4.3 range()函数

  如果你确实需要迭代一个数字序列,内置的函数range()就派上用场了:

>>> for i in range(5):...     print(i)...01234

  给定的终点不是生成的序列的一部分; 例如range(10)生成10个值,即长度为10的序列。Python还可以让范围从另一个数字开始,或者指定不同的增量(甚至是负的;有时这被称为“步长” ):例如

range(5, 10)   5 through 9range(0, 10, 3)   0, 3, 6, 9range(-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 Mary1 had2 a3 little4 lamb

  然而,在大多数情况下,使用enumerate()函数更方便,请参阅循环技术。

  如果你打印一个range(),就会发生比较奇怪的事情:

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

  在很多时候,range()返回的对象的行为觉得它是一个list,但实际上它不是。当您遍历它时,它是返回所需序列的连续items的对象,但它并不真正的列表,这样可以节省空间。
  如果需要打印这个值得先转成list对象,然后再打印, 如

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

  稍后我们将分析更多的函数,这些函数是返回iterables并以iterables为参数的。

4.4 break和continue语句,及其它循环的语法

  break语句就像在C语言使用一样,跳出最小的for或while循环。
  循环语句可能还有一个else子句; 它在循环结束后才执行,而不是由break语句终止后时才执行。 下面是一个搜索素数的循环例子:

>>> 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 number3 is a prime number4 equals 2 * 25 is a prime number6 equals 2 * 37 is a prime number8 equals 2 * 49 equals 3 * 3

(是的,这是正确的代码。仔细看看:else子句属于for循环,而不是if语句。)

  当与循环一起使用时,else子句与try语句的else子句相同,而不是if语句:try语句的else子句将在没有异常发生的情况下运行, 循环的else子句在没有发生break的情况下运行 。 有关try语句和异常的更多信息,请参阅处理异常。

  continue语句将继续运行循环的下一个迭代:

>>> 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 2Found a number 3Found an even number 4Found a number 5Found an even number 6Found a number 7Found an even number 8Found 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 这里写链接内容定义函数

  我们可以创建一个函数来实现带任意边界的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用来定义一个新的函数。它后面必须跟随函数名称和形参。函数体从下一行开始,必须缩进。
  函数体的第一个语句可以是字符串文字(可选); 这个字符串用来介绍该函数的功能文档。 (有关docstrings的更多信息可以在文档字符串部分中找到。)有些工具使用docstrings自动生成在线或打印的文档,或者让用户交互式浏览代码; 在你写的代码中包含docstrings是一个很好的做法,应该形成一个习惯。
  函数的执行过程中引入了可用于函数的局部变量的新符号表,更准确地说,函数中的所有变量赋值将存储在本地符号表中; 而变量引用首先在本地符号表中查找,其次在封闭函数的本地符号表中查找,再次在全局符号表中查找,最后才查找表中的内置名称。因此,全局变量不能被直接赋予函数内的值(除非在全局语句中命名)。
  函数调用的实际参数(参数)在被调用函数的局部符号表中被引入;因此,使用call by value传递参数(其值始终是对象引用,而不是对象的值)。 当函数调用另一个函数时,将为该调用创建一个新的本地符号表。
  函数定义在当前符号表中引入函数名称。函数名称的值具有由解释器识别为用户定义函数的类型。该值可以分配给另一个名称,然后可以将其用作函数。这是一个通用的重命名机制:

>>> 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通常被解释器抑制。 如:

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

  编写一个返回Fibonacci数列list的函数很简单,

>>> 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。
  result.append(a)调用list对象result方法。 一个方法是一个“属于”一个对象并被命名为obj.methodname的函数,其中obj是某一对象(也可能是一个表达式),而methodname是由对象的类型定义的方法的名称。 不同的类型定义不同的方法。 不同类型的方法可能具有相同的名称,而不会引起歧义。 (可以使用类定义自己的对象类型和方法,请参阅Classes)示例中的方法append()是为list对象定义的; 它在list的末尾添加了一个新元素。 在这个例子中,它相当于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

  重要警告:默认值仅能被使用一次。 当默认是可变对象(如list,dictionary或大多数类的实例)时,这将有所不同。 例如,以下函数在后续调用时累加传递给它的参数:
  

def f(a, L=[]):    L.append(a)    return Lprint(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 argumentparrot(voltage=1000)                                  # 1 keyword argumentparrot(voltage=1000000, action='VOOOOOM')             # 2 keyword argumentsparrot(action='VOOOOOM', voltage=1000000)             # 2 keyword argumentsparrot('a million', 'bereft of life', 'jump')         # 3 positional argumentsparrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

  但以下所有的调用方式是无效:

parrot()                     # required argument missingparrot(voltage=5.0, 'dead')  # non-keyword argument after a keyword argumentparrot(110, voltage=220)     # duplicate value for the same argumentparrot(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 ?TypeError: function() got multiple values for keyword argument 'a'

  当一个以**name形式的最终形参存在时,它会收到包含所有关键字参数的dictionary(请参阅映射类型 - dict),除了那些与形参对应的关键字参数。 这可以与以* name形式的形参组合, 这些形参可以接收包含超出形参列表的位置参数的元素(将在下一小节中描述)。 (*name必须在**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)    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 LimburgerIt's very runny, sir.It's really very, VERY runny, sir.----------------------------------------shopkeeper : Michael Palinclient : John Cleesesketch : Cheese Shop Sketch>>>

请注意,打印关键字参数的顺序须与函数调用中提供的顺序相匹配。

4.7.3  任意参数列表

  最后,最常用的选项是指定可以使用任意数量的参数调用函数。 这些参数将被包装在一个tuple元组中(参见元组和序列)。 在可变数量的参数之前,可能会出现零个或多个正常

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

  通常,这些variadic参数将在形参列表中最后出现,因为它们会遍历传递给函数的所有剩余输入参数。 在* 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 Unpacking Argument Lists

  当参数已经在list或tuple中,但是需要被解压缩给需要单独位置参数的函数调用时,会发生相反的情况。 例如,内置函数range()需要单独的起始和停止参数。 如果它们不是单独可用,需要使用*操作符来解压list或者tupleq外的参数给函数调用:

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

  同理,字典可以使用** -运算符来传递关键字参数:

>>> 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中删除多行字符串文字的缩进,因此处理文档的工具必须根据需要删除缩进。这是使用以下约定完成的。字符串第一行之后的第一个非空行确定整个文档字符串的缩进量。 (我们不能使用第一行,因为它通常与字符串的开头引号相邻,因此它的缩进在字符串文字中不明显)。然后从该字符串的所有行的起始处删除与此缩进的“等效”空格。那些没有缩进的行不应该发生,但是如果它们发生的话,所有的空格应该被剥离。应在扩展tabs后(通常为8个空格)来测试空格的等同性。

  这是一个多行的docstring的例子:

>>> 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 函数注释

  函数注释是完全可选metadata信息,这些信息是关于用户定义函数的使用类型(有关详细信息,请参阅PEP 484)。

  注释作为字典存储在函数的annotations属性中,对函数的任何其他部分没有影响。 参数注释由参数名后面的冒号定义,后跟一个表达式计算注释的值。 返回注释由‘- >’定义(后跟一个表达式)定义在参数列表和冒号之间。 以下示例具有位置参数,关键字参数和注释的返回值:

>>> 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 Intermezzo: 编码风格

  如果你将要编写更长更复杂的Python,那么就有必要谈谈编码风格了。大多数语言可以写成(或更简洁,格式化)的不同风格; 可读性更高。让别人轻松阅读你的代码永远是一个好主意,采用一个很好的编码风格是有有很大的帮助的。
  对于Python,PEP 8已经成为大多数项目遵循的风格指南;它是一种可读性高和令人着迷的编码风格。每个Python开发人员都应该在某些时候阅读它;这里是为您提取的最重要的一点:  

  • 使用4空格缩进,而不是tabs。
      4个空格是小缩进之间的一个很好的妥协(允许更大的嵌套深度)和大的缩进(更容易阅读)。Tabs会引起混乱,最好不要了。
  • 一行最好不超过79个字符。
      这让用户可以使用小型尺寸的显示器,或者在较大的显示器上可以并排放置多个代码文件。

  • 使用空行来分离函数和类,以及函数中较大的代码块。

  • 如有可能,将注释放在单独一行。

  • 使用docstrings。

  • 在运算符周围,逗号后面使用空格,如:a = f(1, 2) + g(3, 4)。

  • 一致地命名你的类和方法; 惯例是类使用CamelCase,函数和方法使用lower_case_with_underscores。始终使用self作为第一个方法参数的名称(有关类和方法的更多信息,请参阅A First Look at Classes)。

  • 如果你的代码旨在在国际环境中使用,请勿使用花式编码。在任何情况下,Python的默认值,UTF-8或甚至纯ASCII效果最好。

  • 同样地,由于不同国家的人可能会言会读取或维护代码,请勿在标识符中使用非ASCII字符。

0 0
原创粉丝点击