[关闭]
@spiritnotes 2016-01-31T09:20:08.000000Z 字数 20760 阅读 6617

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

Python 读书笔记


引论


01 理解 Pythonic 概念

02 编写 Pythonic 代码

03 理解 Python 与 C 的不同之处

04 在代码中适当添加注释

05 通过适当添加空行使代码布局更为优雅、合理

06 编写函数的4大原则

07 将常量集中到一个文件

如何使用常量

  1. #filename const.py
  2. class _const:
  3. def __setattr__(self, name, value):
  4. if name in self.__dict__:#禁止修改
  5. raise Exception
  6. if not name.isupper():#要求必须大写
  7. raise Exception
  8. self.__dict__[name] = value
  9. import sys
  10. sys.modules[__name__] = _const()

使用

  1. import const
  2. const.COMPANY = "IBM"

编程惯用法


08 利用 assert 语句来发现问题

捕获用户定义约束,而非程序本身错误

注意点:

09 数据交换时不推荐使用中间变量

10 充分利用Lazy Evaluation的特性

避免不必要的计算,带来性能上的提升

  1. #words都是以.结尾,这里先判断最后一个字符是否为.,提升性能
  2. if w[-1]=='.' and w in word:

节省空间,使得无限循环的数据结构成为可能(生成器等)

11 理解枚举替代实现的缺陷

  1. class Seasons:
  2. Spring, Summer, Autumn, Winter = range(4)
  1. def enum(*posarg, **keyarg):
  2. return type("Enum", (object), dict(zip(posarg, xrange(len(posarg))),**keyarg))
  3. Seasons = enum("Spring", "Summer", "Autumn", Winter=3)

12 不推荐使用 type 来进行类型检查

  1. if type(a) is types.ListType: pass

13 尽量转换为浮点类型后再做除法

14 警惕 eval 的安全漏洞

  1. __import__("os").system("dir")
  2. [c for c in ().__class__.__base__[0].__subclasses__() if c.__name__ == 'Quitter'][0](0)()]

可以使用 ast.literal_eval

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

16 分清 == 和 is 的适用场景

  1. a = float('NaN')
  2. a is a
  3. a != a

17 考虑兼容性,尽可能使用 unicode

  1. # coding=utf-8
  2. #-*- coding:utf-8 -*-

18 构建合理的包层次来管理 module


基础语法


19 有节制地使用 from...import 语句

import原理
1. 搜索sys.modules是否已经存在该模块
2. 未找到,为模块创建字典对象插入sys.modules
3. 根据需要对模块对应文件进行编译
4. 执行动态加载,在当前模块的命名空间执行编译后的字节码,并将其中所有的对象放入模块对应的字典中
from...import的缺点
- 命名空间冲突
- 循环嵌套导入

  1. #c1.py
  2. from c2 import g
  3. def x():
  4. pass
  1. #c2.py
  2. from c1 import x
  3. def g():
  4. pass

20 优先使用 absolute import 来导入模块

21 i+=1 不等于 ++i

++i在python中被解析为+(+i)),为两个正号,对于----也是一样的

22 使用 with 自动关闭资源

  1. with exp1 as a, exp2 as b:
  2. pass
  3. # 相当于
  4. with exp1 as a:
  5. with exp2 as b:
  6. pass
  1. import contextlib
  2. @contextlib.contextmanager
  3. def work():
  4. print('First part!')
  5. yield
  6. print('last part!')
  7. with work():
  8. print('middle part')

23 使用 else 子句简化循环(异常处理)

  1. ## while
  2. while True:
  3. pass
  4. else: #break未执行时执行else语句,循环自然结束
  5. pass
  6. ## for
  7. for i in range(x):
  8. pass
  9. else: #break未执行时执行else语句,循环自然结束
  10. pass
  11. ## try
  12. try:
  13. pass
  14. except:
  15. pass
  16. else: #未抛出异常时执行else语句
  17. pass

24 遵循异常处理的几点基本原则

