[关闭]
@spiritnotes 2016-06-15T13:26:08.000000Z 字数 4943 阅读 1740

《Effective Python》

Python 读书笔记 DOING


第1章 用Pythonic方式来思考

1 确认自己所使用的Python版本

使用python3来运行3.x版本
在运行程序时通过sys.version_info获得当前版本信息

2 遵循PEP8风格指南

命名

表达式与语句

3 了解bytes、str与unicode的差别

可以使用两个辅助函数进行转换

  1. ### python3
  2. def to_str(str_or_bytes):
  3. if isinstance(str_or_bytes, bytes):
  4. value = str_or_bytes.decode('utf-8')
  5. else:
  6. value = str_or_bytes
  7. return value
  8. def to_bytes(str_or_bytes):
  9. ...
  10. # bytes和str是两种类型,写程序时需要注意
  11. ### python2
  12. def to_unicode(str_or_unicode):
  13. ...
  14. def to_str(str_or_unicode):
  15. ..
  16. # 对于接受str的函数,也可以使用只使用ascii的unicode,如果接受unicode,可以传入str

4 用辅助函数来取代复杂的表达式

可读性 辅助函数 > if/else表达式 > or/and复杂表达式

5 了解切割序列的方法

somelist[start: end]

6 在单次切片操作内,不要同时指定start、end、stride

7 用列表推导来取代map和filter

如题

8 不要使用含有两个以上表达式的列表推导

9 使用生成器表达式来改写数据量较大的列表推导

生成器计算为迭代器
生成器有状态,只能onepass

10 尽量用enumerate取代range

替代range生产i然后访问x[i]
enumerate可以接受参数用于指定开始使用的计数值

11 用zip函数同时遍历两个迭代器

python2中zip是返回元组,可以使用itertools.izip
长度不同,使用 itertools.izip-longest/zip_longest

12 不要在for和while循环后面写else块

13 合理利用try/except/else/finally结构中的每个代码块

第2章 函数

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

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

  1. def sort_priority(values, group):
  2. def helper(x):
  3. if x in group:
  4. return (0, x)
  5. return (1, x)
  6. values.sort(key=helper)

变量赋值规则

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

使用yield表达式达到同样效果

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

  1. def do(get_iter):
  2. sum_ = sum(get_iter())
  3. for i in get_iter():
  4. pass
  1. if iter(numbers) is iter(numbers): ## iter使用在迭代器上返回自身
  2. raise TypeError('Must Be a Container')
  3. ...

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

使用 ×args

  1. def log(messsage, *args):
  2. return message % args
  3. log('ccccc', *(1,2,3))

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

  1. def f(a, b=1, c=2, d=3): pass
  2. f(1, c=3)

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

即期望默认值在每次执行函数时运算,而该期望与Python语言特性是矛盾的
正确应该设置为None,判断为None时进行计算
如果默认值类型是可变的,则一般情况下需要使用None代替

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

python3中 fun(a, v, *, z, m, **kwargs) z,m必须以关键字赋值,可以有默认值
Python2实现

  1. ## 1
  2. def decr_fun(n):
  3. def dec(fun):
  4. def _fun(*args, **kwargs):
  5. if 'd' not in kwargs:
  6. raise ...
  7. ...
  8. return _fun
  9. return dec
  10. @decr_fun(3)
  11. def fun(a, b, c, d, *args, **kwargs):
  12. pass
  13. ## 2
  14. def fun(a, b, c, **kwargs):
  15. d = kwargs.pop('d', 0)
  16. e = kwargs.pop('e', 0)
  17. if kwargs:
  18. raise

第3章 类与继承

22 尽量用辅助类来维护程序的状态,而不要用字典和元组

字典用于保存某个对象在其生命周期里的动态内部状态。
动态是指这些待保存的信息,其标识符无法提前获知。
将嵌套结构重构为类。
可以使用namedtuple来过渡。问题:1)无默认参数,2)会被人通过下标和迭代访问,后续会产生修改问题。
嵌套字典或者元组,可以依次改为嵌套类。

23 简单的接口应该接受函数,而不是类的实例

