编写优质python代码的10个技巧

1. 用enumerate代替range(len())

如果在遍历列表时,同时获得索引和数据,你可以使用range(len()) 这种语法

lst = [87, 84, 34]
for index in range(len(lst)):
    print(index, lst[index])

这种写法虽然可以实现同时遍历索引和元素,但编写过程并不流畅,如果使用enumerate就可以避免使用索引来获取元素

lst = [87, 84, 34]
for index, item in enumerate(lst):
    print(index, item)

实现相同的功能,使用enumerate让代码看起来更加的清爽。

2. 使用列表推导式代替for循环

创建一个列表,列表里的元素是从0到9的整数序列,使用for循环,你可以这样编写

lst = []
for i in range(10):
    lst.append(i)

print(lst)

使用列表推导式,可以写出更加简洁的代码,不仅如此,列表推导式的性能更快

lst = [i for i in range(10)]
print(lst)

3. 使用sorted函数对序列进行排序

列表支持sort方法,可以对列表里的元素进行排序,而sorted函数更加强大,它对序列排序后,返回一个新的列表

lst = [5, 1, 3]
lst.sort()      # 对列表排序
print(lst)

tup = (5, 1, 3)     # 元组不支持sort方法
sort_lst = sorted(tup, reverse=True)
print(sort_lst)

同列表的sort方法一样,sorted也支持key参数来指定排序比较大小时的数值

lst = [(3, 5), (6, 2), (1, 6)]
lst.sort(key=lambda x: x[0])      # 对列表排序
print(lst)

tup = ((3, 5), (6, 2), (1, 6))     # 元组不支持sort方法
sort_lst = sorted(tup,key=lambda x: x[0], reverse=True)
print(sort_lst)

如果需要被排序的序列不是列表,或者你不希望需改原有的列表,那么你应当选择sorted函数进行排序

4. 使用生成器节省内存

如果你需要产生一个比较大的序列,那么强烈建议你使用生成器而非列表。列表会占用非常大的内存,而生成器不会。

import sys

lst1 = [i for i in range(10000) if i %2 == 0]    # 偶数序列
lst2 = [i for i in range(10000) if i %2 != 0]    # 奇数序列

lst = [(i, j) for i, j in zip(lst1, lst2)]
print(sys.getsizeof(lst1) + sys.getsizeof(lst2) + sys.getsizeof(lst), 'bytes')

lst1 = (i for i in range(10000) if i %2 == 0)
lst2 = (i for i in range(10000) if i %2 != 0)
lst = ((i, j) for i, j in zip(lst1, lst2))
print(sys.getsizeof(lst1) + sys.getsizeof(lst2) + sys.getsizeof(lst), 'bytes')

两种写法所使用的内存量相差非常大

129120 bytes
264 bytes

生成器是延迟计算,你只能对其使用for循环遍历,而不能像对待列表一样使用切片方法,因为事先并不存在一个完整的序列,只有在for循环进行时,才会产生相应的数据。

5. 使用get方法和setdefault方法获取和设置字典的value

在使用字典进行统计时,强烈建议你使用setdefault方法设置一个key的初始值,现在,请使用字典统计列表里各个单词出现的次数,如果不使用setdefault,你必须这样来写代码

lst = ['python', 'python', 'java', 'c++', 'java', 'php']

word_count_dict = {}
for word in lst:
    # 如果key不存在则设置初始值
    if word not in word_count_dict:
        word_count_dict[word] = 0

    word_count_dict[word] += 1

这样实现,到也并非是个坏方法,但对word进行一次是否是字典key的判断,还是有些多余,如果使用setdefault方法,则可以避免

lst = ['python', 'python', 'java', 'c++', 'java', 'php']

word_count_dict = {}
for word in lst:
    word_count_dict.setdefault(word, 0)
    word_count_dict[word] += 1

少了一行if条件语句,让代码看起来更加简洁了,如果word已经是字典的key,那么setdefault方法不会起到任何作用,不会改变key对应的value,但如果key不存在,则会设置key对应的value为0。

