Python_cookbook_数据结构与算法

数据结构与算法


1.1将序列分解为单独的变量

1.1.1问题:我们有一个包含N个元素的序列或元组(可迭代) 现在想将它分解为N个单独的变量
p=(4,5)
x,y=p
x
4
y
5
data=['ACME',50,91.1,(2012,12,21)]
name,shares,price,date=data
name
'ACME'
shares
50
price
91.1
date
(2012, 12, 21)
#若元素的数量不匹配将会得到一个错误提示
p=(4,5)
x,y,z=p
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-10-57b40cd77f56> in <module>
      1 #若元素的数量不匹配将会得到一个错误提示
      2 p=(4,5)
----> 3 x,y,z=p


ValueError: not enough values to unpack (expected 3, got 2)
#丢弃某些特定的值
data=['ACME',50,91.1,(2012,12,21)]
_,shares,price,_=data

shares

price
91.1

1.2从任意长度的可迭代对象中分解元素

1.2.1问题:需要从某个可迭代对象中分解N个元素 但是这个迭代对象长度可能超过N 这会导致出现"分解的值过多(too many values to unpack)"的异常
#使用*表达式
record=('Nishino Nanase','RoseXIN13@yeah.net','777-888-999','777-666-999')
name,email,*phone_numbers=record
name
'Nishino Nanase'
email
'RoseXIN13@yeah.net'
phone_numbers
['777-888-999', '777-666-999']
*trailing,current=[10,8,7,1,9,5,2,3]
trailing
[10, 8, 7, 1, 9, 5, 2]
current
3
#指定丢掉的某些值
record=('Nishino Nanase','RoseXIN13@yeah.net',1,3,(12,18,2020))
name,*_,(*_,year)=record
name
'Nishino Nanase'
year
2020

1.3保存最后N个元素

1.3.1问题:我们希望在迭代或是其他形式的处理过程中对最后几项记录做一个有限的历史记录
#使用collections.deque
from collections import deque
#deque(maxlen=N)创建最大长度为N的队列
#队列已满时 仍有新纪录添加进入队列 队列会自动移除最老的记录
q=deque(maxlen=3)
q.append(1)
q.append(2)
q.append(3)
q
deque([1, 2, 3])
q.append(4)
q
deque([2, 3, 4])
#deque()创建一个无界限的队列
p=deque()
p.append(1)#appendleft()
p.append(2)
p.append(3)
p

deque([1, 2, 3])
p.pop()#popleft
p
deque([1, 2])

1.4找到最大或则最小的元素

1.4.1问题:在某个集合中找出最大或则最小的元素
import heapq
# nlargers() nsmallest()
nums=[1,8,2,23,7,-4,18,23,42,37,2]
print(heapq.nlargest(3,nums))
print(heapq.nsmallest(3,nums))
[42, 37, 23]
[-4, 1, 2]

1.5实现优先级队列

1.5.1问题:我们想要实现一个队列 它能够以给定的优先级来对元素排序 且每次pop操作都返回优先级最高的元素
import heapq
class PriorityQueue:
    def __init__(self):
        self._queue=[]
        self._index=0
    def push(self,item,priority):
        heapq.heappush(self._queue,(-priority,self._index,item))#将item 按照优先级从高到低进行排序 然后放入队列
        self._index+=1
    def pop(self):
        return heapq.heappop(self._queue)[-1]

class Item:
    def __init__(self,name):
        self.name=name
    def __repr__(self):
        return 'Item({!r})'.format(self.name)

q=PriorityQueue()
q.push(Item('foo'),1)
q.push(Item('bar'),5)
q.push(Item('spam'),4)
q.push(Item('grok'),1)
q.pop()
Item('bar')
q.pop()
Item('spam')
q.pop()
Item('foo')
q.pop()
Item('grok')

1.6在字典中将健映射到多个值上

1.6.1问题:我们想要一个健(key)能够映射到多个值上的字典(即所谓的一键多值字典[multidict])
d={
    'a':[1,2,3],
    'b':[4,5]
}
e={
    'a':{1,2,3},
    'b':{4,5}
}
d
{'a': [1, 2, 3], 'b': [4, 5]}
e
{'a': {1, 2, 3}, 'b': {4, 5}}

1.7让字典保存有序