25 避免 finally 中可能发生的陷进

  1. while True:
  2. try:
  3. k = 1/0
  4. finally:
  5. break #导致丢失异常,没有break则异常会被重新抛出
  1. def test(n):
  2. try:
  3. if n <= 0: raise Exception
  4. else: return n
  5. finally:
  6. return -99
  7. test(-1) # 返回 -99
  8. test(2) # 返回 -99

26 深入理解None,正确判断对象是否为空

27 链接字符串优先使用 join 而不是 +

join只计算一次内存,而+使用多次

28 格式化字符串时尽量使用 . format 格式而不是 %

29 区别对待可变对象和不可变对象

30 [],{},()一致的容器初始化形式

  1. a = ['aa','aaa']
  2. b = ['bb','bbb']
  3. [(i+j) for i in a for j in b]
  4. >>>['aabb', 'aabbb', 'aaabb', 'aaabbb']
  5. [[j for j in i]for i in a]
  6. >>>[['a', 'a'], ['a', 'a', 'a']]
  7. [j for i in a for j in i]
  8. >>>['a', 'a', 'a', 'a', 'a']

31 记住函数传参既不是传值也不是传引用

传递对象,或者说传递对象的引用

32 警惕默认参数潜在问题

33 慎重使用变长参数

*args, **kwargs

缺点
- 使用过于灵活
- 使用变长参数一般说明函数可以更好的实现,应该被重构

适用
- 为函数添加装饰器
- 参数的数目不确定
- 用来实现函数的多态,或者在继承的情况下子类需要调用父类的某些方法

34 深入理解 str() 和 repr() 的区别

  1. obj = eval(repr(obj))

35 分清 staticmethod 和 classmethod 的适用场景

依赖于装饰器实现
实例方法
每个实例自己的状态
类方法
继承树上每个类自己的状态
静态方法
继承树上所有类公有的状态
静态方法从实现上说可以定义在类的外部

36 掌握字符串的基本用法

  1. a = ("a" 'b'
  2. 'c')
  1. a.startswith(('ab', 'b'))
  1. a = ' b c d '
  2. a.split(' ')
  3. >>>['', 'b', '', '', 'c', 'd', '', '']
  4. a.split() #去掉左右空白字符,以任意个空白字符作为分割
  5. >>>['b', 'c', 'd']

37 按需选择 sort 和 sorted

  1. sorted(iterable, cmp, key, reverse)
  2. s.sort(cmp, key, reverse)

区别

  1. sorted(dict_)/sorted(dict_.items())
  2. # 多维list
  3. l =[ ['12', '43'], ['02', '44'], ['00', '41']]
  4. print([i for i in sorted(l, key=operator.itemgetter(1,1))])
  5. >>>[['00', '41'], ['12', '43'], ['02', '44']]
  6. # 字典中混合 list(value为list)
  7. sorted(dict_.items(),key=lamda(k,v): operator.itemgetter(1)(v))
  8. # list 中 字典
  9. sorted(list_, key=operator.itemgetter("rating","name"))

38 使用 copy 模块深拷贝对象

39 使用 Counter 进行计数统计

40 深入掌握 ConfigParse

  1. [section1]
  2. option1 = 1

41 使用 argparse 处理命令行参数

getopt / optparse / argparse / docopt

42 使用 pandas 处理大型 csv 文件

43 一般情况下使用 ElementTree 解析 XML

44 理解 pickle 优劣

序列化: 将内存中的数据结构在补丢失身份和类型信息的情况转成对象的文本或者二进制表示的过程

45 序列化的另一个不错的选择 JSON

  1. try:
  2. import simplejson as json
  3. except ImportError:
  4. import json

46 使用 traceback 获取栈信息

47 使用 logging 记录日志信息

48 使用 threading 模块编写多线程程序

  1. # 继承并重写 run
  2. class MyThread(thread.Thread):
  3. def run(self):
  4. pass
  5. # 调用对象创建传入 可执行对象
  6. thread_ = threading.Thread(fuc_)

49 使用 Queue 使多线程编程更加安全

Queue本身是线程安全的

设计模式