在从字典里通过key取值时,如果key不存在,则会引发KeyError 异常,如果不会异常进行捕获,就会导致程序崩溃。对于这种情况,建议你使用get方法,它是一种安全的方法,如果key在字典里不存在,则返回默认值None,你也可以设置key不存在时返回的默认值。

print(word_count_dict.get('node.js'))   # 返回None
print(word_count_dict.get('node.js', 3))   # 返回3

6. 使用collections.Counter对列表进行统计

第5小结的示例,讲解了如何用字典统计列表里单词的出现次数,对于这类操作,使用collections.Counter更加方便,collections模块提供了非常多有用的类

from collections import Counter

lst = ['python', 'python', 'java', 'c++', 'java', 'php']
counter = Counter(lst)

print(counter)              # Counter({'python': 2, 'java': 2, 'c++': 1, 'php': 1})
print(counter['python'])    # 2

Counter提供了most_common方法,可以返回出现次数最多的元素

most_common = counter.most_common(2)
print(most_common)      # [('python', 2), ('java', 2)]

7. 使用 f-Strings 格式化字符串

从3.6 版本后,就不要在使用% 和 format方法对字符串进行格式化了,因为f-Strings提供了更加方便快捷的格式化方法,下面的代码,将对比这三种格式化的操作

name = '小明'
age = 14

msg = "%s今年%d岁" % (name, age)
print(msg)

msg = "{name}今年{age}岁".format(name=name, age=age)
print(msg)

msg = f"{name}今年{age}岁"
print(msg)

孰优孰劣,诸位自行判断吧

8. 使用jion方法连接字符串

假如需要将列表里的单词连接成一个新的字符串, 你可以遍历列表,拼接字符串

lst = ['I', 'like', 'python']

my_string = ""
for word in lst:
    my_string += word + " "

my_string = my_string[:-1]  # 去掉最后一个空格

print(my_string)

这样拼接,不仅代码臃肿,由于每次都要在接入的单词后面加一个空格,导致整个句子最后也会有一个空格,不得不进行一次切片操作。每一次for循环,都会在内存中创建一个新的字符串,申请一片新的内存空间用以存储拼接后的字符串,如果数据量很大,这样的操作费时又费力。

如果使用join方法,则便捷的多

lst = ['I', 'like', 'python']

my_string = " ".join(lst)
print(my_string)

9. 使用解包操作简化代码

合并两个字典,可以使用下面的方法

dict_1 = {'python': 90}
dict_2 = {'java': 98}

dict_3 = {**dict_1, **dict_2}  # {'python': 90, 'java': 98}
print(dict_3)

解包的用处不仅于此,还可以用它来快速的传递函数参数

def test(name='', age=0):
    print(name, age)

info = {
    'name': '小明',
    'age': 18
}

test(**info)

10 . 使用三元表达式

python中是没有三元表达式的,但可以通过if ... else 伪装成三元表达式

lst = [2, 5, 6, 7]

bool_lst = []
for index, item in enumerate(lst):
    if item % 2 == 0:
        bool_lst[index] = True
    else:
        bool_lst[index] = False

创建一个bool_lst, 如果lst[i]是偶数,则bool_lst[i] 设置为True,反之设置为False, 上面的代码从逻辑上,没有任何问题,但使用伪三元表达式,可以简化代码编写

lst = [2, 5, 6, 7]

bool_lst = []
for index, item in enumerate(lst):
    bool_lst[index] = True if item % 2 == 0 else False

想要写出简洁的代码,要对python的高级语法融会贯通,下面是另外两种写法

预先创建等长列表

lst = [2, 5, 6, 7]

bool_lst = [False for item in lst]
for index, item in enumerate(lst):
    if item % 2 == 0:
        bool_lst[index] = True

使用列表推导式

lst = [2, 5, 6, 7]

bool_lst = [True if item % 2 == 0 else False for item in lst ]

列表推导式虽然让代码更简洁,但要防止走向极端,如果推导式本身很复杂,甚至融入了业务逻辑,那么还是应该拆分成多行语句,尽管代码变多了,但易于理解和阅读。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案