1.7.1问题:我们想创建一个字典 同时对字典做迭代或则序列操作时 也能控制其中的元素的顺序
#使用collections中OrderedDict
#注意 OrderedDict的大小是普通字典的两倍多
#OrderedDict会严格按照元素初始添加打的顺序进行 接下来对已存在的健重新赋值不会改变健的顺序
from collections import OrderedDict
d=OrderedDict()
d['foo']=1
d['bar']=2
d['spam']=3
d['grok']=4

for key in d:
    print(key,d[key])
foo 1
bar 2
spam 3
grok 4
d={}
d['foo']=1
d['bar']=2
d['spam']=3
d['grok']=4

for key in d:
    print(key,d[key])
foo 1
bar 2
spam 3
grok 4

1.8与字典有关的计算问题

1.8.1问题:在字典上对数据进行操作
prices={
    'ACME':45.21,
    'AAPL':61.78,
    'IBM':205.55,
    'HPQ':37.20,
    'FB':10.75
}
#zip()将key value 反转
min_price=min(zip(prices.values(),prices.keys()))
print(min_price)
(10.75, 'FB')
#数据进行排序
prices_sorted=sorted(zip(prices.values(),prices.keys()))
print(prices_sorted)
[(10.75, 'FB'), (37.2, 'HPQ'), (45.21, 'ACME'), (61.78, 'AAPL'), (205.55, 'IBM')]
#注意 zip()创建了一个迭代器 内容只能被消费一次
ex=zip(prices.values(),prices.keys())
print(min(ex))#OK
print(max(ex))#ERROR!!!
(10.75, 'FB')



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-86-e8887df74774> in <module>
      2 ex=zip(prices.values(),prices.keys())
      3 print(min(ex))#OK
----> 4 print(max(ex))#ERROR!!!


ValueError: max() arg is an empty sequence

1.9在两个字典中寻找相同点

1.9.1问题:有两个字典 我们想找出它们中间可能相同的地方
a={
    'x':1,
    'y':2,
    'z':3
}
b={
    'w':10,
    'x':11,
    'y':2
}


#通过keys() 或 items()执行集合操作
a.keys()&b.keys()
{'x', 'y'}
a.keys()-b.keys()
{'z'}
a.items()&b.items()
{('y', 2)}
#过滤一些键
c={key:a[key] for key in a.keys()-{'z','w'}}
c
{'x': 1, 'y': 2}

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

1.10.1问题:我们想去除序列中的重复元素 但仍然保持剩下的元素顺序不变
#序列中的值可哈希
def dedupe(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]
list(dedupe(a))
[1, 5, 2, 9, 10]
#序列中存在不可哈希的元素
def dedupe(items,key=None):
    seen=set()
    for item in items:
        val=item if item 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':2,'y':2},{'x':2,'y':3}]
list(dedupe(a,key=lambda d:(d['x'],d['y'])))
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-27-c11ca211de19> in <module>
      1 a=[{'x':1,'y':2},{'x:':1,'y':3},{'x':2,'y':2},{'x':2,'y':3}]
----> 2 list(dedupe(a,key=lambda d:(d['x'],d['y'])))


<ipython-input-26-235543b636c4> in dedupe(items, key)
      3     seen=set()
      4     for item in items:
----> 5         val=item if item is None else key(item)
      6         if val not in seen:
      7             yield item


<ipython-input-27-c11ca211de19> in <lambda>(d)
      1 a=[{'x':1,'y':2},{'x:':1,'y':3},{'x':2,'y':2},{'x':2,'y':3}]
----> 2 list(dedupe(a,key=lambda d:(d['x'],d['y'])))


KeyError: 'x'

1.11对切片命名

1.11.1问题:当我们的代码已经变得无法阅读 到处都是硬编码的切片索引 我们想要将它清理干净
#slice()函数 创建切片对象
items=[0,1,2,3,4,5,6]
a=slice(2,4)
items[2:4]
[2, 3]
items[a]
[2, 3]
items[a]=[10,11]
items
[0, 1, 10, 11, 4, 5, 6]
del items[a]
items
[0, 1, 4, 5, 6]

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

