Python数据结构和算法
Posted HT . WANG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python数据结构和算法相关的知识,希望对你有一定的参考价值。
1.将序列分解为单独的变量
任何的序列(或者是可迭代对象)可以通过一个简单的赋值操作来分解为单独的变量。 唯一的要求就是变量的总数和结构必须与序列相吻合。
如果元素的数量不匹配,会得到一个错误提示
示例如下:
>>> p = (4, 5)
>>> x, y = p
>>> x
4
>>> y
5
>>>
>>> data = [ 'CSDN', 50, 91.1, (2022, 4, 14) ]
>>> name, shares, price, date = data
>>> name
'CSDN'
>>> date
(2022, 4, 14)
>>> name, shares, price, (year, mon, day) = data
>>> name
'CSDN'
>>> year
2022
>>> mon
4
>>> day
14
>>>
如果只想解压其中一部分,丢弃其他的值。对于这种情况 Python 并没有提供特殊的语法。 但是可以使用任意变量名去占位,到时候丢掉这些变量就行了
注意:占位所用变量名必须保证不会在代码其他地方二次使用
示例如下:
>>> data = [ 'CSDN', 50, 91.1, (2022, 4, 14) ]
>>> _, shares, price, _ = data
>>> shares
50
>>> price
91.1
>>>
2.解压可迭代对象赋值给多个变量
上一节中将可迭代对象进行逐个分解赋值给对应变量,如果一个可迭代对象的元素个数超过变量个数时,会抛出一个 ValueError
。 通过星号表达式能从这个可迭代对象中解压出 N 个元素出来。
>>> record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
>>> name, email, *phone_numbers = record
>>> name
'Dave'
>>> email
'dave@example.com'
>>> phone_numbers
['773-555-1212', '847-555-1212']
>>>
注意的是上面解压出的 phone_numbers
变量永远都是列表类型,不管解压的电话号码数量是多少(包括 0 个)。
应用(1):星号表达式在迭代元素为可变长元组的序列时
records = [
('foo', 1, 2),
('bar', 'hello'),
('foo', 3, 4),
]
def do_foo(x, y):
print('foo', x, y)
def do_bar(s):
print('bar', s)
for tag, *args in records:
if tag == 'foo':
do_foo(*args)
elif tag == 'bar':
do_bar(*args)
应用(2) 字符串分割
>>> line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
>>> uname, *fields, homedir, sh = line.split(':')
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
>>>
应用(3)实现递归
>>> def sum(items):
... head, *tail = items #星号表达式记录 除第一个元素外 后续所有元素
... return head + sum(tail) if tail else head #进行递归操作 直至后续只剩唯一头元素则结束递归
...
>>> sum(items)
36
>>>
3.保留最后 N 个元素
使用 deque(maxlen=N)
构造函数会新建一个固定大小的队列。当新的元素加入并且这个队列已满的时候, 最老的元素会自动被移除掉。
>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(4)
>>> q
deque([2, 3, 4], maxlen=3)
>>> q.append(5)
>>> q
deque([3, 4, 5], maxlen=3)
应用:匹配历史记录最后N行
from collections import deque
def search(lines, pattern, history=5):
previous_lines = deque(maxlen=history)
for line in lines:
if pattern in line:
yield line, previous_lines
previous_lines.append(line)
# Example use on a file
if __name__ == '__main__':
with open(r'../../cookbook/somefile.txt') as f:
for line, prevlines in search(f, 'python', 5):
for pline in prevlines:
print(pline, end='')
print(line, end='')
print('-' * 20)
4.查找最大或最小的 N 个元素
借助heapq模块 从一个集合中获得最大或者最小的 N 个元素列表
import heapq
portfolio = [
'name': 'IBM', 'shares': 100, 'price': 91.1,
'name': 'AAPL', 'shares': 50, 'price': 543.22,
'name': 'FB', 'shares': 200, 'price': 21.09,
'name': 'HPQ', 'shares': 35, 'price': 31.75,
'name': 'YHOO', 'shares': 45, 'price': 16.35,
'name': 'ACME', 'shares': 75, 'price': 115.65
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
print(cheap)
print(expensive)
结果:
['name': 'YHOO', 'shares': 45, 'price': 16.35, 'name': 'FB', 'shares': 200, 'price': 21.09, 'name': 'HPQ', 'shares': 35, 'price': 31.75]
['name': 'AAPL', 'shares': 50, 'price': 543.22, 'name': 'ACME', 'shares': 75, 'price': 115.65, 'name': 'IBM', 'shares': 100, 'price': 91.1]
heapq模块底层实现通过建立最小堆排序完成
>>> nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
>>> import heapq
>>> heap = list(nums)
>>> heapq.heapify(heap) #heapq将list转化为堆排序
>>> heap
[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]
>>>
堆数据结构最重要的特征是 heap[0]
永远是最小的元素。并且剩余的元素可以很容易的通过调用 heapq.heappop()
方法得到, 该方法会先将第一个元素弹出来,然后用下一个最小的元素来取代被弹出元素(这种操作时间复杂度仅仅是 O(log N),N 是堆大小)。 比如,如果想要查找最小的 3 个元素可以:
>>> heapq.heappop(heap)
-4
>>> heapq.heappop(heap)
1
>>> heapq.heappop(heap)
2
注意:
(1)当要查找的元素个数相对比较小的时候,函数 nlargest()
和 nsmallest()
是很合适的。
(2)如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用 min()
和 max()
函数会更快
(3)如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点 ( sorted(items)[:N]
或者是 sorted(items)[-N:]
)。
5.实现一个优先级队列
heapq功能:
heappush(heap,item) | heap为堆 item为新增加元素 |
heapify(list) | 将列表转换为堆 |
heappop(heap) | 删除堆第一个元素,堆第一个元素heap[0]为最小值,即删除并返回最小值 |
heapreplace(heap,item) | 删除并返回最小元素,添加新元素 |
heappushpop(list,item) | 新添加元素与堆第一个元素对比:如果大,则删除并返回第一个元素,然后添加新元素 ;如果小,返回新元素,原堆不变 |
merge(heap1,heap2...) | 多个堆合并 |
nlargest(n,heap) | 查询堆中最大n个元素 |
nsmallest(n,heap) | 查询堆中最小n个元素 |
优先级队列:
class PriorityQueue:
def __init__(self):
self.__queue = []
self.__index = 0
def push(self, item, priority):
heapq.heappush(self.__queue, (-priority, self.__index, item))
# 第一个参数:添加进的目标序列
# 第二个参数:将一个元组作为整体添加进序列,目的是为了方便比较
# 在priority相等的情况下,比较_index
# priority为负数使得添加时按照优先级从大到小排序,因为堆排序的序列的第一个元素永远是最小的
self.__index += 1
def pop(self):
# 返回按照-priority 和 _index 排序后的第一个元素(是一个元组)的最后一个元素(item)
return heapq.heappop(self.__queue)[-1]
q = PriorityQueue()
q.push("bar", 2)
q.push("foo", 1)
q.push("gork", 3)
q.push("new", 1)
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
"""
gork # 优先级最高
bar # 优先级第二
foo # 优先级与new相同,比较index,因为先加入,index比new小,所以排在前面
new
"""
6.字典中的键映射多个值
一个字典就是一个键对应一个单值的映射。如果想要一个键映射多个值,就需要将这多个值放到另外的容器中, 比如列表或者集合里面。
d =
'a' : [1, 2, 3],
'b' : [4, 5]
e =
'a' : 1, 2, 3,
'b' : 4, 5
使用 collections
模块中的 defaultdict
来构造这样的字典
from collections import defaultdict
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
##结果
defaultdict(, 'a': [1, 2], 'b': [4])
创建一个多值映射字典:普通方法与 defaultdict
方法比较
d =
for key, value in pairs:
if key not in d:
d[key] = []
d[key].append(value)
#**********************************
d = defaultdict(list)
for key, value in pairs:
d[key].append(value)
7.字典排序
为了能控制一个字典中元素的顺序,你可以使用 collections
模块中的 OrderedDict
类。 在迭代操作的时候它会记录插入字典的顺序 之后再对该字典进行插入的键值保持元素被插入时的顺序.
OrderedDict
内部维护着一个根据键插入顺序排序的双向链表。每次当一个新的元素插入进来的时候, 它会被放到链表的尾部。对于一个已经存在的键的重复赋值不会改变键的顺序
from collections import OrderedDict
d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4
# Outputs "foo 1", "bar 2", "spam 3", "grok 4"
for key in d:
print(key, d[key])
d['baidu'] = 3
d['tencent'] = 4
# Outputs "foo 1", "bar 2", "spam 3", "grok 4", "baidu 3", "tencent 4"
for key in d:
print(key, d[key])
注意:
一个 OrderedDict
的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。 所以如果你要构建一个需要大量 OrderedDict
实例的数据结构的时候(比如读取 100,000 行 CSV 数据到一个 OrderedDict
列表中去), 那么就得仔细权衡一下是否使用 OrderedDict
带来的好处要大过额外内存消耗的影响
8.字典的运算
如果你在一个字典上执行普通的数学运算,你会发现它们仅仅作用于键,而不是值
zip()
函数方案通过将字典”反转”为 (值,键) 元组序列来解决了上述问题。 当比较两个元组的时候,值会先进行比较,然后才是键。
prices =
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
min_price = min(zip(prices.values(), prices.keys()))
# min_price is (10.75, 'FB')
max_price = max(zip(prices.values(), prices.keys()))
# max_price is (612.78, 'AAPL')
min(prices, key=lambda k: prices[k]) # Returns 'FB'
max(prices, key=lambda k: prices[k]) # Returns 'AAPL'
9.查找两字典的相同点
a =
'x' : 1,
'y' : 2,
'z' : 3
b =
'w' : 10,
'x' : 11,
'y' : 2
# Find keys in common
a.keys() & b.keys() # 'x', 'y'
# Find keys in a that are not in b
a.keys() - b.keys() # 'z'
# Find (key,value) pairs in common
a.items() & b.items() # ('y', 2)
(1)一个字典就是一个键集合与值集合的映射关系。 字典的 keys()
方法返回一个展现键集合的键视图对象。 支持集合操作,比如集合并、交、差运算。 所以,如果对集合的键执行一些普通的集合操作,可以直接使用键视图对象而不用先将它们转换成一个 set。
(2)字典的 items()
方法返回一个包含 (键,值) 对的元素视图对象。 这个对象同样也支持集合操作,并且可以被用来查找两个字典有哪些相同的键值对。
(3)尽管字典的 values()
方法也是类似,但是它并不支持这里介绍的集合操作。 某种程度上是因为值视图不能保证所有的值互不相同,这样会导致某些集合操作会出现问题。 不过,如果非要在值上面执行这些集合操作的话,你可以先将值集合转换成 set,然后再执行集合运算就行了。
10.删除序列相同元素并保持顺序
如何在一个序列上面保持元素顺序的同时消除重复的值
set集合方法不能维护元素的顺序,生成的结果中的元素位置被打乱
(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]
>>>
(2)字典元素
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)
#这里的key参数指定了一个函数,将序列元素转换成 hashable 类型
>>> a = [ 'x':1, 'y':2, 'x':1, 'y':3, 'x':1, 'y':2, 'x':2, 'y':4]
>>> list(dedupe(a, key=lambda d: (d['x'],d['y'])))
['x': 1, 'y': 2, 'x': 1, 'y': 3, 'x': 2, 'y': 4]
>>> list(dedupe(a, key=lambda d: d['x']))
['x': 1, 'y': 2, 'x': 2, 'y': 4]
>>>
11.命名切片
内置的 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]
通过调用切片的 indices(size)
方法将它映射到一个已知大小的序列上。 这个方法返回一个三元组 (start, stop, step)
,所有的值都会被缩小,直到适合这个已知序列的边界为止。 这样,使用的时就不会出现 IndexError
异常
>>> s = 'HelloWorld'
>>> a.indices(len(s))
(5, 10, 2)
>>> for i in range(*a.indices(len(s))):
... print(s[i])
...
W
r
d
12.序列中出现次数最多的元素
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
word_counts = Counter(words)
# 出现频率最高的3个单词
top_three = word_counts.most_common(3)
print(top_three)
# Outputs [('eyes', 8), ('the', 5), ('look', 4)]
一个 Counter
对象底层实现就是一个字典,将需要统计的元素映射到它统计的次数上
>>> word_counts['not']
1
>>> word_counts['eyes']
8
>>>
13.通过某个关键字排序一个字典列表
通过使用 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_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))
print(rows_by_fname)
print(rows_by_uid)
'''
#结果
['fname': 'Big', 'uid': 1004, 'lname': 'Jones',
'fname': 'Brian', 'uid': 1003, 'lname': 'Jones',
'fname': 'David', 'uid': 1002, 'lname': 'Beazley',
'fname': 'John', 'uid': 1001, 'lname': 'Cleese']
['fname': 'John', 'uid': 1001, 'lname': 'Cleese',
'fname': 'David', 'uid': 1002, 'lname': 'Beazley',
'fname': 'Brian', 'uid': 1003, 'lname': 'Jones',
'fname': 'Big', 'uid': 1004, 'lname': 'Jones']
'''
rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))
print(rows_by_lfname)
'''
#结果
['fname': 'David', 'uid': 1002, 'lname': 'Beazley',
'fname': 'John', 'uid': 1001, 'lname': 'Cleese',
'fname': 'Big', 'uid': 1004, 'lname': 'Jones',
'fname': 'Brian', 'uid': 1003, 'lname': 'Jones']
'''
itemgetter()
有时候也可以用 lambda
表达式代替,比如:
#根据lambda表达式中字典key值进行索引排序
rows_by_fname = sorted(rows, key=lambda r: r['fname'])
rows_by_lfname = sorted(rows, key=lambda r: (r['lname'],r['fname']))
14.排序不支持原生比较的对象
使用 operator.attrgetter()
来完成
class User:
def __init__(self, user_id):
self.user_id = user_id
def __repr__(self):
return 'User()'.format(self.user_id)
users = [User(23), User(3), User(99)]
print(users)
print(sorted(users, key=lambda u: u.user_id))
'''
#结果
[User(23), User(3), User(99)]
[User(3), User(23), User(99)]
'''
from operator import attrgetter
print(sorted(users, key=attrgetter('user_id')))
'''
#结果
[User(3), User(23), User(99)]
'''
15.通过某个字段将记录分组
itertools.groupby()
函数对于这样的数据分组操作非常实用
现在假设你想在按 date 分组后的数据块上进行迭代。为了这样做,你首先需要按照指定的字段(这里就是 date进行
排序, 然后调用 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 GRANVILLE', '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 GRANVILLE', 'date': '07/04/2012'
'''
groupby()
函数扫描整个序列并且查找连续相同值(或者根据指定 key 函数返回值相同)的元素序列。一个非常重要的准备步骤是要根据指定的字段将数据排序。 因为 groupby()
仅仅检查连续的元素,如果事先并没有排序完成的话,分组函数将得不到想要的结果
16.过滤序列元素
最简单的方法:列表推导
>>> 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]
>>>
过滤并替换
mylist = [1, 4, -5, 10, -7, 2, 3, -1]
clip_neg = [n if n > 0 else 0 for n in mylist]
print(clip_neg)
clip_pos = [n if n < 0 else 0 for n in mylist]
print(clip_pos)
'''
#结果
[1, 4, 0, 10, 0, 2, 3, 0]
[0, 0, -5, 0, -7, 0, 0, -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)
# Outputs ['1', '2', '-3', '4', '5']
另外一个过滤工具就是 itertools.compress()
, 它以一个 iterable
对象和一个相对应的 Boolean
选择器序列作为输入参数。 然后输出 iterable
对象中对应选择器为 True
的元素。
addresses = [
'5412 N CLARK',
'5148 N CLARK',
'5800 E 58TH',
'2122 N CLARK',
'5645 N RAVENSWOOD',
'1060 W ADDISON',
'4801 N BROADWAY',
'1039 W GRANVILLE',
]
counts = [ 0, 3, 10, 4, 1, 7, 6, 1]
>>> from itertools import compress
>>> more5 = [n > 5 for n in counts]
>>> more5
[False, False, True, False, False, True, True, False]
>>> list(compress(addresses, more5))
['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']
>>>
17.从字典中提取子集
(1)字典推导
prices =
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
# Make a dictionary of all prices over 200
p1 = key: value for key, value in prices.items() if value > 200
print(p1)
'''
#结果
'AAPL': 612.78, 'IBM': 205.55
'''
# Make a dictionary of tech stocks
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
'''
(2)dict()函数
prices =
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
# Make a dictionary of all prices over 200
#p1 = key: value for key, value in prices.items() if value > 200
p1 = dict((key, value) for key, value in prices.items() if value > 200)
print(p1)
'''
#结果
'AAPL': 612.78, 'IBM': 205.55
'''
# Make a dictionary of tech stocks
tech_names = 'AAPL', 'IBM', 'HPQ', 'MSFT'
#p2 = key: value for key, value in prices.items() if key in tech_names
p2=dict((key, value)for key, value in prices.items() if key in tech_names)
print(p2)
'''
#结果
'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2
'''
18.映射名称到序列元素
即所谓,通过名称而不是通过下标访问元素
命名元组访问:
>>> from collections import namedtuple
>>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
>>> sub = Subscriber('jonesy@example.com', '2012-10-19')
>>> sub
Subscriber(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'
>>>
命名元组的一个主要用途是将你的代码从下标操作中解脱出来。 下标操作通常会让代码表意不清晰,并且非常依赖记录的结构,但是如果你使用了命名元组,那么就不会有这样的顾虑。
def compute_cost(records):
total = 0.0
for rec in records:
total += rec[1] * rec[2]#存储结构为一个很大的元组列表,通过下标去操作其中的元素, 当你在结构中添加了新的列的时候你的代码可能就会出错了
return total
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price'])
def compute_cost(records):
total = 0.0
for rec in records:
s = Stock(*rec)
total += s.shares * s.price
return total
命名元组如果需要改变属性的值,使用_replace()
方法进行替换或填充数据
>>> s = s._replace(shares=75)
>>> s
Stock(name='ACME', shares=75, price=123.45)
>>>
from collections import namedtuple
Stock = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
# Create a prototype instance
stock_prototype = Stock('', 0, 0.0, None, None)
# Function to convert a dictionary to a Stock
def dict_to_stock(s):
return stock_prototype._replace(**s)
>>> a = 'name': 'ACME', 'shares': 100, 'price': 123.45
>>> 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'
>>> dict_to_stock(b)
Stock(name='ACME', shares=100, price=123.45, date='12/17/2012', time=None)
>>>
19.转换并同时计算数据
nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)
print(s)
'''
55
'''
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)
'''
20
'''
20.合并多个字典或映射
现在有多个字典或者映射,想将它们从逻辑上合并为一个单一的映射后执行某些操作, 比如查找值或者检查某些键是否存在。
a = 'x': 1, 'z': 3
b = 'y': 2, 'z': 4
现在假设你必须在两个字典中执行查找操作(比如先从 a
中找,如果找不到再在 b
中找)。 一个非常简单的解决方案就是使用 collections
模块中的 ChainMap
类。
from collections import ChainMap
c = ChainMap(a,b)
print(c['x']) # Outputs 1 (from a)
print(c['y']) # Outputs 2 (from b)
print(c['z']) # Outputs 3 (from a) 如果出现重复键,那么第一次出现的映射值会被返回。 因此,例子程序中的 c['z'] 总是会返回字典 a 中对应的值,而不是 b 中对应的值。
一个 ChainMap
接受多个字典并将它们在逻辑上变为一个字典。 然后,这些字典并不是真的合并在一起了, ChainMap
类只是在内部创建了一个容纳这些字典的列表 并重新定义了一些常见的字典操作来遍历这个列表。
>>> len(c)
3
>>> list(c.keys())
['x', 'y', 'z']
>>> list(c.values())
[1, 2, 3]
>>>
对于字典的更新或删除操作总是影响的是列表中第一个字典
>>> c['z'] = 10 #修改a中'z'的值
>>> c['w'] = 40 #向a中添加新值‘w’
>>> del c['x'] #删除a中'x'的值
>>> a
'w': 40, 'z': 10
>>> del c['y'] #不会修改第二个字典 则报错返回
Traceback (most recent call last):
...
KeyError: "Key not found in the first mapping: 'y'"
>>>
注意:ChainMap不同与使用 update()
方法将两个字典合并,update建立一个新字典作为合并字典进行存储,原字典的修改不会影响合并字典
以上是关于Python数据结构和算法的主要内容,如果未能解决你的问题,请参考以下文章