读书笔记--《编写高质量代码:改善Python程序的91个建议》

 

第一章 引论

建议写Pythonic式的代码,我理解为充分利用pythonAPI,用最简洁方式写出代码

1、两个变量交换:

a, b = b, a

2、翻转list:

a = [1, 2, 3, 4] print(list(reversed(a)))

3、格式化字符串

.format

print ( 'my name is {} '.format('jack'))

3.6版本新的格式化字符串方法

name = 'Mollylin' print(f'hello,{name}~!') >>>'hello,Mollylin~!'

第二章

1、assert

其基本语法如下:

assert expression1, ["," expression2],使用的例子如下:

x, y = 1, 2 assert x == y, "not equal"

其中会计算expression1 的值,返回true 或 false,值为false的时候会引起AssertionError,expression2是可选的,用来传递具体的异常信息

断言不可滥用:

  • 断言是有代价的,它会对性能产生一定的影响,通常禁用断言的方法是在运行脚本的时候加上 -O 标志,这种方式带来的影响是它并不优化字节码,而是忽略与断言相关的语句。
  • 不要滥用。若由于断言引发了异常,通常代表程序中存在 bug。因此断言应该使用在正常逻辑不可到达的地方或正常情况下总是为真的场合。
  • 如果 Python 本身的异常能够处理就不要再使用断言。如对于类似于数组越界、类型不匹配、除数为 0 之类的错误,不建议使用断言来进行处理。

# 下面的断言是多余的, 如果类型不匹配本身就会抛出异常 def stradd(x, y):    assert isinstance(x, basestring)    assert isinstance(y, basestring)    return x + y

  • 不要使用断言来检查用户的输入。如对于一个数字类型,如果根据用户的设计该值的范围应该是 2~10,较好的做法是使用条件判断,并在不符合条件的时候输出错误提示信息。
  • 在函数调用后,当需要确认返回值是否合理时可以使用断言
  • 当条件是业务逻辑继续下去的先决条件时可以使用断言,比如 list1 和其副本 list2,如果由于某些不可控的因素,如使用了浅拷贝而 list1 中含有可变对象等,就可以使用断言来判断这两者的关系。

2、isinstance()

所有基本类型对应的名称都可以在 types 模块中找到,然而使用 type() 函数并不适合用来进行变量类型检查。

  • 基于内建类型扩展的用户自定义类型,type 函数并不能准确返回结果
  • 在古典类中,所有类的实例的 type 值都相等

对于内建的基本类型来说,使用 type() 进行类型检查问题不大,但在某些特殊场合 type() 方法并不可靠。解决方法是,如果类型有对应的工厂函数,可以使用工厂函数对类型做相应转换,否则可以使用 isinstance() 函数来检测。

  • type() 不会认为子类是一种父类类型,不考虑继承关系。
  • isinstance() 会认为子类是一种父类类型,考虑继承关系
>>>a = 2 
>>> isinstance (a,int)
True
>>> isinstance (a,str)
False
>>> isinstance (a,(str,int,list)) # 是元组中的一个返回 True True

3、eval()的安全漏洞

Python 中eval() 函数将字符串 str 当成有效的表达式来求值并返回计算结果。其函数声明如下:eval(expression[, globals[, locals]])。

>>>eval("'A'+'B'") 
'AB'
>>>eval("1+2")
3
>>>eval("1+1==2")
True

由于网络环境下运行它的用户并非都是可信任的,比如输入 __import__("os").system("dir") ,它会显示当前目录下的所有文件列表;输入 __import__("os").system("del * /Q"),会导致当前目录下的所有文件都被删除了,而这一切没有任何提示。

4、enumerate()

使用 enumerate() 获取序列迭代的索引和值

 li = ['a', 'b', 'c', 'd', 'e']

 for i, e in enumerate(li):

 print "index:", i, "element:", e

推荐使用函数 enumerate(),主要是为了解决在循环中获取索引以及对应值的问题。它具有一定的惰性(lazy),每次仅在需要的时候才会产生一个(index, item)对。其函数签名如下:enumerate(sequence, start=0)

其中,sequence 可以为序列,如 list、set 等,也可以为一个 iterator 或者任何可以迭代的对象,默认的 start 为 0,函数返回本质上为一个迭代器,可以使用 next() 方法获取下一个迭代元素。

5、== 和is

is 表示的是对象标示符(object identity),而 == 表示的意思是相等(equal)

is 的作用是用来检查对象的标示符是否一致的,也就是比较两个对象在内存中是否拥有同一块内存空间,它并不适合用来判断两个字符串是否相等。

第三章 基础语法

1、++1

Python 解释器会将 ++i 操作解释为 +(+i),其中 + 表示正数符号。对于 --i 操作也是类似。

因此需要明白 ++i 在 Python 中语法上是合法的,但并不是我们理解的通常意义上的自增操作。

2、finally

  • 当 try 块中发生异常的时候,如果在 except 语句中找不到对应的异常处理,异常将会被临时保存起来,当 finally 执行完毕的时候,临时保存的异常将会再次被抛出,但如果 finally 语句中产生了新的异常或者执行了 return 或者 break 语句,那么临时保存的异常将会被丢失,从而导致异常屏蔽。
  • 在实际应用程序开发过程中,并不推荐在 finally 中使用 return 语句进行返回,这种处理方式不仅会带来误解而且可能会引起非常严重的错误。

3、None

其中常量 None 的特殊性体现在它既不是 0、False,也不是空字符串,它就是一个空值对象。其数据类型为 NoneType,遵循单例模式,是唯一的,因而不能创建 None 对象。所有赋值为 None 的变量都相等,并且 None 与任何其他非 None 的对象比较结果都为 False。

4、join()

读书笔记--《编写高质量代码:改善Python程序的91个建议》

join() 方法的效率要高于 + 操作符。

  • 当用操作符 + 连接字符串的时候,由于字符串是不可变对象,其工作原理实际上是这样的:如果要连接如下字符串:S1+S2+S3+...+SN,执行一次 + 操作便会在内存中申请一块新的内存空间,并将上一次操作的结果和本次操作的右操作数复制到新申请的内存空间,在 N 个字符串连接的过程中,会产生 N-1 个中间结果,每产生一个中间结果都需要申请和复制一次内存,总共需要申请 N-1 次内存,从而严重影响了执行效率,时间复杂度近似为 O(n^2)。
  • 而当用 join() 方法连接字符串的时候,会首先计算需要申请的总的内存空间,然后一次性申请所需内存并将字符序列中的每一个元素复制到内存中去,所以 join 操作的时间复杂度为 O(n)。

5、可变对象和不可变对象

  • 数字、字符串、元祖属于不可变对象
  • 字典、列表、字节数组属于可变对象

当有两个对象同时指向一个字符串对象的时候,对其中一个对象的操作并不会影响另一个对象。

第四章 库

1、sort()、sorted()

sorted() 函数会返回一个排序后的列表,原有列表保持不变;而 sort() 函数会直接修改原有列表,函数返回为 None。

上一篇:改善Python 程序的 91 个建议


下一篇:《编写高质量代码:改善Python程序的91个建议》读后感