1.12.1问题:我们有一个元素序列 我们想要知道次数最多的元素
#collections Counter类
words=['look','around','my','eye','into','my','eyes','the','the','yes','eye']
from collections import Counter
word_counts=Counter(words)
top_three=word_counts.most_common(3)
print(top_three)
[('my', 2), ('eye', 2), ('the', 2)]
#底层实现中 Counter是一个字典 在元素和它们出现的次数间做了映射
word_counts['the']
2
#更新
more_words=['look','the','the','around']
word_counts.update(more_words)
print(word_counts.most_common(3))
[('the', 4), ('look', 2), ('around', 2)]
a=Counter(words)
b=Counter(more_words)
a
Counter({'look': 1,
         'around': 1,
         'my': 2,
         'eye': 2,
         'into': 1,
         'eyes': 1,
         'the': 2,
         'yes': 1})
b
Counter({'look': 1, 'the': 2, 'around': 1})
#Combine counts
c=a+b
c
Counter({'look': 2,
         'around': 2,
         'my': 2,
         'eye': 2,
         'into': 1,
         'eyes': 1,
         'the': 4,
         'yes': 1})
#Subtract counts
d=a-b
d
Counter({'my': 2, 'eye': 2, 'into': 1, 'eyes': 1, 'yes': 1})

1.13通过公共键对字典列表排序

1.13.1问题:我们有一个字典列表 想根据一个或则多个字典中的值来对列表排序
#利用operator模块中的itemgetter函数排序
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_name=sorted(rows,key=itemgetter('fname'))
rows_by_uid=sorted(rows,key=itemgetter('uid'))