50 利用设计模式实现单例模式

  1. class _Singleton:
  2. pass
  3. Singleton = _Singleton()
  4. del _Singleton
  5. _Singleton_class = Singleton.\_\_class\_\_
  1. class Singleton():
  2. _instance = None
  3. def __new__(cls, *args, **kwargs):
  4. if not cls._instance:
  5. cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
  6. return cls._instance
  7. s1 = Singleton()
  8. s2 = Singleton()
  9. assert id(s1) == id(s2)
带锁多线程版本:
  1. class Singleton():
  2. objs = {}
  3. obj_locker = threading.Lock()
  4. def __new__(cls, *args, **kwargs):
  5. if cls in cls.objs:
  6. return cls.objs[cls]
  7. cls.obj_locker.acquire()
  8. try:
  9. if cls in cls.objs:
  10. return cls.objs[cls]
  11. cls.objs[cls] = object.__new__(cls)
  12. finally:
  13. cls.obj_locker.release()
问题:
 1. 子类若重载了 new 方法,则可能会覆盖或者干扰父类的 new 的执行,必须要求子类必须调用父类的 new 方法
 2. 子类若有 init 方法,则会被调用多次,解决方法:换掉子类的 init 方法
  1. class BROG():
  2. _shared_state = {}
  3. def __init__(self):
  4. self.__dict__ = self._shared_state

51 用 mixin 模式让程序更加灵活

  1. __base__ += (mixin_class1, mixin_class2, ...)

52 用发布订阅模式实现松耦合

  1. ## Broker.py
  2. from collections import defaultdict
  3. class Broker():
  4. route_table = defaultdict(list)
  5. def sub(self, topic, callback):
  6. if callback in route_table[topic]:
  7. return
  8. route_table[topic].append(callback)
  9. def pub(self, topc, *a, **kw):
  10. for func in route_table[topic]:
  11. func(*a, **kw)
  12. ## use
  13. def greeting(name):
  14. print(name, ' hello ')
  15. broker.sub('greet', greeting)
  16. broker.pub('are you ok')

53 用状态模式美化代码

状态模式: 当一个对象的内在状态改变时允许改变其行为,但这个对象看上去像改变了其类。主要用于控制一个对象的状态的条件表达式过于复杂的情况,其可把状态的判断逻辑转移到表示不同状态的一系列类中,进而把复杂的判断逻辑简化

  1. def workday() : pass
  2. def weekday() : pass
  3. people = People()
  4. while True:
  5. for i in range(1, 8):
  6. if i == 6:
  7. people.day = workday
  8. if i == 1:
  9. people.day = weekday
  1. @stateful
  2. class People():
  3. class Workday(State):
  4. default = True
  5. @behavior
  6. def day(self): pass
  7. class Weekday(State):
  8. @behavior # 为静态函数,self是指的people
  9. def day(self): pass
  10. people = People()
  11. while True:
  12. for i in range(1, 8):
  13. if i == 6:
  14. switch(people, people.Weekday)
  15. if i == 0:
  16. switch(people, people.Workday)
  17. people.day()

内部机制

54 理解 build-in objects

对象 type<*> *.__class__ *.__bases__ isinstance(*,object) isinstance(*,type)
object 'type' 'type' () true true
type 'type' 'type' (object,) true true
旧式类A实例a 'instance' A () true false
B(objet);b=B() B B (obejct,) true false
C(type);c=C() C C ('type',) true true
D(dict);d=D() D D (dict,) true false
  1. # 旧类转变为新类
  2. class TestNewClass:
  3. __metaclass__ == type

古典类元类: types.ClassType
新类元类: type

55 __init__()不是构造方法

__new__ 真正创建类的实例,是类的构造函数
__init__ 是实例的初始化函数

  1. .__new__(cls[, args...])
  2. .__init__(self[, args...])

差别

何时需要重写 new

  1. class UserSet(frozenset):
  2. def __new__(cls, *args):
  3. if isinstance(args[0], str):
  4. args = (args[0].split(),) + args[1:]
  5. return super(UserSet, cls).__new__(cls, *args)
  6. print(UserSet('I am a boy'))
  7. >>>UserSet({'boy', 'a', 'am', 'I'})
  1. class ShapeFactory():
  2. shapes={'A':A, 'B':B}
  3. def __new__(cls, type_):
  4. if type_ in cls.shapes:
  5. return cls.shapes[type_]()
  6. else:
  7. raise UnkonwnTypeError
  8. b = ShapeFactory('B')

