python 笔记之 文档字符串,收集关键字参数,作用域嵌套, 递归,异常
? python ?    2021-03-10 17:07:35    1264    0    0
gua_l   ? python ?

文档字符串

除了# 注释,有另一种编写注释的方式,就是添加独立的字符串。在有些地方,如def语句后面(以及模块和类的开头,),添加这样的字符串很有用。放在函数开头的字符串称为文档字符串(docstring),将作为函数的一部分存储起来。下面的代码演示了如何给函数添加文档字符串:

  1. def square(x):
  2. 'Calculates the square of the number x.'
  3. return x * x

可以像下面这样访问文档字符串:

  1. >>> square.__doc__
  2. 'Calculates the square of the number x.'

收集参数

单星号 * (元组)
前面有星号的参数将被放在元组中。
因此星号意味着收集余下的位置参数。如果没有可供收集的参数,params将是一个空元组。

  1. >>> print_params_2('Nothing:')
  2. Nothing:
  3. ()

与赋值时一样,带星号的参数也可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续参数。单星号不会收集关键字参数。

  1. >>> def in_the_middle(x, *y, z):
  2. ... print(x, y, z)
  3. ...
  4. >>> in_the_middle(1, 2, 3, 4, 5, z=7)
  5. 1 (2, 3, 4, 5) 7

双星号 ** (字典)
要收集关键字参数,可使用两个星号。

  1. >>> def print_params_3(**params):
  2. ... print(params)
  3. ...
  4. >>> print_params_3(x=1, y=2, z=3)
  5. {'z': 3, 'x': 1, 'y': 2}

如你所见,这样得到的是一个字典而不是元组。可结合使用这些技术。

  1. def print_params_4(x, y, z=3, *pospar, **keypar):
  2. print(x, y, z)
  3. print(pospar)
  4. print(keypar)

其效果与预期的相同。

  1. >>> print_params_4(1, 2, 3, 5, 6, 7, foo=1, bar=2)
  2. 1 2 3
  3. (5, 6, 7)
  4. {'foo': 1, 'bar': 2}
  5. >>> print_params_4(1, 2)
  6. 1 2 3
  7. ()
  8. {}

作用域

vars() 作用域
变量到底是什么呢?可将其视为指向值的名称。因此,执行赋值语句x = 1后,名称x指向值1。这几乎与使用字典时一样(字典中的键指向值),只是你使用的是“看不见”的字典。实际上,这种解释已经离真相不远。有一个名为vars的内置函数,它返回这个不可见的字典:

  1. >>> x = 1
  2. >>> scope = vars()
  3. >>> scope['x']
  4. 1
  5. >>> scope['x'] += 1
  6. >>> x
  7. 2

遮盖和全局变量 globals()
读取全局变量的值通常不会有问题,但还是存在出现问题的可能性。如果有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量遮 住了。如果需要,可使用函数globals来访问全局变量。这个函数类似于vars,返回一个包含全局变量的字典。(locals返回一个包含局部变量的字典。)例如,在前面的示例中,如果有一个名为parameter的全局变量,就无法在函数combine中访问它,因为有一个与之同名的参数。然而,必要时可使用globals()[‘parameter’]来访问它。

  1. >>> def combine(parameter):
  2. ... print(parameter + globals()['parameter'])
  3. ...
  4. >>> parameter = 'berry'
  5. >>> combine('Shrub')
  6. Shrubberry

重新关联全局变量(使其指向新值)是另一码事。在函数内部给变量赋值时,该变量默认为局部变量,除非你明确地告诉Python它是全局变量。那么如何将这一点告知Python呢?

  1. >>> x = 1
  2. >>> def change_global():
  3. ... global x
  4. ... x = x + 1
  5. ...
  6. >>> change_global()
  7. >>> x
  8. 2

作用域嵌套

Python函数可以嵌套,即可将一个函数放在另一个函数内,如下所示:

  1. def foo():
  2. def bar():
  3. print("Hello, world!")
  4. bar()

