Python 经验 - 数据结构典型用法

当初能快速上手Python最重要的一点是其内置的数据结构非常强大,关于数据类型的差别(例如可修改对象与不可修改对象)作为基础的知识点在其他教程中已经有非常详细的介绍,所以这里就不再赘述了。这次主要分享一些(也许算是换一种思路的)能提升开发效率的使用心得。

从列表、字典、集合筛选数据

我们都知道列表生成式是一种很Pythonic的用法:

[x for x in data if x>=0]

仅一行代码就能从可迭代对象data中筛选出满足大于等于0条件的元素。
它的迭代器版本为:

(x for x in data if x>=0)

区别在于返回的是一个满足筛选条件的迭代器,当遍历时元素在当前循环中才产生所以能大量节省内存,但定义后只能使用一次。

其实字典和集合也可以使用推导式:

{k: v for k, v in data.iteritems() if v>=0 }    # iteritems是items的迭代器版本
{x for x in s if x>=0}

返回的是经过筛选的字典/集合,比使用for循环要简单直接得多。

  • 尽可能使用生成式,效率比较高;
  • 复杂的场景生成式可能会影响可读性,不建议使用。

为元组元素命名

有C/C++开发经验的朋友常常会用到:

// 宏定义
#define NAME 0
#define AGE 1


// 枚举类型
enum Student{NAME, AGE}

我想说的是这里用到一种为元素命名的方法,直接通过名称能在顺序结构中以更快的速度访问元素(而不需要遍历整个对象)。
在Python中也可以通过名称(而不是下标)的方法访问列表/元组元素:
例如可以自己定义“枚举类型”

NAME, AGE, SEX, EMAIL = xrange(4)
student = ("Jim", 16, "male", "[email protected]")
print(student[NAME], student[AGE], student[SEX], student[EMAIL],)

另外也可以使用collections模块的namedtuple:

from collections import namedtuple

# 先定义一个名为Student的namedtuple
Student = namedtuple("Student", ["name", "age", "sex", "email"])

# 然后就可以使用自定义的Student类型创建namedtuple对象啦
s1 = Student(
    name="Jim",
    age=16,
    sex="male",
    email="[email protected]"
)
print(s1.name, s1.age, s1.sex, s1.email)

以上两种方法都可以实现通过名称访问元组/列表的元素哦~

统计序列中元素的出现频度

有时我们需要统计列表/元组中某些元素出现的次数。
其中一种方法是使用字典来统计:

# 生成范围在1 ~9的元素共100个,并存放在字典中
li = [random.randrange(1, 10) for i in range(100)]  
di = dict.fromkeys(li, 0)
for i in li:
    di[i] += 1

这个时候更方便的方法是使用collections的Counter类型:

from collections import Counter
c = Counter(li)

这时候返回的是一个计数器对象,可以使用dict直接转换为以元素为key、次数为value的字典。
而且还可以直接取出出现次数排名前三的元素,并组成新的字典:

c.most_common(3)

把字典根据值的大小排序

先复习一下字典的特性,字典与列表的对比:

字典 列表
查找和插入速度快,不会随着键的增加而变慢 查找和插入的时间随着元素增加而增加
占用大量内存,浪费较多 占用内存少,浪费较多
通过键(哈希)查询,键不可改变(所以不能用列表等可变类型作为键) 通过下标查询
默认无序(可以人工排序) 有序

实际上字典也是可以排序、达到按序查找的效果:
对于一个随机的字典:

from random import randint
d = {x: randint(60, 100) for x in 'abcxyz'}

排序的方法是转化为一个元组列表,再对元组的项排序:

keys = d.iterkeys()    
values = d.itervalues()
# 分别取字典的键和值组成元组(Python3中不支持,只能用keys()、values())
tu = zip(keys, values)
new_di = dict(sorted(tu))

更好的方法是利用sorted函数的第二个参数指定参与排序的项:

dict(sorted(d.items(), key=lambda x: x[1]))

找到多个字典中的公共键

假设我们有多个字典,需要找到这些字典中都存在的key:

di_a = {"a": 1, "b": 2, "c": 3}
di_b = {"a": 5, "d": 2, "e": 3}
di_c = {"a": -5, "f": 5, "g": 0}

可见公共键就是"a",但在比较大的字典中要迅速找到这个公共键,常见的方法是构造循环遍历di_a,并在循环中判断di_a的键是否同时存在于di_b和di_c......
有一种更简便的方法:

di_a.viewkeys() & di_b.viewkeys() & di_c.viewkeys()

di_a.viewkeys()返回的是一个view对象,可以直接转化为存放字典di_a的列表,最重要的优点是view对象是支持集合运算的,上面这个语句返回的是同时存在于di_a、di_b、di_c中的元素(交集)的集合,即三个字典的公共键集合:

set(['a'])

让字典保持有序

我们使用字典有时会碰上一种情景,要求让字典一直保持有序(例如比赛中统计名次和成绩),这时候使用collections的OrderDict类型就非常方便了,以下是一个模拟比赛的程序:

from random import randint
from time import time
from collections import OrderedDict

d = OrderedDict()           # 存放选手成绩的有序字典
players = list('ABCDEFG')   # 模拟七个选手

start = time()
for i in xrange(8):
    input()     # 阻塞:每次输入回车随机弹出一个选手表示该选手完成比赛
    p = players.pop(randint(0, 7-i))    
    end = time()
    print(i+1, p, end-start)    # 输出名次、选手名、用时
    d[p] = (i+1, end-start)     # 把选手成绩计入有序字典

print()
for k in d:
    print(k, d[k])       # 根据进入字典的顺序打印元素

实现用户的历史记录功能(最多n条)

很多时候我们需要让程序实现自动记录用户的历史数据和操作,达到缓存的效果(例如浏览器的搜索记录等)。
Python的collections模块为我们提供了双向队列,可以很方便的实现这个功能,以下是一个猜数游戏(用户每次输入数字,返回答案的取值范围,随着输入次数增多范围缩小,当用户输入与答案相同的数字时游戏结束):

from random import randint

N = randint(0, 100)     # 返回0~100范围内的数字

def guess(k):
    if k == N:
        print("Right!")
        return True
    elif k < N:
        print("{} < N...".format(k))
    else:
        print("{} > N...".format(k))

while 1:
    num = input("Please input a number:")
    if num:
        k = int(num)
        if guess(k):
            break

但很多时候随着输入的次数增多,就很容易忘记之前已经输入过的数字,这时候可以引入一个存放历史记录的队列:

from collections import deque
from pickle import dump, load       # 需要时可用pickle写入文件
history = deque([], 5)      # 空队列,长度为5
# ...
while 1:
    num = input("Please input a number:")
    if num:
        k = int(num)
        history.append(k)
        # 当用户输入?或history时返回历史输入提示
        if num in ['history', '?']: 
            print(list(history))
        if guess(k):
            break

你可能感兴趣的:(Python 经验 - 数据结构典型用法)