10天读完《编写高质量代码 改善Python编程的91个建议》——Day4
计划
Hello,这是一个读书摘要的计划,10天读完《编写高质量代码 改善Python编程的91个建议》,我会每天摘录大概9个书中提到的建议,分享在这里,也作为自己的打卡任务。关于这本书,他并不是python入门的教学书籍,而是一本用来改善编程习惯和风格的书,可以帮助我们写出漂亮的,也就是符合pythonic的python代码。书中的知识难度不会很大,每天看几条,然后落实到实际编程中加以巩固,就能融会贯通,久而久之也就能养成好的编程习惯。如果你没有时间看原书,那么直接看我的摘要就好啦,遇到感兴趣的再去详细了解也会方便很多,当然这里摘要的内容是我觉得有用的部分啦。注意,一定要会运用!
书摘
建议29 区别对待可变对象和不可变对象
-
不可变对象有:数字,字符串,元组
可变对象有:列表,字节数组 -
可变对象作为函数默认参数时可能变成不可变(内存中同一个地址),解决办法是传入None作为默认参数
例如:
# 错误
def fn(arg=[]):
""""""
# 正确
def fn(arg=None):
if arg == None:
arg = []
建议30 [] () {} 一致的容器初始化形式
- 列表解析
a)语法:[expr for iter_item in iterable if cond_expr]
b)支持多重嵌套
c)支持多重迭代
例如,求['a', '1', 1, 2] 和 ['1', 3, 4, 'b'] 的笛卡尔积
l1 = ['a', '1', 1, 2]
l2 = ['a', '1', 1, 2]
[(x,y) for x in l1 for y in l2 if x != y]
d)解析中的表达式可以是简单表达式,也可以是复杂表达式,甚至是函数
e)解析中的iterable可以是任意可迭代对象
例如,
f = open('test.txt','r')
res = [i for i in f if 'abc' in i]
print res
- 元组,集合,字典也有类似的解析语法
元组:(expr for iter_item in iterable if cond_expr)
集合:{expr for iter_item in iterable if cond_expr}
字典:{expr1,expr2 for iter_item in iterable if cond_dir}
- 注意,在元组中,(1)与(1,)是完全不同的,后者才是元组
建议31 记住函数传参既不是传值也不是传引用
- python与C/C++的赋值原理不一样
同样一段代码
a = 5
b = a
b = 7
在C/C++中,『a=5』表示从内存中申请一块内存并将a的值5复制到该内存中,『b=a』表示b申请一块内存,然后把a的值复制到b的内存,『b=7』表示把b内容中的值改为7。
在Python中,『a=5』表示内存中存一个数值5,然后a指向这个内存,『b=a』表示b也指向该内存,『b=7』表示内存中存一个数值7并把b指向这个内存。下面验证一下,
a = 5
id(a)
b = a
id(b)
b = 7
id(b)
id(a)
- Python传的是对象或者说是传对象的引用
函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,调用者和被调用者之间共享这个对象,而对于不可变对象,由于不能真正地修改,因此修改往往是通过生成一个新对象然后赋值来实现的。
建议32 警惕默认参数潜在的问题
- 默认参数是共享的,例如
def fn(num, data=[]):
data.append(num)
return data
fn(1)
# [1]
fn(2)
# [1,2] 而不是 [1]
- 不想共享?使用None作为占位符
def fn(num, data=None):
if data is None:
data = []
data.append(num)
return data
建议33 慎用变长参数
- 可变长度参数,使用*arg和**kwargs
a)*arg 用于接受一个『元组』形式的参数列表来传递非关键字参数,个数任意
b)**kwargs 用于接受一个『字典』形式的关键字参数
- 为什么要慎用?
a)使用过于灵活,他人阅读比较花时间
b)如果一个函数参数列表过长,通常意味着该函数可以有更好的实现方式,应该被重构
c)适合下列情况使用:
i)为函数添加一个装饰器
ii)参数数目不确定
iii)用来实现函数的多态,或者在继承情况下子类需要调用父类的某些方法
建议34 深入理解str()和repr()的区别
- 两者多数情况下相同,区别大致有以下几点:
a)str()主要面向用户,目的是可读性;repr()面向python解释器,目的是准确性(debug用)
b)解释器中直接输入a时默认调用repr(),而print a时则为str()
c)repr()的返回值一般可以用eval()函数来还原对象,如 obj=eval(repr(obj))
建议35 分清 staticmethod 和 classmethod 的适用场景
- Python中静态方法staticmethod,类方法classmethod都依赖于装饰器来实现,如
@staticmethod
def fn1(arg): return
@classmethod
def fn2(arg): return
- 类方法可用在继承中(父类中使用)
建议36 掌握字符串的基本使用
- 小技巧
Python遇到未闭合的小括号时会自动将多行代码拼接为一行,并把相邻两个字符串字面量拼接在一起(取出换行符和前导空格),例如
s = ('SELECT * '
' FROM person '
' WHERE gender="male" ')
print s
-
Python2 中判断变量s是不是字符串应该使用 isinstance(s, basestring) 注意不是str,因为basestring分为str和unicode
-
字符串基本用法掌握
a)性质判断:isalnum(), isalpha(), isdigit(), islower(), isupper(), isspace(), istitle(), startswith(), endswith() ...
b)查找与替换:count(), find(), index(), rfind(), rindex(), replace(old, new [, count]) ...,这些都支持start,end参数,注意要判断是不是子字符串应该使用in,not in
c)分切与连接:partition(sep), rpartition(sep), splitlines(), split(), rsplit() ...,注意split()与split('')返回值不同
d)变形:lower(), upper(), capitalize(), swapcase(), title(), String.capwords(), strip(), lstrip(), rstrip() ...
建议37 按需求选择sort() 和 sorted()
- sorted 使用更为广泛,两者形式:
sorted(iterable[, cmp[, key[, reverse]]])
s.sort([cmp[, key[, reverse]]])
注意,cmp是用户定义的比较函数(不常用),reverse表反转(默认从小到大)
-
sorted作用于任务可迭代对象,sort用于列表
-
sorted返回一个排序完的新表,sort直接修改原列表
-
sorted 可针对不同数据结构排序,如字典
score = {'Alice':100, 'Bob':87, 'Cat':95}
sorted(score.items(), key = lambda item:item[1])
print score