print(rows_by_name)
print(rows_by_uid)
[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}]
[{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}]
#itemgetter 可以接受多个健值
rows_by_lfname=sorted(rows,key=itemgetter('lname','fname'))
print(rows_by_lfname)
[{'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}]

1.4对不原生支持比较操作的对象排序

1.4.1问题:我们在想同一个类的实例之间做排序 但是它们并不支持比较操作
class User:
    def __init__(self,user_id):
        self.user_id=user_id
    def __repr__(self):
        return 'User({})'.format(self.user_id)
users=[User(3),User(99),User(57)]
users
[User(3), User(99), User(57)]
#lambda表达式
users_sorted=sorted(users,key=lambda r:r.user_id,reverse=True)
users_sorted
[User(99), User(57), User(3)]
#operator.attrgetter()
from operator import attrgetter
sorted(users,key=attrgetter('user_id'))
[User(3), User(57), User(99)]
#min() manx()
min(users,key=attrgetter('user_id'))
User(3)
max(users,key=attrgetter('user_id'))
User(99)

1.15根据字段将记录分组

1.15.1问题:有一系列的字典或则对象实例 我们想根据某个特定的字段(比如说日期)来分组迭代数据
#itertools.groupby()
rows=[
    { 'address':'5412 N CLARK','date':'07/01/2012'},
    { 'address':'5148 N CLARK','date':'07/04/2012'},
    { 'address':'5800 E 58TH','date':'07/02/2012'},
    { 'address':'2122 N CLARK','date':'07/03/2012'},
    { 'address':'5645 N RAVENSWOOD','date':'07/02/2012'},
    { 'address':'1060 W ADDISON','date':'07/02/2012'},
    { 'address':'4801 N BROADWAY','date':'07/01/2012'},
    { 'address':'1039 W GRANVILIE','date':'07/04/2012'},
]
from operator import itemgetter
from itertools import groupby

#Sort by the desired field first
rows.sort(key=itemgetter('date'))

#Iterate in groups
for date,items in groupby(rows,key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ',i)

07/01/2012
  {'address': '5412 N CLARK', 'date': '07/01/2012'}
  {'address': '4801 N BROADWAY', 'date': '07/01/2012'}
07/02/2012
  {'address': '5800 E 58TH', 'date': '07/02/2012'}
  {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}
  {'address': '1060 W ADDISON', 'date': '07/02/2012'}
07/03/2012
  {'address': '2122 N CLARK', 'date': '07/03/2012'}
07/04/2012
  {'address': '5148 N CLARK', 'date': '07/04/2012'}
  {'address': '1039 W GRANVILIE', 'date': '07/04/2012'}
#若是简单的将日期分组到一起
from collections import defaultdict
rows_by_date=defaultdict(list)
for row in rows:
    rows_by_date[row['date']].append(row)
rows_by_date
defaultdict(list,
            {'07/01/2012': [{'address': '5412 N CLARK', 'date': '07/01/2012'},
              {'address': '4801 N BROADWAY', 'date': '07/01/2012'}],
             '07/02/2012': [{'address': '5800 E 58TH', 'date': '07/02/2012'},
              {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
              {'address': '1060 W ADDISON', 'date': '07/02/2012'}],
             '07/03/2012': [{'address': '2122 N CLARK', 'date': '07/03/2012'}],
             '07/04/2012': [{'address': '5148 N CLARK', 'date': '07/04/2012'},
              {'address': '1039 W GRANVILIE', 'date': '07/04/2012'}]})
for r in rows_by_date['07/01/2012']:
    print(r)
{'address': '5412 N CLARK', 'date': '07/01/2012'}
{'address': '4801 N BROADWAY', 'date': '07/01/2012'}

1.16筛选序列中的元素

1.16.1问题:序列中有一些数据 我们需要提取出其中的值或则根据某些标准对序列做删减
#列表推导式
mylist=[1,4,-5,10,-7,2,3,-1]
[n for n in mylist if n>0]
[1, 4, 10, 2, 3]
[n for n in mylist if n<0]
[-5, -7, -1]
#筛选条件比较复杂 处理筛选的逻辑代码放入单独函数 然后使用filter()
values=['1','2','-3','-','4','N/A','5']

def is_int(val):
    try:
        x=int(val)
        return True
    except ValueError:
        return False

ivals=list(filter(is_int,values))
print(ivals)
['1', '2', '-3', '4', '5']
#列表推导式和生成器还具有对数据转换的功能
import math

[math.sqrt(i) for i in mylist if i>0]
[1.0, 2.0, 3.1622776601683795, 1.4142135623730951, 1.7320508075688772]

1.17从字典中提取子集

1.17.1问题:我们想创建一个字典 其本身是另一个字典的子集
prices={
    'ACME':45.23,
    'AAPL':612.78,
    'IBM':205.55,
    'HPQ':37.20,
    'FB':10.75
}

#Make a dict of all prices over 200
p1={key:value for key,value in prices.items() if value>200}
p1
{'AAPL': 612.78, 'IBM': 205.55}
#Make a dict of tech stocks
tech_name={'AAPL','IBM','HPQ','MSFT'}
p2={key:values for key,value in prices.items() if key in tech_name}
p2
{'AAPL': ['1', '2', '-3', '-', '4', 'N/A', '5'],
 'IBM': ['1', '2', '-3', '-', '4', 'N/A', '5'],
 'HPQ': ['1', '2', '-3', '-', '4', 'N/A', '5']}

1.18将名称映射到序列的元素中

1.18.1问题:我们的代码通过位置(即索引 或下标) 来访问列表或则元组
但有时候这会使得代码变得难以阅读 我们希望通过名称来访问元素 以此减少结构中对位置的依赖性
#collections.namedtuple

from collections import namedtuple 
Subscriber=namedtuple('Subscriber1',['addr','joined'])
sub=Subscriber('jonesy@example.com','2012-10-19')
sub
Subscriber1(addr='jonesy@example.com', joined='2012-10-19')
sub.addr
'jonesy@example.com'
sub.joined
'2012-10-19'
len(sub)
2
addr,joined=sub
addr
'jonesy@example.com'
joined
'2012-10-19'

1.19同时对数据做转换和换算

1.19.1问题:我们需要调用一个换算(reduction)函数 (例如 sum() max() min()) 但首先的对数据做转换或则筛选
nums=[1,2,3,4,5]
s=sum(x*x for x in nums)
s
55
s=('ACME',50,123.45)
print(','.join(str(x) for x in s))
ACME,50,123.45

1.20将多个映射合并为单个映射

1.20.1问题:我们有多个字典和映射 想在逻辑上将它们合并为一个单独的映射结构
以此执行某些特定的操作 比如查找值或检查值是否存在
a={'x':1,'z':3}
b={'y':2,'z':4}
#使用collections.ChainMap
#有重复值是 采用第一个映射中对应的值
from collections import ChainMap
c=ChainMap(a,b)
print(c['x'])# Output 1 from a
print(c['y'])# Output 2 from b
print(c['z'])# Output 3 from a
1
2
3
len(c)
3
list(c.keys())
['y', 'z', 'x']
list(c.items())
[('y', 2), ('z', 3), ('x', 1)]
list(c.values())
[2, 3, 1]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值