初始化的 init 函数

56 理解名字查找机制

LEGB

赋值语句

57 为什么需要 self 参数

  1. def len(a):
  2. pass
  3. class A():
  4. pass
  5. A.len = len
  6. A().len()
多种方法调用类
  1. A.m(a, 2)
  2. a.m(2)
  3. A.__dict__['m'](a, 2)
调用父类方法时
  1. base_.method_(self, <arg_list>)
  2. super(cls, self).method_(<arg_list>)

58 理解 MRO 与多继承

MRO: method resolution order方法解析顺序

古典类: 深度优先
新式类: C3

  1. class C(A, B): pass
  2. class D(B, A): pass
  3. class E(C, D): pass

59 理解描述符机制

  1. class Property():
  2. def __init__(self, fget=None,...):
  3. self.fget = fget
  4. self.__doc__ = doc
  5. def __get__(self, obj, objtype):
  6. if obj is None:
  7. return self
  8. if self.fget is None:
  9. rasie ...
  10. return self.fget(obj)
  11. def __set__(self, obj, value):
  12. if self.fset is None:
  13. raise ...
  14. self.fset(obj, value)
  15. def __delete__(self, obj):
  16. if self.fdel is None:
  17. raise ...
  18. self.fdel(obj)

60 区别 __getattr__() 和 __getattribute__() 方法

均可以用作 实例 属性的获取与拦截,非 类属性

注意事项

调用次序

  1. class A():
  2. _c = 'test'
  3. def __init__(self):
  4. self.x = None
  5. @property
  6. def a(self):
  7. print('use property to get!')
  8. if self.x is None:
  9. print('return a when x is None!')
  10. return 'a'
  11. else:
  12. if isinstance(self.x, int):
  13. print('Error x is int', self.x)
  14. raise AttributeError
  15. else:
  16. print('return x when is not int')
  17. return self.x
  18. @a.setter
  19. def a(self, value):
  20. self.x = value
  21. def __getattr__(self, name):
  22. print('getattr to get', name)
  23. return str(self.x)
  24. def __getattribute__(self, name):
  25. print('getattribute to get', name)
  26. return object.__getattribute__(self, name)
  27. a1 = A()
  28. print(a1.a)
  29. print('----')
  30. a1.a = 1
  31. print(a1.a)
  32. print('----')
  33. a1.a = 'c'
  34. print(a1.a)
  35. print('----')
  36. print(a1._c)
  37. print('----')
  38. print(A._c)
  39. getattribute to get a
  40. use property to get!
  41. getattribute to get x
  42. return a when x is None!
  43. a
  44. ----
  45. getattribute to get a
  46. use property to get!
  47. getattribute to get x
  48. getattribute to get x
  49. getattribute to get x
  50. Error x is int 1
  51. getattr to get a
  52. getattribute to get x
  53. 1
  54. ----
  55. getattribute to get a
  56. use property to get!
  57. getattribute to get x
  58. getattribute to get x
  59. return x when is not int
  60. getattribute to get x
  61. c
  62. ----
  63. getattribute to get _c
  64. test
  65. ----
  66. test

61 使用更安全的 property

  1. x = property(get_v, set_v, del_v, doc)
  2. @property @x.setter @x.deleter

62 掌握 metaclass

元类

  1. def dynamic_class():
  2. class A():
  3. pass
  4. return A
  5. A_ = dynamic_class()
  6. type(A_) == <type, 'type'>
  1. A = type('A', (<父类元组>), dict_<属性字典,名称和值>)
  1. class Apython2:
  2. __metaclass__ = MyTypeClass
  3. pass
  4. class Bpython3(metaclass=MyTypeClass):
  5. pass
  1. class TypeSetter():
  2. def __init__(self, type):
  3. self.type = type
  4. def is_valid(self, value):
  5. return isinstance(value, self.type)
  6. class TypeCheckMeta(type):
  7. def __new__(cls, name, bases, dict):
  8. print('aloc class memory', dict) ## name,age等会在该dict中
  9. return super(TypeCheckMeta, cls).__new__(cls, name, bases, dict)
  10. def __init__(cls, name, bases, dict):
  11. cls._fields = {}
  12. for key, value in dict.items():
  13. cls._fields[key] = value
  14. class TypeCheck(metaclass=TypeCheckMeta):
  15. def __setattr__(self, key, value):
  16. if key in self._fields:
  17. if not self._fields[key].is_valid(value):
  18. raise Exception
  19. super(TypeCheck, self).__setattr__(key, value)
  20. class Test(TypeCheck):
  21. name = TypeSetter(str) ### 该属性会被写入 dict 中然后调用元类
  22. age = TypeSetter(int)
  23. t = Test()
  24. t.name = 'test'
  25. t.age = '16'

