《Python CookBook》读书笔记-数据结构和算法(二)

从序列中移除重复项且保持元素间顺序不变

方法

可以用集合和生成器来解决

先来了解下什么是可哈希(hashable):
如果一个对象在自己的生命周期中有一哈希值(hash value)是不可改变的,那么它就是可哈希的(hashable)的。可哈希对象是对象有hash(self)内置函数的对象。对于可哈希的对象执行这个函数将会返回一个整数。可哈希对象判断相等的唯一条件就是两者的哈希值相等。Python中所有不可改变的的对象(imutable objects)都是可哈希的,比如字符串,元组,也就是说可改变的容器如字典,列表不可哈希(unhashable)。我们用户所定义的类的实例对象默认是可哈希的(hashable),它们都是唯一的,而hash值也就是它们的id()。

t = (1, 2, 3)
s = '123'
l = [1,2,3]
d = [{'a': 1},{'b':2}]
print(t.__hash__())
print(s.__hash__())
print(l.__hash__) # list不是可哈希的
print(l[1].__hash__()) # list的对象可哈希
print(d.__hash__) # dict不是可哈希的
# set(d)            # 列表的元素是字典,字典是不可哈希的,所以返回TypeError: unhashable type: 'dict'
print(set([(1,2), (1,2), (2,)])) # 列表的元素是元组,元组是可哈希的
2528502973977326415
-8527273320470595707
None
2
None
{(1, 2), (2,)}
# 当序列中的元素是可哈希时
def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)
            
a= [1, 5, 2, 1, 7, 5, 9 ,0]
b = set(a)
c = dedupe(a)
print(b)
print(list(c))
{0, 1, 2, 5, 7, 9}
[1, 5, 2, 7, 9, 0]
# 当不确定序列中的元素是否可哈希时,需要对程序改造如下
def dedupe(items, key=None):
    seen = set()
    for item in items:
        val = item if key is None else key(item)
        if val not in seen:
            yield item
            seen.add(val)
            
a= [{'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
# b = set(a) # a的元素是字典,字典不是可哈希的,所以返回TypeError: unhashable type: 'dict'
c = dedupe(a, key=lambda d: (d['x'], d['y']))
d = dedupe(a, key=lambda d: (d['x']))
print(list(c))
print(list(d))
[{'y': 2, 'x': 1}, {'y': 3, 'x': 1}, {'y': 4, 'x': 2}]
[{'y': 2, 'x': 1}, {'y': 4, 'x': 2}]
# 读取一个文件,去除其中的重复行
with open('a.txt', 'r') as f:
#     print(list(f)) # ['abc\n', 'a\n', 'df\n', 'abc\n', 'df\n', '123\n']
    print(list(dedupe(f))) # ['abc\n', 'a\n', 'df\n', '123\n']
['abc\n', 'a\n', 'df\n', '123\n']

找出序列中出现次数最多的元素

方法

可以用collections模块中的Counter类来实现
Counter的底层是一个字典,在元素和它们出现的次数间做了一个映射。Counter对象提供任何可哈希的对象序列作为输入。

from collections import Counter

words = ['a', 'a', 'b', 'a', 'e', 'f', 'a', 'e', 'e', 'd', 'd']
count = Counter(words)
print(count) 
print(count.most_common(3)) # 出现次数最多的前三个元素
Counter({'a': 4, 'e': 3, 'd': 2, 'f': 1, 'b': 1})
[('a', 4), ('e', 3), ('d', 2)]
print(count['f'])
count['f'] += 1 # 还可以手动增加某个元素的出现次数
print(count['f'])
1
2
print(count)
words_2 = ['b', 'a', 'c']
count.update(words_2) # update方法可以更新count的数据
print(count)
Counter({'a': 8, 'e': 6, 'd': 4, 'f': 3, 'b': 2})
Counter({'a': 9, 'e': 6, 'd': 4, 'f': 3, 'b': 3, 'c': 1})
# Counter对象的各种运算
a = Counter(words)
b = Counter(words_2)
print(a)
print(b)
print(a + b)
print(a- b)
Counter({'a': 4, 'e': 3, 'd': 2, 'f': 1, 'b': 1})
Counter({'c': 1, 'b': 1, 'a': 1})
Counter({'a': 5, 'e': 3, 'b': 2, 'd': 2, 'f': 1, 'c': 1})
Counter({'a': 3, 'e': 3, 'd': 2, 'f': 1})

对字典列表的排序

friends = [
    {'name': 'jlan', 'age': 27, 'gender': 'm'},
    {'name': 'lann', 'age': 25, 'gender': 'f'},
    {'name': 'bob', 'age': 23, 'gender': 'm'},
    {'name': 'herry', 'age': 28, 'gender': 'f'},
    {'name': 'dairy', 'age': 26, 'gender': 'm'}
]

print(friends)
from operator import itemgetter
print(sorted(friends, key=itemgetter('age')))
print(sorted(friends, key=itemgetter('age', 'name')))

# itemgetter()的参数可以是字典的键、用数字表示的列表元素等任何可以穿给对象的__getitem__()方法的值。
# 用lambda也可以实现这样的功能,但是用itemgetter通常效率更高
print(sorted(friends, key=lambda f: f['age']))
print(sorted(friends, key=lambda f: (f['age'], f['name'])))
[{'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'f', 'age': 28, 'name': 'herry'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}]
[{'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}, {'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'f', 'age': 28, 'name': 'herry'}]
[{'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}, {'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'f', 'age': 28, 'name': 'herry'}]
[{'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}, {'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'f', 'age': 28, 'name': 'herry'}]
[{'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}, {'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'f', 'age': 28, 'name': 'herry'}]
# key=itemgetter()或lambda方法同样也可以用于min,max之类的函数

根据字段将记录分组

用itertools.groupby()方法

from operator import itemgetter
from itertools import groupby

# 字符串压缩,面试经常会出现这样的问题
l = 'aabbbbcccddeddff'
for key, group in groupby(l):
    print(key)
    print(list(group))
a
['a', 'a']
b
['b', 'b', 'b', 'b']
c
['c', 'c', 'c']
d
['d', 'd']
e
['e']
d
['d', 'd']
f
['f', 'f']
friends = [
    {'name': 'jlan', 'age': 27, 'gender': 'm'},
    {'name': 'lann', 'age': 25, 'gender': 'f'},
    {'name': 'bob', 'age': 23, 'gender': 'm'},
    {'name': 'herry', 'age': 28, 'gender': 'f'},
    {'name': 'dairy', 'age': 26, 'gender': 'm'}
]
# 对friends按性别分组
friends.sort(key=itemgetter('gender'))
for key, group in groupby(friends, key=itemgetter('gender')):
    print(key)
    print(list(group))
f
[{'gender': 'f', 'age': 25, 'name': 'lann'}, {'gender': 'f', 'age': 28, 'name': 'herry'}]
m
[{'gender': 'm', 'age': 27, 'name': 'jlan'}, {'gender': 'm', 'age': 23, 'name': 'bob'}, {'gender': 'm', 'age': 26, 'name': 'dairy'}]

说明

groupby()方法通过扫描序列找出拥有相同值(或是由参数key指定的函数返回的值)的序列项,并将它们分组。groupby()创建了一个迭代器,每次迭代都返回一个值(分组的key)和一个子迭代器(属于该分组的group)。对与friends这样序列如果要按照性别分组,需要先对friends按性别进行排序,把相同性别的项放在一块,然后才能按groupby()进行分组。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容