Python中很多接口都支持传递函数,如回调函数、sort的key参数
函数可以是无状态函数,也可以是含有状态信息的闭包。
使用类实例的绑定方法用作回调,可以用于保存状态。
使用定义了__call__方法的类实例,直接进行计算。

24 以@classmethod形式的多态去通用地构建对象

多态:使得继承体系中的多个类都能以各自所独有的方式来实现某个方法。满足相同接口,或继承自相同的抽象类。
Python中每个类只有一个 __init__ 来构造对象。
可以通过@classmethod来创建对象,也可以用于创建对象的子类。

25 用super初始化父类

多重继承情况下,直接通过father.__init__调用会产生问题。钻石继承在多路径上的类的方法会被调用多次。
通过super按照mro(方法解析顺序)的方向来调用初始化。
可以通过mro方法来查看类的顺序。
python3中可以直接通过super()函数进行调用,其是通过__class__和self来调用,而python2未实现__class__,而self.__class__是指的类的实际类型

26 只在使用Mix-in组件制作工具类时进行多重继承

Mix-in类:只定义其他类可能需要提供的一套附加方法。不定义自己的实例属性,不要求调用__init__方法
实现的原理:Python动态特性,可以允许本类的方法访问的属性不是本类定义。
例子:将对象的属性以及其嵌套属性都递归转换为字典表示,再用于json进行序列化

27 多用public属性,少用private属性

private属性,双下划线开头,其会被重命名,子类不能访问父类的私有属性
原理:Python看到访问__开头的属性时,先会加上该类的类名后再寻找新变量名_ClassName__VariName,因此可手动产生变量明访问
习惯性使用下划线开头来告诉使用者注意
private合理使用是用于避免子类和父类属性重名,如_value,在实例中只会存在一个,在父类中__value表示,则子类中可以定义自己的_value

28 继承collections.abc以实现自定义的容器类型

实现比较简单的类型可以直接继承内部类型
abc中定义了一些抽象基类,其可以帮我么检查是否所有需要的方法都已经定义,而且其还提供了很多相关方法

第4章 元类及属性

29 用纯属性取代get和set方法

  1. class AAA:
  2. @property
  3. def x(self):
  4. return self._x
  5. @x.setter
  6. def x(self, value):
  7. self._x = value

可以用属性来进行类型检查、数值检查、延迟计算、防止父类属性被覆盖等
写属性时应该合理,最小惊讶原则,getter不应该改变其他属性的值

30 考虑用@property来代替属性重构

把简单的数值属性迁移为实时计算(按需计算、动态计算等)
如果@property使用过于频繁,则需要考虑是否应该重构

31 用描述符来改写需要复用的@property方法

  1. class Grade:
  2. def __get__(self, instance, instance_type):
  3. #
  4. def __set__(self, instance, value):
  5. instance._value = value # 不能赋值给self,该实例会被共享
  6. self._values[instance] = value # 会对instance的删除产生问题,需要使用weakref,WeakKeyDictionary
  7. class Exam:
  8. math_grade = Grade() # 所有类实例共享同一份实例
  9. science_grade = Grade()
  10. # exam.math_grade = v
  11. # => Exam.__dict__['math_grade'].__set__(exam, v)

如果实例对象没有该名称的属性,则转向其类定义查找同名的类属性,如果其是定义了__get__和__set__方法的对象,则Python认为该对象遵循描述符协议

32 用__getattr__, __getattribute__, __setattr__实现按需生成的属性

普通实例属性、@property和描述符都需要预先定义

todo

第5章 并发及并行

并发与并行的关键区别在于能不能提速。

36 用subprocess模块来管理子进程

subprocess.Popen, communicate()/poll(),可以使用管道来进行输入输出
communicate有timeout参数,避免死锁或失去响应,3.3之后有

37 可以用线程来执行阻塞式I/O,但不要用它来平行计算

GIL是以把互斥锁

38 在线程中使用Lock来防止数据竞争

Python虚拟机中原子操作在于其自己的字节码,而不是可见的语句为单位

39 用Queue来协调各线程之间的工作

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