layout title keywords category tags
post
编写高质量的python代码 -- 02. 函数
python
python
python

2017-12-29-effective-python-02-functions

14. 尽量用异常来表示特殊情况,而不返回 None

  • 用 None 这个返回值来表示特殊意义的函数,很容易使调用者犯错,因为 None 和0以及空字符之类的值,在条件表达式里都会评估为 False
  • 函数在遇到特殊情况时,应该抛出异常,而不要返回 None.调用者看到该函数的文档中所描述的异常之后,应该就会编写相应的代码来处理它们了

如两个数字进行相除的函数

def divide(a, b):"""求两个数相除"""try:return a / bexcept ZeroDivisionError as e:raise ValueError('Invalid inputs') from ex, y = 5, 2
try:result = divide(x, y)
except ValueError:print('Invalid inputs')
else:print('Result is {}'.format(result))

15. 了解如何在闭包里使用外围作用域中的变量

  • 对于定义在某作用域内的闭包来说,它可以引用这些作用域中的变量
  • 使用默认方式对闭包内的变量赋值,不会影响外围作用域中的同名变量
  • 在 python3中,程序可以在闭包内用 nonlocal 语句来修饰某个名称,使该闭包能够修改外围作用域中的同名变量
  • 在 python2中,程序可以使用可变值(如,包含单个元素的列表)来实现与 nonlocal 语句相仿的机制
  • 处理那种比较简单的函数,尽量不要用 nonlocal 语句

示例:假如有一份列表,其中的元素都是数字,现在要对其排序,但排序时,要把出现在某个群组内的数字,放在群组外的那些数字之前。

numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}def sort_priority(values, group):def helper(x):if x in group:return (0, x)return (1, x)values.sort(key=helper)
sort_priority(numbers, group)
>>> [2, 3, 5, 7, 1, 4, 6, 8]# 这个山水能正常工作是因为
# 1. python 支持闭包:闭包是一种定义在某个作用域中的函数,这种函数引用了那个作用域里面的变量。helper 函数之所以能够访问 sort_priority 的 group 参数,原因就在于它是闭包
# 2. python 的函数是一级对象,也就是说,我们可以直接饮用函数,把函数赋给变量、把函数当成参数传递给其它函数,并通过表达式及 if 语句对其进行比较和判断,等待。于是,我们可以把 helper 这个闭包函数,传递个 sort 方法的 key 参数
# 3. python 使用特殊的规则来比较两个元组。它首先比较各元组中下标为0的对应元素,如果相等,在比较下表为1的对应元素,依次类推

改进 sort_priority函数,返回一个值用来表示用户界面里是否出现了优先级较高的原件,使得该函数的调用者,可以根据这个返回值做出相应的处理。

numbers = [8, 3, 1, 2, 5, 4, 7, 6]
group = {2, 3, 5, 7}def sort_priority(values, group):found = Falsedef helper(x):if x in group:found = True    # 闭包里面的赋值,相当于定义了一个新的变量 found # 不会修改sort_priority函数中的 found 值return (0, x)return (1, x)values.sort(key=helper)return foundsort_priority(numbers, group)>>>
Found: False # 期望返回 True,返回了 False
[2, 3, 5, 7, 1, 4, 6, 8]# 之所以出现上面的问题,是Python 语言故意这么设计的。这样做可以防止函数中的局部变量污染函数外面的那个模块。

python3中获取闭包内的数据,可以使用 nonlocal。也就是给相关变量赋值的时候,应该在上层作用域中查找该变量。nonlocal 的唯一限制在于,它不能延伸到模块级别,这是为了防止污染全局作用域。

def sort_priority(values, group):found = Falsedef helper(x):nonlocal foundif x in group:found = Truereturn (0, x)return (1, x)values.sort(key=helper)return found

python2中不支持 nonlocal 关键字,为了实现类似的功能,我们需要利用 python 的作用域规则来解决。

def sort_priority(numbers, group):found = [False]def helper(x):if x in group:found[0] = Truereturn (0, x)return (1, x)numbers.sort(key=helper)return found[0]

如果使用 nonlocal 的那些代码,已经写得原来越复杂,那就应该将其相关的状态封装成辅助类。下面定义的类,与 nonlocal 所表达的功能形同。

class Sorter(object):def __init__(self, group):self.group = groupself.found = Falsedef __call__(self, x):if x in self.group:self.found = Truereturn (0, x)return (1, x)sorter = Sorter(group)
numbers.sort(key=sorter)
assert sorter.found is True

