Python 编程中经验型的一些规范
持续更新
使用li[:]代替copy.copy(li)
li[:]
等价于copy.copy(li)
li[:]
不等价于copy.deepcopy(li)
li[:]
可以简化代码
从后开始访问列表
- list作为stack用时访问栈顶元素list[-1]
1
2
3
4
5
6
7
8
9list1 = [1, 5, 2, 3]
print list1[-1]
print list1[-2]
print list1[1:-1]
# output:
3
2
[5, 2]
函数内函数
- Python可以在函数内部定义函数,但是要注意不能与外部变量混淆
1
2
3
4
5
6
7
8
9
10
11
12
13
14def sum_all(alist):
def sum_two(a, b):
return a+b
result = 0
for i in alist:
result = sum_two(result, i)
return result
print sum_all([1, 2, 3, 4, 5])
# output:
15
不要随意调用特殊方法
(比如:__len__)
- 特殊方法不应该被开发者调用,而应该被开发者定义新类时实现
- 一般使用
len(object)
即可 - 原因: 内置的方法(比如:len())可能会直接返回对象中的
ob_size
属性,而不用调用__len__()
(这个函数往往会用迭代或者其他比较复杂的方法实现)
多使用列表推导
(list comprehension, 简写listcomps)和生成器表达式(generator expression, 简写genexps)
listcomps
1
[str(i) for i in range(1,10)]
genexps
1
(str(i) for i in range(1,10))
考虑使用reduce而不是循环语句
- reduce
1
2
3def add(x, y):
return x + y
reduce(add, [1,2,3,4,5])
1 | from operator import mul |
多使用pprint
- (Pretty Print)而不是print
1
2
3
4
5
6
7
8
9
10
11import pprint
l1 = (1, {2: 3}, "first", ("second", 5, 6, [7, 8, 9]), [1, 3, 5], "this is a pprint")
pprint.pprint(l1)
# Output:
(1,
{2: 3},
'first',
('second', 5, 6, [7, 8, 9]),
[1, 3, 5],
'this is a pprint')
多使用三目运算符
Python不像Java和C++一样,有x?y:z这样的三目运算符号,但是可以有自己的特殊使用方法,等价于三目运算符且更容易理解
1
2
3# same as x?y:z
result = y if x is True else z
return y if x is True else z- 注意: 在使用return语句中的三目运算时必须有else语句,否则编译不通过,因为返回值可能会缺失
匿名变量的使用
- 初始化一个列表时
1
list1 = [_ for _ in list2 if _.val > 10]
Python异常处理
异常处理的正确姿势, 注意如果不是必要的话不要使用Exception, 可以考虑列出来需要捕获的所有异常, 然后在函数内部判断异常类型
1
2
3
4
5try:
read some thing
except IOError, ValueError, e:
exception_type = type(e)
print("%s" % e)当然, 我们一般为了方便也会直接使用下面的方法
1
2
3
4
5try:
read some thing
except Exception, e:
exception_type = type(e)
print("%s" % e)
位运算
- 在Python中, 位运算与C++中有所不同
- 参见Python——位运算与逻辑运算和C++有什么不同
函数
- 内部函数访问全局变量时使用
global
关键字声明(与外部函数一样) - 内部函数访问外部函数的变量使用
nonlocal
(仅限Python 3)关键字声明 - 如果没有声明
- 变量变为只读的
- 可以写出
a.append()
这样的语句 - 但不可以写出
a = b
这样的语句
- 可以写出
- 变量变为只读的
- 函数内部变量与函数外同名时:
- 若写出赋值操作,则认为当前变量为局部变量
- 否则认为是函数外的全局变量
- 若先访问变量(一般访问,不是对变量赋值,此时视为全局变量),然后对变量赋值(此时视为局部变量),则产生矛盾,Python解释器报错
三元表达式的使用
如果在三元表达式使用在加法中,需要加上括号,不然整体意思会变成错误的
比如两个结点的加法操作,带有进位
carry
,我们可以简单的写一行:下面是错误示例:
1
val = l1.val if l1 else 0 + l2.val if l2 else 0 + carry
上面的表达式可以理解为如果
l1
不为空,返回l1.val
,否则返回0 + l2.val if l2 else 0 + carry
当
l1
为空时又可以理解为如果l2
不为空,返回0+l2.val
否则返回0+carry
正确的写法
1
val = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry
迭代dict的键时用dict.iterkeys()
迭代键值时使用
iterkeys()
而不是keys()
iterkeys()
返回一个迭代器而不是所有键值列表keys()
返回所有键值列表1
2
3
4# 迭代效率高
for _ in dict.iterkeys()
# 迭代效率低
for _ in dict.keys()迭代值时也同理
1
2
3
4# 迭代效率高
for _ in dict.itervalues()
# 迭代效率低
for _ in dict.values()
zip实现二维列表的行列变换
只适用于Python3,因为涉及到*
操作
1 | a = [[1,2,3], |
- Python3中才能使用
*
作为操作列表解压符 - Python3中
zip
函数返回的是一个生成器而不是列表,所以需要迭代成列表
Python中逻辑运算符的巧用
不建议使用
1 | print 0 or 1 |
理解
1
2
3
4
5
6
7print a or b
<==>
print a if a else b
print a and b
<==>
print b if a else a记忆:
- 把
a
,b
当做逻辑表达式,如果访问到b
则返回b
,否则返回a
- 把
reversed函数的使用
接受参数为可迭代对象,返回一个反向访问迭代对象的迭代器
1
2
3
4
5for i in reversed(range(n)):
<==>
for i in range(n-1, -1, -1):reversed
的使用似乎更优雅,也更容易理解容易遗忘的点,需要注意: reversed的参数必须是可迭代的对象,而不是两个数字
将简单的句子优雅的写到一行
返回值
1
2
3
4if i < 0: return Flase
<==>
if i < 0:
return False其他简单的执行语句直接合并
1
i = 10; j = 10
判断语句之后使用
1
if bool: i = 10;
判断语句后使用多条
1
if bool: i = 10; j = 20
while语句后使用
1
while(bool): print 10; print 20
当句子较长时不建议使用
一般为了美观,平时的项目也不建议使用
刷题时为了让代码看起来简便,是可以使用的,但是这样会使得代码不易调试
总之:慎用
用同一个值初始化两个变量
1 | x = y = value |
- 在初始化链表头部时最常用
1
head, curr = ListNode(None)
使用collections.Counter
1 | import collections |