第一章:用Pythonic方式来思考
第2条 PEP8
《Python Enhancement Proposal #8》8号Python增强提案。
空白:①4个空格代表tab ②每行<=79字符 ③分行后加4个空格 ④类中方法空1行,方法和类空2行
命名:①函数、变量、属性用小写字母和下划线组成 ②受保护用单下划线,私有用双下划线 ③类与异常用驼峰 ④模块级别常量用全大写 ⑤类中实例方法首个参数是self,表示改对象;类方法的首个参数是cls,表示该类。
表达式和语句:①not不要放在表达式前面 ② 采用if not list 判断列表是否为空③import放开头且使用绝对路劲引用(但可以form . import models)④import顺序按照标准库、第三方模块库、自用模块导入,每部分按字母顺序排列。
第3条:了解bytes、str、unicode区别
isinstance(obj, class)判断class类型是否是obj的(父)类
str.encode(‘utf-8’)转变为bytes类型 bytes.decode(‘utf-8’)转变为str类型
在读写文件中,默认是’utf-8’,如果使用二进制必须加上‘b’
python3 bytes是8位值的序列,str是包含Unicode字符序列。
python2 str是8位值的序列,unicode是包含Unicode字符序列。若str只包含7位ASCII,可以使用相关操作同时使用。
第4条:用辅助函数来取代复杂的表达式
repr(obj),将obj对象转换成字符串类型
or左边表达式为真则用左边的,否则用右边的 a = False or ‘1’ # a=1
if/else表达式 a = x if x>y else y
第5条:了解切割序列的办法
assert 表达式, 当表达式等于False时,抛出AssertionError
start或end索引越界也不会出问题
第6条:在单次切片操作内,不要同时指定start、end和stride
同时指定start、end和stride会导致代码难以阅读,stride尽量使用正值。
可考虑使用itertools模块中的islice。(第46条:使用内置算法与数据结构:)
第9条:用生成器表达式来改写数据量较大的列表推导
it = (x for x in range(20000))
next(it)
两个生成器可以串起来用,产生连锁反应。
第13条:合理利用try/except/else/finally
else后面的代码是在try顺利完成后才运行。
except用法
except ZeroDivisionError as e:
raise ValueError('dsa') from e
第二章:函数
第15条:了解如何在闭包里使用外围作用域中的变量
用nonlocal语句
nonlocal与global相互补充,nonlocal修改闭包外那个作用域变量,global修改模块作用域的变量
也可以用列表、字典、集合来取代,这样作用域会自动向外搜索
第16条:考虑生成器来改写直接返回列表的函数
多用生成器,可转list。生成器⊆迭代器
第17条:在参数上面迭代时,要多加小心
注意生成器只能遍历一次!如果重复迭代,第二次不会报错,会产生一个空列表。解决办法:直接传入迭代器函数进去。
迭代器其实就是fun.__ iter __这个特殊方法
第18条:用数量可变的位置参数减少视觉杂讯
*arg 可变长,转变为元组
**kwargs 字典
也可用默认值代替*arg
第20条:用None和文档字符串来描述具有动态默认值的参数
参数中有默认值,函数被反复调用,默认值只会产生一次。解决办法用None
def gen(bad={}):
return bad
def main():
f1 = gen()
f1['1'] = 1
f2 = gen()
f2['2'] = 3
print(f1) # {'1': 1, '2': 3}
print(f2) # {'1': 1, '2': 3}
第三章:类与继承
第22条:尽量用辅助类来维护程序的状态,而不是用字典和元组
namedtuple
Grade = collections.namedtuple('Grade', ('score', 'weight'))
a = Grade(100, 0.2) # Grade(score=100, weight=0.2) 可迭代 a.score
如果类太复杂,可以拆成多个类。如:Gradebook->Student->Subject
第23条:简单接口应该接受函数,而不是类的实例
字典初始化定义
res = collections.defaultdict(fun, dict)
# fun函数返回的是字典初始化值
assert 条件,如果条件为false,则程序报错
第24条:以@classmethod形式的多态去通用地构建对象
-
多态:多种状态,接口的多种不同实现方式。写出通用代码。
-
定义公共基类、继承
class InputData:
def read(self):
raise NotImplementedError
class PathInputData(InputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
print(self.path)
return 0
- @classmethod是类的方法,第一个参数是cls。每个类只有一个__ init__方法,所以需要@classmethod来实现抽象类的方法。
第25条:用super初始化父类
- 出现砖石型的继承关系时候,使用super会同时初始化两个类。
第26条:只在使用Mix-in组件制作工具类时进行多重继承
-
尽量避开多重继承
-
若一定使用,则用mix-in类。(额~其实这里我想不出有什么用)
第27条:多用public属性,少用private属性
-
protect属性是单下划线,private是双下划线。
-
@property将protect属性写成函数访问
-
如果要访问私有属性,则继承A类,然后obj._A__private可访问,因为python会对私有属性的名称做变换
-
多用protect属性,少用private,当子类不受控制时,才考虑用private
第28条:继承collection.abc以实现自定义的容器 类型
- collection.abc中有大量抽象类,如果没有实现基类的功能会报错
第四章:元类及属性
- 元类:class语句就是元类,每次定义具体类时,都提供独特的行为。
第29条:用纯属性取代get和set方法
- @property返回属性;@attr.setter设置属性
- hasattr(class, ‘attr’)判断父类有无该属性
第30条:考虑用@property来代替属性重构
- repr给程序员看的,str给使用者看的,避免输出误以为是报错。repr>str
第31条:用描述符来改写需要复用的@property
- @property修饰器不能复用
- classmethod第一个参数是cls,可以调用自身类,而staticmethod不可以调用自身类
第32条:用_ getattr_ 、_ getattribute_ 和_ setattr _实现按需生成的属性
-
getattr当对象找不到对应属性时,会在这里找
-
def __getattr__(self, item): value = '123' setattr(self, item, value) # 设置这个值 return value
-
getattribute无论属性是否在__ dict__中,都会触发该函数,如果没有该属性会raise AttributeError.调用该方法一定要super(),否则会递归的一直调用。
33-35书上的内容过旧
第33条:用元类来验证子类
- new和init区别:new是创建类前运行的,init是初始化对象的属性
- metaclass用来验证子类