Effective Python 学习笔记 4
尽量使用异常来表示特殊情况,而不要返回None
_表示用不到的变量
要点
- 用None这个返回值来表示特殊意义的函数,很容易使调用者犯错,以为None和0及空字符串之类的值,在表达式里面都会评估为False
- 函数遇到特特殊情况时应该抛出异常,而不是返回None
了解如何在闭包里使用外围作用域中的变量
1 | ''' |
要点
- 对于定义在某作用域内的闭包来说,它可以引用这些作用域中的变量
- 使用默认方式对闭包内的变量赋值,不会影响外围作用域的同名变量
- 在python 3中,程序可以在闭包内用nonlocal语句来修饰某个名称,使该闭包能够修改外围作用域中的同名变量
- 除了简单的函数,尽量不要使用nonlocal语句
考虑使用生成器来改写直接返回列表的函数
1 | # eg.返回字符串中英文单词的首字母和其下标 |
以上程序的问题:
- 代码拥挤,每次找到新的结果,都要调用append方法。而我们真正强调的不是对append的调用,而是该方法给列表中添加的那个值且函数首位都要对resut进行创建和返回
1 | # 生成器改写 |
- index_words函数在它返回前,要把所有结果都放在列表中。如果数据量非常大,那么程序可能会耗尽内存。用生成器改写后,可以应对任意长度的输入数据
要点
- 使用时生成器比用list返回结果更加清晰
- 由生成器函数所返回的那个迭代器,可以把生成器函数体中,传给yield表达式的那些值,逐次生产出来
- 无论数据量多大,生成器都能产生一系列输出,不会对内存造成压力
在参数上面迭代时要多加小心
细节见书本第17条
要点
- 如果参数是迭代器,那么可能会导致奇怪的行为并错失某些值
- python的迭代器协议,描述了容器和迭代器应该如何与iter和next内置函数、for循环及相关表达式相互配合
- 把__iter__方法时限为生成器,即可定义自己的容器类型
- 想判断某个值是迭代器还是容器,可以拿该值为参数,两侧调用iter函数,若结果相同,则是迭代器,调用内置的next函数,即可令该迭代器前进一步
用数量可变的位置参数减少视觉杂讯
令函数接受可选位置参数(由于这种参数习惯上写为*args,所以又称为star args,星号参数),能够使代码更加清晰,并减少视觉杂讯(visual noise)。
visual noise:一种比喻,意思是使代码看起来不要太过杂乱,以强调其中的重要内容。
出现的问题
- 变长参数在传给函数时,总是先转化为元组。这就意味着,如果用带有*操作符的生成器为参数,来调用这种参数,python必须把该生成器完整迭代一轮,并把所生成的每个值,都放入元组之中。这可能会消耗大量内存。所以只有当我们确定参数个数较少时,才采用这种写法
- 如果以后要给函数添加新的位置参数,那就必须修改原来调用该函数的那些旧代码
要点
- def语句中用*args,即可令函数接受数量可变的位置参数
- 调用函数时,可以采用*操作符,把序列中的元素当成位置参数,传给该函数
- 对生成器使用*操作符,可能导致内存耗尽
- 在已经接受*args参数的函数上继续添加位置参数,可能会产生难以排查的bug
用关键字参数来表达可选行为
1 | def func(arg1, arg2): |
关键字参数的好处
- 易于理解,参数含义与参数值都呈现在面前
- 可以在函数中提供默认值
- 它可以提供一种扩充函数参数的有效方式,使得扩充之后的函数依然能与原有的那些调用代码兼容
要点
- 函数参数可以按照位置或关键字来指定
- 只是用位置参数来调用函数,可能会导致这些参数数值含义不够明确,而关键字参数则能够阐明每个参数的意图
- 给函数添加新行为时,可以使用带默认值的关键字参数,以便与原有的函数点用代码保持兼容
- 可选的关键字参数,总是应该以关键字形式来指定,而不是以位置参数的形式来指定