Python进阶编程:编写更高效、优雅的Python代码
上QQ阅读APP看书,第一时间看更新

3.1.7 转换并计算数据

在实际应用中,对序列的操作方式有很多,有时需要先对序列数据做转换或过滤,再对序列做聚集,如执行sum()、min()、max()等操作。

一个非常优雅的做数据计算与转换的操作就是使用一个生成器表达式参数。如计算平方和可以这样操作,示例如下:


num_list = [1, 2, 3, 4, 5]
print(sum(x * x for x in num_list))

更多的示例如下:


import os
file_list = os.listdir('dirname')
if any(name.endswith('.py') for name in file_list):
    print('There be python!')
else:
    print('Sorry, no python.')

# Output a tuple as CSV
course = ('python', 20, 0.3)
print(','.join(str(x) for x in course))

# Data reduction across fields of a data structure
course_info = [
    {'name':'python', 'score': 100.0},
    {'name':'java', 'score': 85.0},
    {'name':'c', 'score': 90.0},
    {'name':'c++', 'score': 95.0}
]
min_score = min(cf['score'] for cf in course_info)
print(min_score)

该示例演示了当生成器表达式作为一个单独参数传递给函数时候的巧妙语法。下面这些语句是等效的:


# 显式传递一个生成器表达式对象
print(sum((x * x for x in num_list)))
# 更加优雅的实现方式,省略了括号
print(sum(x * x for x in num_list))

使用一个生成器表达式作为参数比先创建一个临时列表更加高效和优雅。如不使用生成器表达式,可能会使用如下实现方式:


num_list = [1, 2, 3, 4, 5]
print(sum([x * x for x in num_list]))

这种方式同样可以达到想要的效果,但是它会多一个步骤——创建一个额外的列表。这对于小型列表可能没什么关系,但是如果元素数量非常大,则需创建一个巨大的、仅仅使用一次就被丢弃的临时数据结构。而生成器方案会以迭代的方式转换数据,因此更省内存。

在使用一些聚集函数比如min()和max()的时候可能更加倾向于使用生成器版本,它们接收一个key关键字参数。如对于前面的示例,我们可以考虑如下的实现方式:


print(min(cf['score'] for cf in course_info))
print(min(course_info, key=lambda cf: cf['score']))

扩展:字符串连接优先使用join,而不是+。

字符串的连接在编程过程中会经常遇到。Python中的字符串是不可变对象,一旦创建便不能更改。这个特性对Python中的字符串连接有一些影响。当连接次数比较多时,join操作的效率明显高于“+”操作。

使用“+”操作,每执行一次便会在内存中申请一块新的内存空间,并将上一次操作的结果和本次的右操作数复制到新申请的内存空间,致使在N次连接操作过程中,需要申请N-1个内存,从而严重影响效率。使用“+”操作字符串的时间复杂度近似为O(n2)。

当用join()方法连接字符串时,首先会计算需要申请的总内存空间,然后一次性申请所需内存并将字符序列中的每一个元素复制到内存中,所以join操作的时间复杂度为O(n)。

对于字符串的连接,特别是大规模字符串的处理,尽量优先使用join操作。