问题1:现在有一个包含N个元素的元组或者是序列,怎样将它里面的值解压后同时赋值给N个变量?
>>> p = (4,5)
>>> x,y = p
>>> x
4
>>> y
5
>>> s = 'hello'
>>> a,b,c,d,e = s
>>> a
'h'
>>> b
'e'
>>> c
'l'
>>> d
'l'
>>> e
'o'
问题2:解压可迭代对象赋值给多个变量
如果一个可迭代对象的元素个数超过变量个数时,会抛出一个ValueError.那么怎么才能从这个可迭代对象中解压出N个元素出来?
- Python的星号表达式可以用来解决这个问题。
>>> record = ("Dava", "cadaaf", "4234-54535", "43433676")
>>> name, addr, *num = record
>>> num
['4234-54535', '43433676']
>>> name
'Dava'
- 注意:解压出来的星号永远都是列表类型,不管解压的里面有没有数值。
- 星号表达式在迭代元素为可变长元组的序列时很有用。
>>> records = [("a", 1, 2),("b", 3, 4)]
>>> tag, *nums = records[1]
>>> tag
'b'
>>> nums
[3, 4]
- 星号解压语法在字符串操作的时候也很有用。
>>> line = "nobody:*:-1-2-:Udsdsnndj User:/var/empty:/usr/bin/false"
>>> uname, *_, homedir, sh = line.split(":")
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
注释:解压后不想要的内容,可以用一个废弃名称,例如 _ 。
问题3:保留最后N个元素
在迭代操作或者其他操作的时候,怎样只保留最后有限个元素的历史记录?
保留有限历史记录正是collections.deque 大显身手的时候。(collections是Python内建的一个集合模块,提供了许多有用的集合类。)
>>> from collections import deque
>>> q = deque(maxlen=3)
>>> q.append(1)
>>> q
deque([1], maxlen=3)
>>> q.append(2)
>>> q.append(3)
>>> q
deque([1, 2, 3], maxlen=3)
>>> q.append(2)
>>> q
deque([2, 3, 2], maxlen=3)
deque如果不设置大小,则会得到一个无限大小队列,并且可以在队列的两端执行添加和弹出操作。在队列两端插入或者删除元素时间复杂度都是O(1),而在列表的开头插入或者删除元素的时间复杂度为O(N).
>>> 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
>>> q
deque([1, 2])
问题4:查找最大或者最小的N个元素
怎样从一个集合中获得最大或者最小的N个元素列表?---堆
- 如果N接近于集合大小,则使用排序+切片较合适。Sorted(items)[:N]
- Heapq模块有两个函数:nlargest()和nsmallest()可以完美解决这个问题。
>>> import heapq
>>> nums = [1,2,47,322,86,-42,56,654,2,65,-65]
>>> print(heapq.nlargest(3,nums))
[654, 322, 86]
>>> print(heapq.nsmallest(3,nums))
[-65, -42, 1]
nlargest()和nsmallest() 能接受一个关键字参数,用于更复杂的数据结构
>>>portfolio=[{"name":"IBM","shares":100,"price":91},
{"name":"APPL","shares":100, "price":81},
{"name":"UHD","shares":100, "price":83}
]
>>> cheap = heapq.nsmallest(3, portfolio, key=lambda s:s["price"])
>>> cheap
[
{'name': 'APPL', 'shares': 100, 'price': 81},
{'name': 'UHD', 'shares': 100, 'price': 83},
{'name': 'IBM', 'shares': 100, 'price': 91}
]
堆数据结构最重要的特征就是heap[0]永远是最小的元素。并且生于的元素可以很容易通过调用heap.heappop()方法得到,该方法会将第一个元素弹出,然后用下一个最小元素来取代被弹出元素(时间复杂度:O(logN),N是堆大小)。
>>> nums = [1,8,3,56,-54,53,-6,25]
>>> import heapq
>>> heapq.heapify(nums)
>>> nums
[-54, 1, -6, 25, 8, 53, 3, 56]
>>> heapq.heappop(nums)
-54
>>> heapq.heappop(nums)
-6
>>> heapq.heappop(nums)
1
问题5:字典中的键映射多个值
怎样实现一个键对应多个值的字典(也叫multidict)?
一个字典就是一个键对应一个单值的映射。如果想要一个键映射多个值,那么就需要将多个值放到另外一个容器中,例如列表,元组,集合或者字典。
>>> d = {"a":[1,2,3],"b":(1,2,3)}
>>> d
{'a': [1, 2, 3], 'b': (1, 2, 3)}
也可以使用collections模块中的defaultdict来构造这样的字典。Defaultdict的一个特征是它会自动初始化每个key刚开始对应的值。
>>> d = defaultdict(list)
>>> d["a"].append(1)
>>> d["a"].append(2)
>>> d["b"].append(4)
>>> d
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})
>>> d = defaultdict(set)
>>> d["a"].add(1)
>>> d["a"].add(2)
>>> d["b"].add(3)
>>> d
defaultdict(<class 'set'>, {'a': {1, 2}, 'b': {3}})
关于数据处理中的记录归类问题有关联。
>>> d = defaultdict(list)
>>> for key,value in pairs:
d[key].append(value)
问题6:字典排序
若创建一个字典,并且在迭代或者序列化这个字典的时候能够控制元素的顺序。
为了控制一个字典中元素的顺序,可以使用collections模块中的OrderedDict类,在迭代操作的时候,他会保持元素被插入时的顺序。
>>> from collections import OrderedDict
>>> d = OrderedDict()
>>> d["foo"] = 1
>>> d["bar"] = 2
>>> d["spam"] = 3
>>> d["grok"] = 4
>>> d
OrderedDict([('foo', 1), ('bar', 2), ('spam', 3), ('grok', 4)])
注:OrderedDict内部维护着一个根据键插入顺序排序的双向链表。所以一个OrderedDict的大小是一个普通字典的两倍。
问题7:字典运算
怎样在数据字典中执行一些计算操作(比如求最小值、最大值、排序等等)?
考虑下面的股票名和价格映射字典:
Prices = {
“ACME”:45.32,
“AAPL”:642.54,
“IBM”:433.52,
“HPQ”:34.21,
“FB”:12.76
}
为了对字典值执行计算操作,通常需要使用zip()函数先将键和值反转过来。
>>> min_price = min(zip(Prices.values(),Prices.keys()))
>>> min_price
(12.76, 'FB')
>>> max_price = max(zip(Prices.values(),Prices.keys()))
>>> max_price
(642.54, 'AAPL')
注意:zip()函数创建的是一个只能访问一次的迭代器。
>>> price_name = zip(Prices.values(),Prices.keys())
>>> min(price_name)
(12.76, 'FB')
>>> max(price_name)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: max() arg is an empty sequence
注意:当多个实体拥有相同的值的时候,键会决定返回结果。
>>> prices = {"AAA":23,"ZZZ":23}
>>> min(zip(prices.values(),prices.keys()))
(23, 'AAA')
>>> max(zip(prices.values(),prices.keys()))
(23, 'ZZZ')
问题8:查找两字典的相同点
怎样在两个字典中寻找相同点(比如相同的键、相同的值等等)?
为了寻找两个字典的相同点,可以简单的在两字典的keys()或者items()方法返回结果上执行集合操作。
>>> a = {"x":1,"y":2,"z":3}
>>> b = {"w":10,"x":22,"y":2}
>>> a.keys()&b.keys()
{'y', 'x'}
>>> a.keys()-b.keys()
{'z'}
>>> a.items()&b.items()
{('y', 2)}
这些操作也可以用于修改或者过滤字典元素。
>>> c = {key:a[key] for key in a.keys()-{"z","w"}}
>>> c
{'y': 2, 'x': 1}
问题9:删除序列相同元素并保持顺序
怎样在一个序列上保持元素顺序的同时消除重复的值?
用列表+循环
问题10:命名切片
当程序有一段代码要从一个记录字符串中几个固定位置提取出特定的数据字段(比如文件或者类似格式)。
命名切片使代码更加清晰可读,硬编码下标会阅读困难。
Slice(start, stop, step) slice[ )
>>> record = "..........100.......521.43"
>>> shares = slice(10,13)
>>> record[shares]
'100'
>>> price = slice(20,26)
>>> record[price]
'521.43'
>>> cost = int(record[shares])*float(record[price])
>>> cost
52142.99999999999
还可以通过调用切片的indices(size)方法将它映射到一个确定大小的序列上,避免出现IndexError异常。
indices相当于是切片的stop位置。若indices的size大于切片的stop则取原切片的stop,若小于则取size大小。下面示例详细说明:
>>> s = slice(0,100,2)
>>> s
slice(0, 100, 2)
>>> print(s.indices(9))
(0, 9, 2)
>>> print(s.indices(100))
(0, 100, 2)
>>> print(s.indices(1000))
(0, 100, 2)