对于开发者而言,代码风格是一件很重要的事情,它往往会关系到你代码的可读性甚至是执行效率。对于python而言,经常会在招聘要求中有一条:pythonic的代码。那究竟什么是pythonic,什么样的代码才是pythonic的代码,我自己也一直有让自己的代码更加pythonic,总结平时的工作学习就有了如下这些。

为什么要让代码pythonic

在网上找到一些这方面的资料

  • Programs must be written for people to read, and only incidentally for machines to execute.
  • There should be one - and preferably only one - obvious way to do it.
  • The use of commonly understood syntax or coding constructs can aid readability and clarity. Some idioms can be faster or use less memory than their “non-idiomatic” counterparts. Python’s idioms can make your code Pythonic!

如上所说,pythonic的代码会明显增加代码整体的可读性。并且,经过大家多年来的实际经验已经摸索出一些比较明确的规则来让代码pythonic。另外,pythonic的代码往往除了提高可读性以外会有更快的运行速度和更少的内存占用。

pythonic的一些实践

Make a script both importable and executable

这里可以通过如下的示例来说明白:

1
2
3
4
5
6
# non-idiomatic
def main():
    print("now we're in module", __name__)
   
print('Excuted from the command line')
main()

对于上边这小段内容,如果它仅仅是单个存在的话,直接执行是没有问题的,但是当它被当作module在别的地方被import的话,main()函数会在被引入的时候自动执行。而通过在合适的位置添加if __name__ == '__main__':判断,则可以更好的组织自己的代码。对于我自己来说,我可能在某个module中顺势写了一些测试方法,在被别的地方引入的时候,这些测试方法我不想让它执行,把它们放在判断语句之后会是一个不错的选择。

1
2
3
4
5
6
7
# pythonic
def main():
    print("now we're in module", __name__)

if __name__ == '__main__':
    print('do some test')
    main()

Test for “truthy” and “falsy” values

python中关于真假值的判断,常用的集中类型可以参考这个表:

类型 TRUE FALSE
字符串 非空的字符串 空字符串
数字 非0 0
容器 len(x)!=0 len(x)=0

如何判断真假值,则有一些常用的方法:

1
2
3
4
5
6
7
8
9
# pythonic
name = 'Safe'
pets = ['Dog', 'Cat', 'Hamster']
owners = {'Safe': 'Cat', 'George': 'Dog'}
if name and pets and owners:
    print('We have pets!')
# non-idiomatic
if name != '' and len(pets) > 0 and owners != {}:
    print('We have pets!')

Use in where possible

尽量使用in关键字来判断一个元素是否存在于一个容器中,这个容器可以是list,dict,set,string或者是你自己实现了__contains__方法的类。比如:

1
2
3
4
5
6
7
8
# pythonic
name = 'Safe Hammad'
if 'H' in name:
    print('This name has an H in it!')
# non-idiomatic
name = 'Safe Hammad'
if name.find('H') != -1:
    print('This name has an H in it!')

或者用in配合for来遍历一个序列,它可以是list,set,dict,string或者是你自己实现了__iter__方法的类。

1
2
3
4
5
6
7
8
9
# pythonic
pets = ['Dog', 'Cat', 'Hamster']
for pet in pets:
    print('A', pet, 'can be very cute!')
# non-idiomatic
pets = ['Dog', 'Cat', 'Hamster'] i=0
while i < len(pets):
    print('A', pets[i], 'can be very cute!')
    i += 1

Swap values without temp variable

感觉这个特性很厉害(我自己用的很少,以后要多注意),可以通过一个简单的表达式交换两个元素的值,并且不需要中间变量的参与:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# pythonic
a, b = 5, 6
print(a, b)
a, b = b, a
print(a, b)
# 5, 6 # 6, 5
# 5, 6
# non-idiomatic
a, b = 5, 6
print(a, b)
temp = a a = b
b = temp print(a, b)
# 6, 5

Build strings using sequence

在连接字符串的时候,通过str.join()方法耗用的时间是和传递给join方法的字符串长度线性相关的;而重复的使用‘+’来拼接字符串耗用的时间与字符串长度平方关系递增。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# pythonic
chars = ['S', 'a', 'f', 'e']
name = ''.join(chars)
print(name)           # Safe
# non-idiomatic
chars = ['S', 'a', 'f', 'e']
name = ''
for char in chars:
    name += char
print(name)           # Safe

转换值类型的最佳实践

通过一个实例可以更好的说明这条原则:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# pythonic
d = {'x': '5'}
try:
	value = int(d['x'])
except (KeyError, TypeError, ValueError):
	value = None
# non-idiomatic
d = {'x': '5'}
if 'x' in d and \
   isinstance(d['x'], str) and \
   d['x'].isdigit():
    value = int(d['x'])
else:
	value = None

在我自己的实际使用中,也经常会出现这种情况,通过网页抓去到的id号或者是商品的价格往往是str类型的,我想把它当作数字存在库里仅仅通过value = int(strValue)是很不安全的。而通过上述的方法可以以更安全的方法得到自己想要的结果。 另外,在python中抛出异常的代价对比与java这种要小很多。

Enumerate

Enumerate 从python2.3引进,它多用于for循环中得到元素在容器中所在的位置,使用Enumerate可以有效的避免大量的count += 1这样的代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# pythonic
names = ['Safe', 'George', 'Mildred']
for i, name in enumerate(names):
    print(i, name)   #  0 Safe, 1 George etc.
# non-idiomatic
names = ['Safe', 'George', 'Mildred']
count = 0
for name in names:
    print(i, name)   #  0 Safe, 1 George etc.
    count += 1

Build lists using list comprehensions

更多的使用列表推导式来构建list,在通常情况下,列表推导式会比使用container.append(x)更加简洁。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# pythonic
data = [7, 20, 3, 15, 11]
result = [i * 3 for i in data if i > 10]
print(result)   # [60, 45, 33]
# non-idiomatic
data = [7, 20, 3, 15, 11]
result = []
for i in data:
    if i > 10:
        result.append(i * 3)
print(result)   # [60, 45, 33]

Create dict from keys and values using zip

如果想要将两个现有的list合并为键值对组合,通过zip方法会是一个不错的选择:d = dict(zip(keys, values))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# pythonic
keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = dict(zip(keys, values))
print(d)  # {'Bob': 'Builder',
          #   'Safe': 'Hammad',
          #   'Thomas': 'Engine'}
# non-idiomatic
keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = {}
for i, key in enumerate(keys):
    d[keys] = values[i]
print(d)   # {'Bob': 'Builder',
           #   'Safe': 'Hammad',
           #   'Thomas': 'Engine'}

路漫漫而修远

当然,上述的这些仅仅是冰山一角,在自己写代码的过程中应当尽量遵守这些准则,努力提高自己的代码质量。