第 10 章 性能与优化

“过早地优化是万恶之源。”

——Donald Knuth,摘自Structured Programming with go to Statements

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

10.1 数据结构

如果使用正确的数据结构,大多数计算机问题都能以一种优雅而简单的方式解决,而Python就恰恰提供了很多可供选择的数据结构。

通常,有一种诱惑是实现自定义的数据结构,但这必然是徒劳无功、注定失败的想法。因为Python总是能够提供更好的数据结构和代码,要学会使用它们。

例如,每个人都会用字典,但你看到过多少次这样的代码:

def get_fruits(basket, fruit):
  # A variation is to use "if fruit in basket:"
  try:
    return basket[fruit]
  except KeyError:
    return set()

最好是使用dict结构已经提供的get方法。

def get_fruits(basket, fruit):
  return basket.get(fruit, set())

使用基本的Python数据结构但不熟悉它提供的所有方法并不罕见。这也同样适用于集合的使用。例如:

def has_invalid_fields(fields):
  for field in fields:
    if field not in ['foo', 'bar']:
      return True
  return False

这可以不用循环实现:

def has_invalid_fields(fields):
  return bool(set(fields) - set(['foo', 'bar']))

set数据结构包含许多能解决不同问题的方法,否则这些问题需要通过嵌套的for/if块才能实现。

还有许多高级的数据结构可以极大地减少代码维护负担。例如,可以看看下面的代码:

def add_animal_in_family(species, animal, family):
  if family not in species:
    species[family] = set()
  species[family].add(animal)

species = {}
add_animal_in_family(species, 'cat', 'felidea')

当然,这段代码是完全有效的,但想想看你会在你的程序中需要多少次上面代码的变种?10次?100次?

Python提供的collections.defaultdict结构可以更优雅地解决这个问题。

import collections

def add_animal_in_family(species, animal, family):
  species[family].add(animal)

species = collections.defaultdict(set)
add_animal_in_family(species, 'cat', 'felidea')

每次试图从字典中访问一个不存在的元素,defaultdict都会使用作为参数传入的这个函数去构造一个新值而不是抛出KeyError。在这个例子,set函数被用来在每次需要时构造一个新的集合。

此外,collections模块提供了一些新的数据结构用来解决一些特定问题,如OrderedDict或者Counter

在Python中找到正确的数据结构是非常重要的,因为正确的选择会节省你的时间并减少代码维护量。