常用的基础操作和数据结构

Python 2020-07-01 908

1.可迭代对象中分解元素

这种方式可以很容易的分解参数和产生列表。

>>> a, b, *c = (1,2,3,4,5,6)    
>>> a    
1    
>>> c    
[3, 4, 5, 6]  

2.队列

简单的队列结构,类似list数据类型。从队列两端添加或弹出元素的时间复杂度为O(1)。而列表从头部插入或移除元素时,时间复杂度为O(N)。与列表类似的数据结构还有array,都比列表性能要好!

from collections import deque    
    que = deque(maxlen=5) # iter, maxlen    
    que.appendleft(10)    
    que.appendleft(8)    
    que.appendleft(7)    
    print(que, list(que))  # deque([7, 8, 10], maxlen=5),  [7, 8, 10]    
    que.pop()  # 10    
    que.popleft()  # 7  

3.找最大,最小的N个元素。

平常是采用max,min来寻找元素,但是只会返回一个元素。

nums = [8, 8, -9, 3, 5, 6, 3, 0]    
    max_num0 = max(nums, key=lambda x: x < 4)  # [False, False, True, True, False, False, True, True]    
    print(max_num0)  # -9    
    min_num0 = min(nums) #    
    print(min_num0)  

max或者min都可以有一个key参数,当key=x<4,结果是-9,得出这个结果的原因是因为key的结果是Bool值了,max获取最近的那个布尔值,也就是-9.
多个元素采用nlargest()和nsmallest()。

import heapq    
max_nums = heapq.nlargest(3, nums)    
print(max_nums)  # [8, 8, 6]    
max_nums2 = heapq.nlargest(3, nums, key=lambda x: x < 4)    
print(max_nums2)  # [-9, 3, 3]    
min_nums = heapq.nsmallest(3, nums)    
print(min_nums)   # [-9, 0, 2]    
dic = [{"book": "java", "price": 66}, {"book": "python", "price": 60}, {"book": "C", "price": 80}]    
max_ = heapq.nlargest(2, dic, key=lambda x: x.get('price'))    
print(max_)  # [{'book': 'C', 'price': 80}, {'book': 'java', 'price': 66}]  

PS:
当找最大或最小数据的元素占集合中的总元素数量的比例小时,可以使用heapify()性能更好。

# 堆排序算法    
heap = list(nums)    
heapq.heapify(heap)    
print(heapq.heappop(heap)) # -9    
print(heapq.heappop(heap)) # 0  

将数组放到堆中排列,由于堆的第一个元素一定是最小的,所以使用heapop()可以方便获取最小元素。算法复杂度(O(logN),N为堆的大小)。
一般来说,对数组进行排序,可以提升性能。

4.优先级队列

class PriorityQueue(object):    
    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]    

    def get_list(self):    
        return self._queue    


if __name__ == '__main__':    
    q = PriorityQueue()    
    q.push([1,2,3], 2)    
    q.push([3,4,5], 5)    
    q.push([6,7,8], 1)    
    print(list(q.get_list())) # [(-5, 1, [3, 4, 5]), (-2, 0, [1, 2, 3]), (-1, 2, [6, 7, 8])]    
    print(q.pop()) # [3, 4, 5]    
    print(q.pop()) # [1,2,3]  

在push元素时,队列会按照优先级进行排序(堆排序,如heapify同样引用了_siftdown方法),在pop时,有总是返回最小的元素。push和pop方法的时间复杂度都为(O(logN))。

def _siftdown(heap, startpos, pos):    
    newitem = heap[pos]    
    while pos > startpos:    
        parentpos = (pos - 1) >> 1    
        parent = heap[parentpos]    
        if newitem < parent:    
            heap[pos] = parent # 交换位置    
            pos = parentpos    
            continue    
        break    
    heap[pos] = newitem    
# [(-2, 0, [1, 2, 3])]  第一次    
#  [(-5, 1, [3, 4, 5]), (-2, 0, [1, 2, 3])] 2  

5.一键多值

创建以下结构的数据,可以使用defaultdict,它可以让你更容易实现。

{    
    'a': [1,2,3,4,5],    
    'b': [1,2,3,]    
}

{    
    "a": {1,2,3,4},    
    "b": {1,3,5}    
}  
d = defaultdict(list)    
d['a'].append(1)    
d['a'].append(2)    
d['b'].append(3)    
dic = defaultdict(set)    
dic['c'].add(1)    
dic['c'].add(2)    
dic['d'].add(3)    
dic['c'].add(2)    
print(d, dic)

# defaultdict(<class 'list'>, {'a': [1, 2], 'b': [3]}) defaultdict(<class 'set'>, {'c': {1, 2}, 'd': {3}})  

