任何的序列(或者是可迭代对象)可以通过一个简单的赋值语句解压并赋值给多个变量。 唯一的前提就是变量的数量必须跟序列元素的数量是一样的。
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> name, shares, price, date = data
>>> name
'ACME'
>>> date
(2012, 12, 21)
>>> name, shares, price, (year, mon, day) = data
>>> name
'ACME'
>>> year
2012
>>> mon
12
>>> day
21
>>>
可以使用任意变量名去占位,到时候丢掉不要的值,这些占位符需要保证没被使用。
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
>>> _, shares, price, _ = data
>>> shares
50
>>> price
91.1
变量个数与序列元素的个数不一致时会产生异常。
>>> p = (4, 5)
>>> x, y, z = p
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: need more than 2 values to unpack
星号表达式解包时表示不确定个数或者任意个数的元素,返回结果总是一个列表。星号表达式可以用在列表的任意位置。
星号表达式可以用在字符串的分割。
>>> 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'
同样,星号表达式也可以使用废弃名称,比如 _ 或者 ign (ignore)。
>>> record = ('ACME', 50, 123.45, (12, 18, 2012))
>>> name, *_, (*_, year) = record
>>> name
'ACME'
>>> year
2012
collections.deque
使用 deque(maxlen=N)
构造函数会新建一个固定大小的队列。当新的元素加入并且这个队列已满的时候, 最老的元素会自动被移除掉。
不带参数 maxlen
的时候生成无限长的队列
>>> q = deque()
>>> q.append(1)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3])
>>> q.appendleft(4)
>>> q
deque([4, 1, 2, 3])
>>> q.pop()
3
>>> q
deque([4, 1, 2])
>>> q.popleft()
4
在队列两端插入或删除元素时间复杂度都是 O(1) ,区别于列表,在列表的开头插入或删除元素的时间复杂度为 O(N) 。
heapq 模块有两个函数:nlargest() 和 nsmallest()
import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]
着两个函数可以接受一个关键字参数,用于更复杂的数据结构:
{
'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'])
堆数据结构最重要的特征是 heap[0]
永远是最小的元素,剩下的元素通过 heapq.heappop()
方法得到。
>>> import heapq
>>> heap = list(nums)
>>> heapq.heapify(heap)
>>> heap
[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]
>>> heapq.heappop(heap)
-4
>>> heapq.heappop(heap)
1
>>> heapq.heappop(heap)
2
查找的 N 个元素相对整体比较小的时候,用 nlargest()
和 nsmallest()
很合适;
只有唯一的最小或最大值时(N= 1) ,使用 min()
和 max()
函数比较好;
如果 N 的大小和集合的大小接近的时候使用排序再使用切片操作比较好。( sorted(items)[:N]
或者是 sorted(items)[-N:]
).
这里其实用的就是 heapq.heappush()
时插入新的元组,然后 heapq.heappop()
比较元组的大小,而元组的比较就是依次对比元组里面的元素。
import heapq
'''
第一次pop()删除的是最高优先度的,
后面的同优先度删除的是先添加的值,
pop 和 push 的复杂度为O(logN)
'''
class PriorityQueue:
def __init__(self):
self._queue = []
self._index = 0
def push(self, item, priority):
heapq.heappush(self._queue, (-priority, self._index, 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('sdasd'), 4)
q.push(Item('iuou'), 1)
print(q.pop())
print(q.pop())
print(q.pop())
print(q.pop())
对于三元组 (priority, index, item)
,Python 在做元组比较时候,如果前面的比较已经可以确定结果了, 后面的比较操作就不会发生了:
>>> a = (1, 0, Item('foo'))
>>> b = (5, 1, Item('bar'))
>>> c = (1, 2, Item('grok'))
>>> a < b
True
>>> a < c
True
>>>
如果你使用元组 (priority, item)
,只要两个元素的优先级不同就能比较。 但是如果两个元素优先级一样的话,那么比较操作就会跟之前一样出错:
>>> a = (1, Item('foo'))
>>> b = (5, Item('bar'))
>>> a < b
True
>>> c = (1, Item('grok'))
>>> a < c
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>
如果你想保持元素的插入顺序就应该使用列表, 如果想去掉重复元素就使用集合(并且不关心元素的顺序问题)。
d = {
'a' : [1, 2, 3],
'b' : [4, 5]
}
e = {
'a' : {
1, 2, 3},
'b' : {
4, 5}
}
你可以很方便的使用 collections
模块中的 defaultdict
来构造这样的字典。 defaultdict
的一个特征是它会自动初始化每个 key
刚开始对应的值,所以你只需要关注添加元素操作了。defaultdict
会自动为将要访问的键(就算目前字典中并不存在这样的键)创建映射实体。
d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)
为了能控制一个字典中元素的顺序,你可以使用 collections
模块中的 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])
当你想要构建一个将来需要序列化或编码成其他格式的映射的时候, OrderedDict
是非常有用的。 比如,你想精确控制以 JSON 编码后字段的顺序,你可以先使用 OrderedDict
来构建这样的数据:
>>> import json
>>> json.dumps(d)
'{"foo": 1, "bar": 2, "spam": 3, "grok": 4}'
>>>
OrderedDict
内部维护着一个根据键插入顺序排序的双向链表。每次当一个新的元素插入进来的时候, 它会被放到链表的尾部。对于一个已经存在的键的重复赋值不会改变键的顺序。
一个 OrderedDict
的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。数据过多的时候需要衡量内存的消耗。
一般对字典的运算时候都是对字典的值进行操作,所以先用 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')
排列字典的数据:
prices_sorted = sorted(zip(prices.values(), prices.keys()))
# prices_sorted is [(10.75, 'FB'), (37.2, 'HPQ'),
# (45.23, 'ACME'), (205.55, 'IBM'),
# (612.78, 'AAPL')]
需要注意的是 zip()
函数创建的迭代器只能访问一次,下面代码会出错:
prices_and_names = zip(prices.values(), prices.keys())
print(min(prices_and_names)) # OK
print(max(prices_and_names)) # ValueError: max() arg is an empty sequence
计算操作用到(键,值)对时,当多个实体拥有相同的值时候,键会决定返回结果。
>>> prices = {
'AAA' : 45.23, 'ZZZ': 45.23 }
>>> min(zip(prices.values(), prices.keys()))
(45.23, 'AAA')
>>> max(zip(prices.values(), prices.keys()))
(45.23, 'ZZZ')
>>>
为了寻找两个字典的相同点,可以简单的在两字典的 keys()
或者 items()
方法返回结果上执行集合操作。比如:
# 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) }
这些操作也可以用于修改或者过滤字典元素。
# Make a new dictionary with certain keys removed
c = {
key:a[key] for key in a.keys() - {
'z', 'w'}}
# c is {'x': 1, 'y': 2}
字典的 keys()
方法返回一个展现键集合的键视图对象, 字典的 items()
方法返回一个包含 (键,值) 对的元素视图对象,
上面的两个对象都支持集合操作。
vlaues()
不支持集合操作,某种程度是因为值视图不能保证所有的值互不相同。
如果序列上的值都是 hashable
类型,那么可以很简单的利用集合或者生成器来解决这个问题。比如:
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]
>>>
想消除元素不可哈希(比如 dict 类型)的序列中重复元素的话,你需要将上述代码稍微改变一下,就像这样:
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}]
>>> 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}]
>>>
单纯想消除重复的元素的话直接构建集合就可以,但是生成结果的顺序会被打乱。
>>> a
[1, 5, 2, 1, 9, 1, 5, 10]
>>> set(a)
{
1, 2, 10, 5, 9}
>>>
读取文件消除重复行,可以用上面的函数实现:
with open(somefile,'r') as f:
for line in dedupe(f):
...
内置的 slice()
函数可以改善代码出现大量的硬编码下标会使得代码的可读性和可维护性大大降低的问题。
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]
切片对象a 可以调用它的 a.start
, a.stop
, a.step
属性来获取更多的信息。
>>> a = slice(5, 50, 2)
>>> a.start
5
>>> a.stop
50
>>> a.step
2
>>>
调用切片的 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
>>>
collections.Counter
类就是专门为这类问题设计的
你有一个单词列表并且想找出哪个单词出现频率最高。你可以这样做:
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
对象可以接受任意的由可哈希(hashable)
元素构成的序列对象。 在底层实现上,一个 Counter
对象就是一个字典,将元素映射到它出现的次数上。
>>> word_counts['not']
1
>>> word_counts['eyes']
8
>>>
增加计数的方法:
>>> morewords = ['why','are','you','not','looking','in','my','eyes']
>>> for word in morewords:
... word_counts[word] += 1
...
>>> word_counts['eyes']
9
>>>
'''
自带的 update() 方法更加简单
'''
>>> word_counts.update(morewords)
>>>
Counter
实例一个鲜为人知的特性是它们可以很容易的跟数学运算操作相结合。比如:
>>> a = Counter(words)
>>> b = Counter(morewords)
>>> a
Counter({
'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2,
"you're": 1, "don't": 1, 'under': 1, 'not': 1})
>>> b
Counter({
'eyes': 1, 'looking': 1, 'are': 1, 'in': 1, 'not': 1, 'you': 1,
'my': 1, 'why': 1})
>>> # Combine counts
>>> c = a + b
>>> c
Counter({
'eyes': 9, 'the': 5, 'look': 4, 'my': 4, 'into': 3, 'not': 2,
'around': 2, "you're": 1, "don't": 1, 'in': 1, 'why': 1,
'looking': 1, 'are': 1, 'under': 1, 'you': 1})
>>> # Subtract counts
>>> d = a - b
>>> d
Counter({
'eyes': 7, 'the': 5, 'look': 4, 'into': 3, 'my': 2, 'around': 2,
"you're": 1, "don't": 1, 'under': 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_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': 'Big', 'uid': 1004, 'lname': 'Jones'}]
itemgetter()
函数也支持多个 keys,比如下面的代码
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
表达式代替,比如:
rows_by_fname = sorted(rows, key=lambda r: r['fname'])
rows_by_lfname = sorted(rows, key=lambda r: (r['lname'],r['fname']))
使用 itemgetter()
方式会运行的稍微快点。
当你想排序不支持原生的比较操作的相同类型对象时,有以下两种方式:
内置的 sorted()
函数有一个关键字参数 key
。
class User:
def __init__(self, user_id):
self.user_id = user_id
def __repr__(self):
return 'User({})'.format(self.user_id)
def sort_notcompare():
users = [User(23), User(3), User(99)]
print(users)
print(sorted(users, key=lambda u: u.user_id))
另外一种方式是使用 operator.attrgetter()
来代替 lambda
函数:
>>> from operator import attrgetter
>>> sorted(users, key=attrgetter('user_id'))
[User(3), User(23), User(99)]
>>>
使用 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'},
]
根据 date
排序
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
{
'date': '07/01/2012', 'address': '5412 N CLARK'}
{
'date': '07/01/2012', 'address': '4801 N BROADWAY'}
07/02/2012
{
'date': '07/02/2012', 'address': '5800 E 58TH'}
{
'date': '07/02/2012', 'address': '5645 N RAVENSWOOD'}
{
'date': '07/02/2012', 'address': '1060 W ADDISON'}
07/03/2012
{
'date': '07/03/2012', 'address': '2122 N CLARK'}
07/04/2012
{
'date': '07/04/2012', 'address': '5148 N CLARK'}
{
'date': '07/04/2012', 'address': '1039 W GRANVILLE'}
一个非常重要的准备步骤是要根据指定的字段将数据排序,(所以先用 sort()
)。 因为 groupby() 仅仅检查连续的元素,如果事先并没有排序完成的话,分组函数将得不到想要的结果。
如果你仅仅只是想根据 date
字段将数据分组到一个大的数据结构中去,并且允许随机访问, 那么你最好使用 defaultdict()
来构建一个多值字典:
from collections import defaultdict
rows_by_date = defaultdict(list)
for row in rows:
rows_by_date[row['date']].append(row)
# 结果
>>> for r in rows_by_date['07/01/2012']:
... print(r)
...
{
'date': '07/01/2012', 'address': '5412 N CLARK'}
{
'date': '07/01/2012', 'address': '4801 N BROADWAY'}
>>>
最简单是运用列表推导式,但是缺点是输入大的时候产生非常打的结果集,占用大量的内存:
>>> 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]
>>>
所以可以使用生成器表达式:
>>> pos = (n for n in mylist if n > 0)
>>> pos
<generator object <genexpr> at 0x1006a0eb0>
>>> for x in pos:
... print(x)
...
1
4
10
2
3
>>>
过滤规则比较复杂的话使用内置的 fltler()
函数,返回的是迭代器。
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]
输出 count
大于 5 的地址:
>>> 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']
>>>
需要先创建 Boolean
序列,compress
也是返回一个迭代器。
最简单是使用字典推导式
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}
# 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}
也可以用户以下的方式:
# Make a dictionary of tech stocks
tech_names = {
'AAPL', 'IBM', 'HPQ', 'MSFT' }
p2 = {
key:prices[key] for key in prices.keys() & tech_names }
但是第二种方式的话会比第一种方式耗时更多。
使用 collections.nametuple()
可以实现,你需要传递一个类型名和你需要的字段给它,然后返回一个类,你可以初始化这个类。
>>> from collections import namedtuple
>>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
>>> sub = Subscriber('[email protected]', '2012-10-19')
>>> sub
Subscriber(addr='[email protected]', joined='2012-10-19')
>>> sub.addr
'[email protected]'
>>> sub.joined
'2012-10-19'
>>>
nametuple
跟元组类型是可以交换的,支持所有普通的元组操作,比如索引和解压。
>>> len(sub)
2
>>> addr, joined = sub
>>> addr
'[email protected]'
>>> joined
'2012-10-19'
>>>
命名元组不能直接改变属性的值,通过使用 _replace()
方法实现。
>>> s = s._replace(shares=75)
>>> s
Stock(name='ACME', shares=75, price=123.45)
>>>
_replace()
可以对包含缺省值的原型元组填充数据
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)
>>>
使用生成器表达式作为参数比先创建一个临时列表更加高效和优雅。
nums = [1, 2, 3, 4, 5]
s = sum((x * x for x in nums)) # 显式的传递一个生成器表达式对象
s = sum(x * x for x in nums) # 更加优雅的实现方式,省略了括号
# 这种也能达到目的
nums = [1, 2, 3, 4, 5]
s = sum([x * x for x in nums])
第二种方式,需要先创建一个列表,元素数量非常大的时候,会创建一个巨大的仅仅使用一次就被丢弃的临时数据结构,浪费内存。
使用 collections
模块中的 ChainMap
类
先从 a 中找,如果找不到再在 b 中找
a = {
'x': 1, 'z': 3 }
b = {
'y': 2, 'z': 4 }
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)
出现重复键,第一次出现的映射值会被返回。
ChainMap
类只是在内部创建了一个容纳这些字典的列表 并重新定义了一些常见的字典操作来遍历这个列表。对字典的更新或者删除总是原字典并只影响列表中的第一个字典。
# 常见的字典操作
>>> len(c)
3
>>> list(c.keys())
['x', 'y', 'z']
>>> list(c.values())
[1, 2, 3]
>>>
# 对字典的操作总是影响第一个字典
>>> c['z'] = 10
>>> c['w'] = 40
>>> del c['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()
方法合并两个字典, ChainMap
使用的是原字典, update()
方法创建一个新的字典对象,对原字典的更新不会反应到新创的字典。
>>> a = {
'x': 1, 'z': 3 }
>>> b = {
'y': 2, 'z': 4 }
>>> merged = dict(b)
>>> merged.update(a)
>>> merged['x']
1
>>> merged['y']
2
>>> merged['z']
3
# 更新字典
>>> a['x'] = 13
>>> merged['x']
1
ChainMap
的作用范围
>>> values = ChainMap()
>>> values['x'] = 1
>>> # Add a new mapping
>>> values = values.new_child()
>>> values['x'] = 2
>>> # Add a new mapping
>>> values = values.new_child()
>>> values['x'] = 3
>>> values
ChainMap({
'x': 3}, {
'x': 2}, {
'x': 1})
>>> values['x']
3
>>> # Discard last mapping
>>> values = values.parents
>>> values['x']
2
>>> # Discard last mapping
>>> values = values.parents
>>> values['x']
1
>>> values
ChainMap({
'x': 1})
>>>