- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议13:尽量转换为浮点类型后再做除法
GPA(平均成绩绩点)在出国留学或者奖学金申请中都占有重要的地位。GPA算法有多种形式,其中标准计算方法是将大学成绩乘以课程学分并求和再乘以4,再除以总学分与100之积,一般精确到小数点后两位。假如学生A的各门课程成绩如下:
A课程4学分,成绩96(等级A,绩点4);B课程3学分,成绩85(等级B,绩点3)
C课程5学分,成绩98(等级A,绩点4);D课程2学分,成绩70(等级C,绩点2)
那么该学生的GPA是多少呢?很容易的算术问题对吧,小学生都会!那么你算出来的结果是多少?Python计算出的结果又是多少呢?
>>> gpa = ((4*96+3*85+5*98+2*70)*4)/((4+3+5+2)*100) >>> print gpa 3
上面的结果跟你计算出的答案一致吗?显然是否定的,你的计算结果为3.62571428571,即使四舍五入保留小数点后两位也应该是3.63。如果有所大学规定的最低GPA是3.5的话,用Python作为GPA计算工具的话可就实实在在会误人前程了。问题出现在哪里呢?这要回到Python设计之初。
Python在最初的设计过程中借鉴了C语言的一些规则,比如选择C的long类型作为Python的整数类型,double作为浮点类型等。同时标准的算术运算,包括除法,返回值总是和操作数类型相同。作为静态类型语言,C语言中这一规则问题不大,因为变量都会预先申明类型,当类型不符的时候,编译器也会尽可能进行强制类型转换,否则编译会报错。但Python作为一门高级动态语言并没有类型申明这一说,因此在上面的例子中你不能提前申明返回的计算结果为浮点数,当除法运算中两个操作数都为整数的时候,其返回值也为整数,运算结果将直接截断,从而在实际应用中造成潜在的质的误差。
Python中除了除法运算之外,整数和浮点数的其他操作行为还是一致的,因此这容易让人产生一种误解,数值的计算与具体操作数的类型(整数还是浮点数)无关,但事实上对于整数除法这是编程过程中潜在的一个危险,因为当你编写一个函数时,即使你希望调用者传入的是浮点类型,但如果不在函数入口进行类型检查或者转换,就无法阻止函数调用者传递整数参数,而往往这种类型的错误还不容易发觉。因此推荐的做法之一是当涉及除法运算的时候尽量先将操作数转换为浮点类型再做运算。
>>> gpa = float(((4*96+3*85+5*98+2*70)*4))/float(((4+3+5+2)*100)) >>> print gpa 3.62571428571
当然随着Python语言的发展,对整数除法问题也做了一定的修正,在Python3中这个问题已经不存在了。Python3之前的版本可以通过from __future__ import division机制使整数除法不再截断,这样即使不进行浮点类型转换,输出结果也是正确的(请读者自行试验)。
最后,还需要说明一点,上例中是使用浮点数才精确,但下列场景又变成了浮点数可能是不准确的。先来看以下代码:
>>> i=1 >>> while i!=1.5: ... i = i +0.1 ... print i
上面的代码输出会是多少?正确的答案是这段代码会导致无限循环。为什么呢?因为在计算机的世界里,浮点数的存储规则决定了不是所有的浮点数都能准确表示,有些是不准确的,只是无限接近。如0.1转换为二进制表示形式则为0.000110011001……后面1001无限循环。在内存中根据浮点数位数规定,多余部分直接截断,因此当循环到第5次的时候i的实际值为1.5000000000000004(读者可以逐步调试进行验证),则条件表达式i !=1.5显然为True,循环陷入无终止状态。对于浮点数的处理,要记住其运算结果可能并不是完全准确的。如果计算对精度要求较高,可以使用Decimal来进行处理或者将浮点数尽量扩大为整数,计算完毕之后再转换回去。而对于在while中使用i!=1.5这种条件表达式更是要避免的,浮点数的比较同样最好能够指明精度。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论