6.字典有序

创建一个字典,同时当对字典进行迭代或序列化操作时,可以控制其中元素的顺序。
使用OrderedDict(),当对字典做迭代时,它会严格按照元素初始位置来进行。

from collections import OrderedDict    
d = OrderedDict()    
d['a'] = 1    
d['b'] = 3    
d['c'] = 2    
d['d'] = 0    
for i in d.items():    
    print(i)    
('a', 1)    
('b', 3)    
('c', 2)    
('d', 0)  

其内部维护了一个双向链表,它会根据元素加入的顺序来排列键的位置。第一个被放置的元素会被放置在链表的末尾。加下来对已存在的键重新赋值不会改变键的位置。
但是其占用的内存是普通字典的两倍。

7.字典有关的计算

计算字典中元素的最大,最小,排序等

dic = {    
    "book1": 88,    
    "book2": 50,    
    "book3": 66,    
    "book4": 77    
}    
print(max(zip(dic.values(), dic.keys())))  # (88, 'book1')    
print(min(zip(dic.values(), dic.keys())))  # (50, 'book2')    
print(sorted(zip(dic.values(), dic.keys())))  # [(50, 'book2'), (66, 'book3'), (77, 'book4'), (88, 'book1')]  

zip()创建一个迭代器,但是其内容只能被消费一次

price_nums = zip(dic.values())    
print(max(price_nums))    
print(min(price_nums)) # ValueError: min() arg is an empty sequence  

提供一个key参数给min()和max(),可以得到最大值和最小值所对应的键。

print(max(dic, key=lambda k: dic[k])) # book1  

8.两个字典中寻找相同点
得到两个字典的相同点,可以使用集合的交、并、补集。

x & y # 交集    
x | y # 并集    
x - y # 差集  
# 两字典相同点    
dic1 = {'x': 2, "y": 5, "z": 0}    
dic2 = {"x": 2, "z": 0, "y": 5, "w": 6}    
print(dic1.keys() & dic2.keys())  # 交集    
print(dic2.keys() - dic1.keys())  # 差集,dic2和dic1的差集,是dic2中有,但dic1中没有的    
print(dic1.items() & dic2.items())

# 结果    
{'z', 'y', 'x'}    
{'w'}    
{('y', 5), ('x', 2), ('z', 0)}  

8.字典去重并保留顺序

下面函数中解释了key的作用

def dedupe(items, key=None):    
    """该函数模拟了sorted,min,max函数中key的用法"""    
    seen = set()    
    for i in items:    
        val = i if key is None else key(i)  # 使用key    
        if val not in seen:    
            yield i  # 返回生成器    
            seen.add(val)    
a = [{"x":1, "y":2}, {"x": 1, "y":3}, {"x":1, "y":2}, {"x": 2, "y":4}]    
dedupe(a, key=lambda d: (d['x'], d['y']))    
>>>[{"x":1, "y":2}, {"x":1, "y":2}, {"x": 2, "y":4}]  

9.切片命名

例子:对字符串“aadcfeghh ok are you ok?ppfddsfsef”

a = "aadcfeghh ok are you ok?ppfddsfsef"  

传统切片:a[13:24]=are you ok?
切片部分可能语义不明
使用slice()
使用slice切片:

word = slice(13, 24)    
result = a[word]    
print(result)  # are you ok?  

10.找到序列中出现次数最多的元素

使用Counter类

words = ['a', 'a', 'c', 'bc', 'c', 'b', 'a', 'd', 'a', 'c']    
from collections import Counter    
counts = Counter(words)    
print(counts.most_common(2)) # [('a', 4), ('c', 3)]  

由于Counter是一个字典,只是在键值之间作了映射,因此,你可以向操作字典一样,操作它.

print(counts['a'])  

如果你希望增加元素,你可以这样做

other_words = ['a', 'b', 'c', 'd']    
counts.update(other_words)    
print(counts['a']) # 5  

Counter可以配合数学运算符来使用

words = ['a', 'a', 'c', 'bc', 'c', 'b', 'a', 'd', 'a', 'c']    
from collections import Counter    
counts = Counter(words)    
other_words = ['a', 'b', 'c', 'd']    
new_counts = Counter(other_words)    
print(counts - new_counts)    
print(counts + new_counts)    
# Counter({'a': 4, 'c': 3, 'bc': 1, 'b': 1, 'd': 1})    
# Counter({'a': 5, 'c': 4, 'b': 2, 'd': 2, 'bc': 1})  

11.对列表中的字典排序