嵌套通常用处不大,但有一个很突出的用途:使用一个函数来创建另一个函数。这意味着可像下面这样编写函数:

  1. def multiplier(factor):
  2. def multiplyByFactor(number):
  3. return number * factor
  4. return multiplyByFactor

在这里,一个函数位于另一个函数中,且外面的函数返回里面的函数。也就是返回一个函数,而不是调用它。重要的是,返回的函数能够访问其定义所在的作用域。换而言之,它携带着自己所在的环境(和相关的局部变量)!每当外部函数被调用时,都将重新定义内部的函数,而变量factor的值也可能不同。由于Python的嵌套作用域,可在内部函数中访问这个来自外部局部作用域(multiplier)的变量,如下所示:

  1. >>> double = multiplier(2)
  2. >>> double(5)
  3. 10
  4. >>> triple = multiplier(3)
  5. >>> triple(3)
  6. 9
  7. >>> multiplier(5)(4)
  8. 20

像multiplyByFactor这样存储其所在作用域的函数称为闭包

递归

非无穷递归函数将满足以下两个条件:
基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
递归条件 包含一个或多个调用,这些调用旨在解决问题的一部分。

异常

try except raise

  1. class MuffledCalculator:
  2. muffled = False
  3. def calc(self, expr):
  4. try:
  5. return eval(expr)
  6. except ZeroDivisionError: ## 捕获后处理或继续引发异常
  7. if self.muffled:
  8. print('Division by zero is illegal')
  9. else:
  10. raise ## 捕获后仍引发异常

多个except:

  1. except ZeroDivisionError:
  2. print("The second number can not be Zero.")
  3. except ValueError:
  4. print("that was not a number.")

等价于用元组表示多个异常

  1. except (ZeroDivisionError,ValueError):
  2. print("all error info")

捕获导常对象本身并打印它

  1. try:
  2. x = int(input('Enter the first number: '))
  3. y = int(input('Enter the second number: '))
  4. print(x / y)
  5. except (ZeroDivisionError, TypeError) as e:
  6. print(e)

except子句也捕获两种异常,但由于你同时显式地捕获了对象本身,因此可将其打印出来,让用户知道发生了什么情况。

捕获所有

  1. except:
  2. print('Something wrong happened ...')

优化对异常对象进行检查

  1. except Exception as e

无异常往下 使用else 子句
在有些情况下,在没有出现异常时执行一个代码块很有用。为此,可像条件语句和循环一样,给try/except语句添加一个else子句。

  1. try:
  2. print('A simple task')
  3. except:
  4. print('What? Something went wrong?')
  5. else:
  6. print('Ah ... It went as planned.')

如果你运行这些代码,输出将如下:

  1. A simple task
  2. Ah ... It went as planned.

通过使用else子句,可实现循环。

  1. while True:
  2. try:
  3. x = int(input('Enter the first number: '))
  4. y = int(input('Enter the second number: '))
  5. value = x / y
  6. print('x / y is', value)
  7. except:
  8. print('Invalid input. Please try again.')
  9. else:
  10. break

在这里,仅当没有引发异常时,才会跳出循环(这是由else子句中的break语句实现的)。换而言之,只要出现错误,程序就会要求用户提供新的输入

优化
如果使用except Exception as e,
就可利用8.3.4节介绍的技巧在这个小型除法程序中打印更有用的错误消息。

  1. while True:
  2. try:
  3. x = int(input('Enter the first number: '))
  4. y = int(input('Enter the second number: '))
  5. value = x / y
  6. print('x / y is', value)
  7. except Exception as e:
  8. print('Invalid input:', e)
  9. print('Please try again')
  10. else:
  11. break

finally
不管try 中的语句 是否引发异常,都将执行finally 中的语句

  1. try:
  2. 1 / 0
  3. except NameError:
  4. print("Unknown variable")
  5. else:
  6. print("That went well!")
  7. finally:
  8. print("Cleaning up.")

Pre: python 笔记之 模块

Next: python 笔记之 python函数的参数是可变的数据结构时需要创建副本

1264
Sign in to leave a comment.
No Leanote account? Sign up now.
0 comments
Table of content