删除相同的元素并保持元素原有的顺序
- 官方对于是否可哈希的解释说明:
如果一个对象在其生命周期内有一个固定不变的哈希值 (这需要hash()方法) 且可以与其他对象进行比较操作 (这需要eq()方法) ,那么这个对象就是可哈希对象 (hashable) ,可哈希对象必须有相同的哈希值才算作相等。
由于字典 (dict) 的键 (key) 和集合 (set) 内部使用到了哈希值,所以只有可哈希 (hashable) 对象才能被用作字典的键和集合的元素。
所有python内置的不可变对象都是可哈希的,同时,可变容器 (比如:列表 (list) 或者字典 (dict) ) 都是不可哈希的。用户自定义的类的实例默认情况下都是可哈希的;它们跟其它对象都不相等 (除了它们自己) ,它们的哈希值来自id()方法
eg:
- 如果值为一个可哈希对像,那么利用集合就可以做到
def deque(items):
seen = set()
for item in items:
if item not in seen:
yield item
seen.add(item)
a = [1, 5, 2, 1, 9, 1, 5, 10]
print(list(deque(a)))
- 假如值为不可哈希对像时:
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}]
print(list(dedupe(a, key=lambda d: (d['x'],d['y']))))
print(list(dedupe(a, key=lambda d: d['x'])))
命名切片相关
items = [0, 1, 2, 3, 4, 5, 6]
a = slice(2, 4) # 内置slice函数可以创建一个切片对像,用到任何切片允许使用的地方.
print(items[a])
序列中出现次数最多的元素
- 使用
collections.Counter
有用的most_common()
words = [ 'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes', 'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into', 'my', 'eyes', "you're", 'under' ]
from collections import Counter
print(Counter(words)) # Counter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2, 'not': 1, "don't": 1, "you're": 1, 'under': 1})
print(Counter(words).most_common(3)) #[('eyes', 8), ('the', 5), ('look', 4)]
word_counts = Counter(words) # Counter可以当作字典来操作.
print(word_counts['eyes']) # 8
通过某个关键字排序一个字典列表
- 想根据一个字典的某个或者几个字典来排序这个列表,可以使用
operator
的temgetter
函数.
rows = [ {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
{'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004} ]
from operator import itemgetter
rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))
print(rows_by_fname)
print(rows_by_uid)
上面这种情况也可以使用lambda
来完成,比如
rows_by_fname = sorted(rows, key=lambda r: r['fname'])
但使用itemgetter会快一些,看个人爱好与需要.
通过某个字段将纪录分组
- 想根据某个特定的字段来迭代访问分组,可以使用
itertools.groupby
rows =[ {'address': '5412 NCLARK','date':'07/01/2012'}, {'address': '5148 NCLARK','date':'07/04/2012'}, {'address': '5800 E58TH','date':'07/02/2012'}, {'address': '2122 NCLARK','date':'07/03/2012'}, {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 WADDISON','date': '07/02/2012'}, {'address': '4801 NBROADWAY','date': '07/01/2012'}, {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}, ]
from operator import itemgetter
from itertools import groupby
rows.sort(key=itemgetter('date'))
# print(rows)
for date, items in groupby(rows, key=itemgetter('date')):
print(date)
for i in items:
print('',i)
输出为:
07/01/2012
{'address': '5412 NCLARK', 'date': '07/01/2012'}
{'address': '4801 NBROADWAY', 'date': '07/01/2012'}
07/02/2012
{'address': '5800 E58TH', 'date': '07/02/2012'}
{'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}
{'address': '1060 WADDISON', 'date': '07/02/2012'}
07/03/2012
{'address': '2122 NCLARK', 'date': '07/03/2012'}
07/04/2012
{'address': '5148 NCLARK', 'date': '07/04/2012'}
{'address': '1039 WGRANVILLE', 'date': '07/04/2012'}
假如根据date字段将数据分组到一个大的数据结构中去,并且允许随机访 问, 那么你最好使用 defaultdict() 来构建一个多值字典.
from collections import defaultdict
row_by_date = defaultdict(list)
for row in rows:
row_by_date[row['date']].append(row)
print(row_by_date)
输出为:
defaultdict(<class 'list'>, {'07/01/2012': [{'address': '5412 NCLARK', 'date': '07/01/2012'}, {'address': '4801 NBROADWAY', 'date': '07/01/2012'}], '07/02/2012': [{'address': '5800 E58TH', 'date': '07/02/2012'}, {'address': '5645 NRAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 WADDISON', 'date': '07/02/2012'}], '07/03/2012': [{'address': '2122 NCLARK', 'date': '07/03/2012'}], '07/04/2012': [{'address': '5148 NCLARK', 'date': '07/04/2012'}, {'address': '1039 WGRANVILLE', 'date': '07/04/2012'}]})
假如需要可以根据
for r in row_by_date['07/01/2012']: print(r)
这种方式来获取.
defaultdict
的主要目的是,当Key不存在时不会报错,比如下面的情况
a1 = dict()
s = 'mississippi'
# print(a1['a']) ====>TypeError: 'type' object does not support item assignment
d2 = defaultdict(int)
# print(d2['a'])===>运行正常
for k in s:
d2[k] +=1
print(d2)
过滤序列元素
- 有一个数据序列,想利用一些规则从中提取出需要的值或者是缩短序列
mylist= [1, 4, -5,10,-7,2,3,-1]
print([n for n in mylist if n > 0])
# 上面的缺点就是输入非常大的时候会占用非常大的结果集,占用大量内存,这时候可以使用生成器表达式迭代产生过滤元素.
post = (n for n in mylist if n > 0)
for x in post:
print(x)
复杂情况可以使用filter()
函数,filter()
函数创建了一个迭代器. 所以只需要写个函数来验证值,如下所示
values= ['1', '2', '-3','-','4', 'N/A', '5']
def is_int(val):
try:
x = int(val)
return True
except ValueError:
return False
isvals = list(filter(is_int, values))
print(isvals)
但列表推导是首先可以考虑的一种形式
values= ['1', '2', '-3','-','4', 'N/A', '5']
def is_int(val):
try:
x = int(val)
return True
except ValueError:
return False
isvals = list(filter(is_int, values))
print(isvals)
clip_neg = [n if n > 0 else 0 for n in mylist]
print(clip_neg)
从字典中提取子集
- 比较快的情况,列表推导
prices= { 'ACME':45.23, 'AAPL':612.78, 'IBM':205.55, 'HPQ':37.20, 'FB': 10.75 }
# 返回一个价格大于200的字典
p1 = {key:value for key, value in prices.items() if value > 200}
print(p1) #{'AAPL': 612.78, 'IBM': 205.55}
tech_names= {'AAPL', 'IBM', 'HPQ', 'MSFT'}
p2 = {key:value for key,value in prices.items() if key in tech_names}
print(p2) #{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}
** 映射名称到序列元素 **
- 通过下标访问列表或者元组中元素的代码,但是这样有时候会使得你的代码难以 阅读, 于是你想通过名称来访问元素
collections.namedtuple()
函数通过使用一个普通的元组对象来帮你解决这个问题。 这个 函数实际上是一个返回Python中标准元组类型子类的一个工厂方法。 你需要传递一个类 型名和你需要的字段给它,然后它就会返回一个类,你可以初始化这个类,为你定义的字 段传递值等。
from collections import namedtuple
Subscriber = namedtuple('Subscriber', ['addr','joined'])
sub = Subscriber('jonesy@example.com', '2012-10-19')
print(sub.addr) # jonesy@example.com
另外一点就是_replace()方法,可以改变它的属性值.比如下面这样
Stock= namedtuple('Stock',['name', 'shares', 'price'])
s = Stock('ACME', 100, 123.45)
print(s) # Stock(name='ACME', shares=100, price=123.45)
print(s.shares) #会报错, 因为命名元组是不可以更改的
# 如果真要更改属于可以使用_replace()方法
s = s._replace(shares = 75)
print(s) # Stock(name='ACME', shares=75, price=123.45)
_replace在使用nametuple命名元组有缺失字段或者可选字段时它可以很方便的填充数据, 比如下面这样
Stock= namedtuple('Stock',['name', 'shares', 'price', 'date', 'time'])
stock_prototype = Stock('', 0, 0.0, None, None)
def dict_to_stock(s):
return stock_prototype._replace(**s)
a= {'name': 'ACME','shares':100,'price':123.45}
print(dict_to_stock(a)) #Stock(name='ACME', shares=100, price=123.45, date=None, time=None)
b= {'name': 'ACME','shares':100,'price':123.45, 'date':'12/17/2012'}
print(dict_to_stock(b)) # Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
- 转换并且同时计算数据. 可以考虑推导与min(), max()等,些聚集函数比如 min() 和 max() 的时候你可能更加倾向于使用生成器版本, 它们接受的一个key关键字参数或许对你很有帮助
import os
files = os.listdir('.')
# for name in files:
# if name.endswith('.py'):
# print('aaa')
# else:
# print('not python file')
if any(name.endswith('.py')for name in files):
print('There be python!')
else:
print('Sorry,no python.')
portfolio =[ {'name':'GOOG', 'shares':50}, {'name':'YHOO', 'shares':75}, {'name':'AOL','shares': 20}, {'name':'SCOX', 'shares':65} ]
min_shares= min(s['shares'] for s in portfolio)
print(min_shares)
print(min(portfolio, key=lambda s:s['shares'])) # {'name': 'AOL', 'shares': 20}
合并多个字典或者映射
- 现有多个字典或者映射,想从逻辑上合并为一个单一映射后执行某些操作,比如查找健值或者健是否存在,可以使用
collections 中的ChainMap
类
一个 ChainMap 接受多个字典并将它们在逻辑上变为一个字典。 然后,这些字典并不是真 的合并在一起了, ChainMap 类只是在内部创建了一个容纳这些字典的列表 并重新定义了 一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用的
另外: 对于字典的更新或删除操作总是影响的是列表中第一个字典
from collections import ChainMap
a = {'x': 1, 'z':3 }
b = {'y': 2, 'z':4 }
c = ChainMap(a,b)
print(c)
print(c['x'])
print(c['y'])
print(c['z']) # 重复键,那么第一次出现的映射值会被返回。
print(len(c))
print(list(c.keys())) #['y', 'z', 'x']
print(list(c.values())) # [2, 3, 1]