dic = [    
    {"id": 1001, "name": "bob"},    
    {"id": 1002, "name": "lilei"},    
    {"id": 1006, "name": "hmm"},    
    {"id": 999, "name": "xh"}    
]    
di = sorted(dic, key=lambda x: x.get('id'))    
from operator import itemgetter    
di2 = sorted(dic, key=itemgetter('id'))    
print(di, di2)    
# 结果是相同的,都为:[{'id': 999, 'name': 'xh'}, {'id': 1001, 'name': 'bob'}, {'id': 1002, 'name': 'lilei'}, {'id': 1006, 'name': 'hmm'}]  

也就是说lambda与itemgetter的实现是类似的,但是itemgetter()的性能更高,这在max()和min()中同样适用,并且itemgetter()可以传du多个字段。

12.对对象进行排序

class User(object):    
    def __init__(self, user_id):    
        self.user_id = user_id

    def __repr__(self):    
        return 'user id : {}'.format(self.user_id)    

users = [User(3), User(0), User(6)]    
from operator import attrgetter    
print(sorted(users, key=attrgetter('user_id')))    
# [user id : 0, user id : 3, user id : 6]  

13.对数据进行分组

rows = [    
    {"address": 'beijing', "date": "2020/7/1", "weather": "晴天"},    
    {"address": 'shanghai', "date": "2020/7/1", "weather": "晴天"},    
    {"address": 'beijing', "date": "2020/6/30", "weather": "多云"},    
    {"address": 'wuhan', "date": "2020/7/1", "weather": "晴天"},    
    {"address": 'wuhan', "date": "2020/6/29", "weather": "暴雨"},    
]    
from operator import itemgetter    
from itertools import groupby    
rows.sort(key=itemgetter('date')) # 必须排序    
print(groupby(rows, key=itemgetter('date')))  # 可迭代对象,内部结构是元组    
for date, items in groupby(rows, key=itemgetter('date')):    
    print(date)    
    for i in items:    
        print(' ', i)    
# 2020/6/29    
  {'address': 'wuhan', 'date': '2020/6/29', 'weather': '暴雨'}    
2020/6/30    
  {'address': 'beijing', 'date': '2020/6/30', 'weather': '多云'}    
2020/7/1    
  {'address': 'beijing', 'date': '2020/7/1', 'weather': '晴天'}    
  {'address': 'shanghai', 'date': '2020/7/1', 'weather': '晴天'}    
  {'address': 'wuhan', 'date': '2020/7/1', 'weather': '晴天'}  

14.筛选序列中的元素

通常筛选元素采用列表推导式,可以简单且优雅的筛选元素。但是如果,结果过于庞大,可能会让电脑死机(亲身体验)。可以使用生成器解决这个问题。

def filter_item():    
    """从数量集1000万中筛选小于50的数"""    
    nums = [i for i in range(10000000)]    
    items = (n for n in nums if n < 50)    
    print(list(items))  

如果还需要处理复杂逻辑时,可以使用filter(func, iter)

print(list(filter(lambda x: x < 50, nums)))  

15.将名称映射到序列化的元素中

使用类似对象.属性的方式来获取数据。可以采用collections.namedtuple()(命名元组)来为我们提供便利。

>>> from collections import namedtuple    
>>> Sub = namedtuple('Subsc', ['addr', 'joined'])    
>>> sub = Sub('hubei', '2012')    
>>> sub.addr    
'hubei'    
>>> sub    
Subsc(addr='hubei', joined='2012')  

支持拆包addr, joined = sub,namedtuple是不可变的类型,这一点与tuple相同。
如果,你需要修改属性的值,你可以使用_replace()修改
sub = sub.&#95;replace(addr='beijing')

16.多个映射合并为一个映射

将多个字典或者映射,在逻辑上合并为一个单独的映射结构,以执行特定的操作,比如查找值或者检查键是否存在。

>>> a = {'x': 1, 'y': 2}    
>>> b = {'y': 4, 'z': 4}    
>>> from collections import ChainMap    
>>> c = ChainMap(a, b)    
>>> c    
ChainMap({'x': 1, 'y': 2}, {'y': 4, 'z': 4})    
>>> c['x']    
1    
>>> c['w']    
Traceback (most recent call last):    
  File "<pyshell#23>", line 1, in <module>    
    c['w']    
  File "C:\Python\lib\collections\__init__.py", line 916, in __getitem__    
    return self.__missing__(key)            # support subclasses that define __missing__    
  File "C:\Python\lib\collections\__init__.py", line 908, in __missing__    
    raise KeyError(key)    
KeyError: 'w'    
>>> c['y']    
2  

如果有值就会返回结果否则报错。
ChainMap并不是合并字典,而是简单的维护一个记录底层映射关系的列表,然后重定义常见字典操作来扫描这个列表。
如果有重复的键,会优先采用第一个映射对应的值。

标签:Python

文章评论

评论列表

已有0条评论