元类选择

元类中定义的方法属于其创建的类的类方法,并不属于该类的对象
元类的使用场景

  1. class Singleton(type):
  2. def __init__(cls, name, bases, dic):
  3. super(Singleton, cls).__init__(name, bases, dic)
  4. cls.instance = None
  5. def __call__(cls, *a, **kw):
  6. if cls.instance is None:
  7. cls.instance = super(Singleton, cls).__call__(*a, **kw)
  8. return cls.instance
  9. class MySingleton():
  10. __metaclass__ = Singleton

注意点:

  1. class M1(type): pass
  2. class M2(type): pass
  3. class C1(metaclass=M1): pass
  4. class C2(metaclass=M2): pass
  5. class C3(C1, C2): pass # 会产生异常,Python不能确认两个元类是否兼容
  6. class M3(M1, M2): pass
  7. class C4(metaclass=M3): pass #现在可以了

63 熟悉 Python 对象协议

Python各种操作是通过各种预定义函数实现

64 利用操作符重载实现中缀语法

pipe库实现原理

  1. class Pipe():
  2. def __init__(self, function):
  3. self.function = function
  4. def __ror__(self, other):
  5. return self.function(other)
  6. def __call__(self, *a, **kw):
  7. return Pipe(lambda x: self.function(x, *a, **kw))
  8. @Pipe
  9. def where(iterable, predicate):
  10. return (x for x in iterable if predicate(x))
  11. fib() | take_while(lambda x: x < 10000) \
  12. | where(lambda x: x%2) \
  13. | sum()

解释:

65 熟悉 Python 迭代器协议

迭代器协议:

66 熟悉 Python 的生成器

67 基于生成器的协程以及 greenlet

  1. # 消费者生成者实例
  2. def consumer():
  3. while True:
  4. word = yield
  5. print(word.upper())
  6. def producter(line):
  7. for word in line.split():
  8. yield word
  9. c = consumer()
  10. p = producter('i am very okay')
  11. next(c)
  12. for word in p:
  13. c.send(word)

68 理解 GIL 的局限性

全局解释器锁(GIL: Global Interpreter Lock)

69 对象的管理和垃圾回收

  1. while True:
  2. A = Leak()
  3. B = Leak()
  4. A.b = B
  5. B.a = A
  6. A, B = None, None
  1. gc.isenabled()
  2. gc.get_threshold()
  3. gc.collect() #手动调用垃圾清理
  4. gc.garbage #返回由于循环引用而产生的不可达的垃圾对象的列表

使用工具

70 从 pypi 安装包

71 使用 pip 和 yolk 安装管理包

72 做 paster 创建包

73 理解单元测试概念

74 为包写单元测试

75 利用测试驱动开发提高代码的可测试性

76 利用 pylint 检查代码风格

77 进行高效的代码审查

如何看待代码审查

78 将包发布到 pypi

性能剖析和优化

79 了解代码优化的基本原则

80 借助性能优化工具

81 利用 cprofile 定位性能瓶颈

82 使用 memeory_profile 和 objgraph 剖析内存使用

83 努力降低算法复杂度

84 掌握循环优化的基本技巧

85 使用生成器提高效率

86 使用不同的数据结构优化性能

87 充分利用 set 的优势

88 使用 multiprocessing 克服 GIL 的缺陷

89 使用线程池提高效率

90 使用 C/C++ 模块扩展提高性能

91 使用 Cython 编写扩展模块

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注