16. 考虑用生成器来改写直接返回列表的函数

  • 使用生成器比把收集到的结果放入列表里返回给调用者更加清晰
  • 由生成器函数返回的那个迭代器,可以把生成器函数体重,传给 yield 表达式的那些纸,逐次产生出来
  • 无论输入量有多大,生成器都能产生一系列输出,因为这些输入量和输出量,都不会影响它在执行时所耗的内存

例如: 我们要查出字符串中每个词的首字母,在整个字符串里的位置。

def index_words(text):result = []if text:result.append(0)for index, letter in enumerate(text):if letter == ' ':result.append(index + 1)return result# 把这个函数改为生成器来写会更好。生成器是使用 yield 表达式的函数。调用生成器函数时,
# 它不会真的运行,而是会返回迭代器。每次在这个迭代器上面调用内置的 next 函数时,迭代
# 器会把生成器推进到下一个 yield 表达式那里。生成器传给 yield 的每一个值,都会由迭代
# 器返回给调用者def index_words_iter(text):if text:yield 0for index, letter in enumerate(text):if letter == ' ':yield index + 1result = list(index_words_iter(address))# 下面定义一个生成器,从文件里面依次读入各行内容,然后逐个处理每行汇总的单词,并产生
# 相应结果。该函数执行时所耗的内存,由单行输入值的最大字符数来界定def index_file(handle):offset = 0for line in handle:if line:yield offsetfor letter in line:offset += 1if letter == ' ':yield offsetwith open('/tmp/address.txt', 'r') as f:it = index_file(f)results = islice(it, 0, 3)print(list(results)

17. 在参数上面迭代时,要多加小心

  • 函数在输入的参数上面多次迭代时要小心:如果参数是迭代器,那么可能会导致奇怪的行为并错失某些值
  • python 的迭代器协议,描述了容器和迭代器应该如何与iter和 nextn 内置函数、for 循环及相关表达式相互配合
  • iter 方法实现为生成器,即可定义自己的容器类型
  • 想判断某个值是迭代器还是容器,可以拿该值为参数,两次调用 iter 函数,若结果相同,则为迭代器,调用内置的 next 函数,即可令该迭代器前进一步

以统计城市旅游的人数,占总游客的百分比。

def normalize_defensive(numbers):if iter(numbers) is iter(numbers):raise TypeError('Must supply a container')total = sum(numbers)result = []for value in numbers:percent = 100 * value / totalresult.append(percent)return result# 使用迭代器协议(iterator protocol)的容器类
class ReadVisits(object):def __init__(self, data_path):self.data_path = data_pathdef __iter__(self):with open(self.data_path) as f:for line in f:yield int(line)'''
/tmp/a.data 文件中包含三行数据
15
35
80
'''# python 在 for 循环及相关表达式中遍历某中容器的内容时,就要依靠这个迭代协议。
# 在执行类似 for x in foo 这样的语句时,python 实际上会调动 iter(foo).内置的 iter 函数又会
# 调用 foo.__iter__ 这个特殊方法。该方法必须返回迭代器对象。visits = [15, 35, 80]
normalize_defensive(visits) # No error
visits = ReadVisits('/tmp/a.data')
normalize_defensive(visits)

18. 用数量可变的位置参数减少视觉杂讯

  • 在 def 语句中使用*args, 即可令函数接受数量可变的位置参数
  • 调用函数时,可以采用* 操作符,把序列中的元素当成位置参数,传给该函数
  • 对生成器使用 * 操作符,可能导致程序耗尽内存并崩溃
  • 在已经接受 *args 参数的函数上面继续添加位置参数,可能会产生难以排查的 bug

19. 用关键字参数来表达可选的行为

  • 函数参数可以按位置或关键字来指定
  • 只使用位置参数来调用函数,可能会导致这些参数值的含义不够明确,而关键字参数则能阐明每个参数的意图
  • 给函数添加新的行为时,可以使用带默认值的关键字参数,以便与原有的函数调用代码保持兼容
  • 可选的关键字参数,总是应该以关键字形式来指定,而不应该以位置参数的形式来指定

20. 用 None 和文档字符串来描述具有动态默认值的参数

  • 参数的默认值,只会在程序加载模块并读到本函数的定义时评估一次。对于{} 或 [] 等动态的值,可能会导致奇怪的行为
  • 对于以动态值作为实际默认值的关键字参数来说,应该把形式上的默认值写为 None,并在函数的文档字符串里面描述该默认值所对应的实际行为
from datetime import datetime
from time import sleep
def log(message, when=datetime.now()):print('{}: {}'.format(when, message))log('Hi there!')
sleep(0.1)
log('Hi again!')# 上面的例子输出的时间是一样的,因为 datetime.now 只执行了一次。
# 在 python 若想正确实动态默认值,习惯上把默认值设置为 None,并在文档字符串里面把对None 对应的实际行为
# 描述出来。编写函数代码时,如果返现该参数的值是 None,那就将其设为实际的默认值def log(message, when=None):"""Log a message with a timestamp.Args:message: Message to print.when: datetime of when the message occurred.Defaults to the present time."""from datetime import datetimewhen = datetime.now() if when is None else whenprint('{}: {}'.format(when, message))

21. 用只能以关键字形式指定的参数来确保代码清晰

  • 关键字参数能够使函数调用的意图更加明确
  • 对于各参数之间很容易混淆的函数,可以声明只能以关键字形式指定的参数,以确保调用者必须通过关键字来指定它们。对于接受多个 Boolena 标志的函数,更应该这样做
  • python3 有明确的语法来定义这种只能以关键字形式指定的参数
  • python2 的函数可以接受 **kwargs 参数,并手工抛出 TypeError 异常,以便模拟只能以关键字形式来指定的参数

python3 可以定义一种只能以关键字形式来指定的参数,从而确保调用该函数的代码读起来会比较明确。下面定义的这个 safe_division_c 函数,带有两个只能以关键字形式来指定的参数。参数列表里的*,标志着位置参数就此终结,之后的那些参数,都只能以关键字形式来指定。

# python3 写法
def safe_division_c(number, divisor, *, ignore_overflow=False,ignore_zero_division=False):try:return number /divisorexcept OverflowError:if ignore_overflow:return 0else:raiseexcept ZeroDivisionError:if ignore_zero_division:return float('inf')else:raise# python2 写法
def safe_division_d(number, divisor, **kwargs):ignore_overflow = kwargs.pop('ignore_overflow', False)ignore_zero_division = kwargs.pip('ignore_zero_division', False)if kwargs:raise TypeError('Unexpected **kwargs: %r' % kwargs)try:return number /divisorexcept OverflowError:if ignore_overflow:return 0else:raiseexcept ZeroDivisionError:if ignore_zero_division:return float('inf')else:raise


http://www.taodudu.cc/news/show-4746627.html

相关文章:

  • Java的一些基本概念和它们之间的关系
  • expressjs如何做mysql注入_node-mysql中防止SQL注入的方法
  • Bootstrap typeahead使用问题记录及解决方案
  • 测试开发【Mock平台】06开发:项目管理功能(二)Atnd页面搭建经验实战与学习线路梳理
  • 欢迎来到AI的世界:从树莓派,Arduino 到 HEXA | 了解机器人开发必看
  • 中国联通净增5G用户数超越中国电信,但夺回千年老二或是梦想
  • “数”说“云上盛宴” “智”创未来生活 中国联通:“线上+线下”为智博会赋能添彩
  • java iqq_iQQ 基于WebQQ3.0协议Java开发 跨平台QQ客户端
  • c语言数据结构_链表的应用——学生管理系统
  • java开源项目之IQQ学习记录之单例模式与log4j日志记录
  • SAP Commerce Cloud UI(Spartacus Storefront) 的用户会话管理
  • Ubuntu 12.04 安装 IQQ
  • 王者战力查询,安卓苹果都行
  • Js迷你图书管理系统
  • alias简化k8s命令,节约生命
  • RNNLSTM学习总结
  • Linux学习-HaProxy代理后端Nginx
  • Nginx安装学习
  • Nginx学习部署环境(一)
  • Nginx学习(1/2)
  • 神器 Nginx 的学习手册
  • 学习尚硅谷Nginx整理的笔记
  • nginx:nginx学习
  • nginx基本学习(一)
  • Nginx的学习
  • Nginx实战学习之负载均衡
  • nginx 学习 --->>> nginx 实现动静分离
  • Nginx学习(一)
  • Nginx学习总结
  • Nginx详解学习

2017-12-29-effective-python-02-functions相关推荐

  1. 2017/12/29

    2019独角兽企业重金招聘Python工程师标准>>> 2017/12/29 Firday weather: light rain ! 1.需求: 写一个脚本实现如下功能: 输入一个 ...

  2. 【一周头条盘点】中国软件网(2017.12.25~2017.12.29)

    每一个企业级的人 都置顶了 中国软件网 中国软件网 为你带来最新鲜的行业干货 趋势洞察 麦肯锡:人工智能三个关键业务方向一个最大问题 麦肯锡表示:关于人工智能的三个关键业务方面开始逐渐明朗化. 第一, ...

  3. 2017.12.29会议记录

    闭包 一.基本概念 闭包:闭包是指有权访问另一个函数作用域中的变量的函数. 创建方式:创建闭包的常见方式,就是在一个函数内部创建另一个函数. 二.相关概念 执行环境:执行环境(也称为环境)是JS中一个 ...

  4. 抓住窗口期 成就世界级 —— 29周年之际致全体同仁 wwj 2017.12.6

    [企业与公共组织数字化时代,如何做好产品],重点学习老王精神::::: 抓住窗口期  成就世界级 --在用友创立29周年之际致用友全体同仁 WWJ 2017.12.6

  5. 跟李宁老师学Python视频课程(12):Python常用模块-李宁-专题视频课程

    跟李宁老师学Python视频课程(12):Python常用模块-143人已学习 课程介绍         本系列课程一共20套,每一套视频课程会深入讲解Python的一类知识点.Python是当今炙手 ...

  6. 豆瓣评论9.5的《Effective Python》,帮你解决80%难题!

    上周五晚上 11 点,我在浏览蓝桥云课代码交流Q时,无意看到了一条这样的消息:"最近看到豆瓣推荐,正在看< Effective Python :编写高质量 Python 代码的 90 ...

  7. OSN博士必须掌握的必杀技(更新至2017/12/15)

    以下内容更新至2017.12.15 概率图模型 1 Representation 11 The Bayesian Network Representation 12 Undirected Graphi ...

  8. 自然语言处理技术(NLP)在推荐系统中的应用 原2017.06.29人工智能头条 作者: 张相於,58集团算法架构师,转转搜索推荐部负责人,负责搜索、推荐以及算法相关工作。多年来主要从事推荐系统以及机

    自然语言处理技术(NLP)在推荐系统中的应用 原2017.06.29人工智能头条 作者: 张相於,58集团算法架构师,转转搜索推荐部负责人,负责搜索.推荐以及算法相关工作.多年来主要从事推荐系统以及机 ...

  9. 【Python 2.7】str和unicode的互相转换,摘自《Effective Python》

    str 和 unicode 以下摘自<Effective Python> python3 有两种表示字符序列的类型:bytes 和 str.前者的实例包含原始的8位值:后者的实例包含Uni ...

  10. 《Effective Python》笔记

    本文介绍了如何进行有效的Python编程,以提高编码质量. 本文主要参考的书籍<Effective Python> [美] Brett Slatkin. 文章目录 用Pythonic方式思 ...

最新文章

  1. usaco Ordered Fractions 顺序的分数(两种解法)
  2. 容器LinkedList原理(学习)
  3. 远看像乱序执行,近看是内存屏障的 BUG 是如何解决的?
  4. 基于java ee的源码,基于java EE的云ERP系统
  5. 如何实现文件上传 - JavaWeb
  6. hybrid app支持html5,Hybrid App 接入
  7. VMware12虚拟机安装
  8. 开源 ASP.NET CMS
  9. 【esp32lvgl】-2.1 # esp32移植lvgl7驱动st7789屏幕(ESP-IDF框架)
  10. 对计算机科学的认识论文,关于对计算机的认识论文
  11. NCIS调查表辅助工具-病案首页数据上传-病案数据统计
  12. 计算机设备如何巡检,计算机联锁设备巡检作业指导书.pdf
  13. 数据库原理知识点总结一
  14. 暨大计算机研学教育,暨大数学复试线平台,统计学
  15. (每日一练c++)组合总和
  16. 芯片程序烧录的熔断机制与安全
  17. 电商API接口系列,数据分享
  18. 值得反复体会的几部电影
  19. 编程初学者为什么要首选Java?
  20. RTSP服务器(二)

热门文章

  1. 魔幻离现实仅一步之遥:细说Python的循环调用、循环引用和循环导入
  2. php的sic考试,关于V9项目M2 SIC考试题调查分析
  3. JQuery——一个快速、简洁的JavaScript库
  4. 双网卡同时上内外网的方法
  5. 双网卡实现同时连通两个网络
  6. SpringSecurity(Web权限方案)
  7. Python 如何突破反爬虫指纹 JA3
  8. 笔记本电脑分屏显示设置
  9. 腾讯试水6点下班,行业加班文化哪有那么容易破除?
  10. 服务器故障内存位置怎么确定,服务器内存故障定位方法