Python–cookbook–1.数据结构与算法

Python–cookbook–1.数据结构与算法

解压序列赋值给多个变量

# 可以使用在任何可迭代对象上面,但数量必须保持一致
# 可以使用占位丢弃一部份值
data = ['ACME', 50, 91.1, (2012, 12, 21)]
name, shares, price, (year, mon, day) = data
#  _, shares, price, _ = data
print(name, shares, price, year, mon, day)

解压可迭代对象赋值给多个变量

# 使用*表示解压不确定元素个数,显示为列表类型
record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
name, email, *phone_numbers = record
# name, *_, (*_, year) = record
print(phone_numbers)

函数

records = [
    ('foo', 1, 2),
    ('bar', 'hello'),
    ('foo', 3, 4),
]
def do_foo(x, y):
    print('foo_fun', x, y)
def do_bar(s):
    print('bar_fun', s)
for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)

文件对比,对比当前行和之前行

def search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)  # maxlen保证最大数量,超过会把前面挤出去
    for line in lines:
        if pattern in line:
            yield line, previous_lines  # 使得函数为函数生成器,可迭代return
        previous_lines.append(line)
with open(r'useless_1.txt') as f:
    for curline, prevlines in search(f, 'python', 5):
        for pline in prevlines:
            print(pline, end='')
            print(curline, end='')
            print('-' * 20)

查找最大或最小的N个元素

from heapq import nlargest, nsmallest, heappop, heappush, heapify
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(nlargest(3, nums))  # Prints [42, 37, 23]
print(nsmallest(3, nums))  # Prints [-4, 1, 2]
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 = nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = nlargest(3, portfolio, key=lambda s: s['price'])
print(cheap)
print(expensive)
# 想查找唯一的最小或最大(N=1)的元素的话,那么使用 min() 和max() 函数会更快些。
# 类似的,如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点

实现优先级队列

class PriorityQueue:
    def __init__(self):
        self._queue = []
        self._index = 0
    def push(self, item, priority):
        heappush(self._queue, (-priority, self._index, item))
        self._index += 1
    def pop(self):
        return heappop(self._queue)[-1]
class Item:
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'Item({!r})'.format(self.name)

字典中的键映射多个值

a_dict = defaultdict(list)
b_dict = defaultdict(set)

字典排序

d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4
# 迭代会保持插入的顺序
for key in d:
    print(key, d[key])

字典的运算

prices = {
    'ACME': 45.23,
    'AAPL': 612.78,
    'IBM': 205.55,
    'HPQ': 37.20,
    'FB': 10.75
}
# zip() 函数创建的是一个只能访问一次的迭代器
min_price = min(zip(prices.values(), prices.keys()))
print(min_price)
max_price = max(zip(prices.values(), prices.keys()))
print(max_price)
prices_sorted = sorted(zip(prices.values(), prices.keys()))
print(prices_sorted)
min_key = min(prices, key=lambda k: prices[k])
print(min_key)

查找两字典的相同点

a = {
    'x': 1,
    'y': 2,
    'z': 3
}
b = {
    'w': 10,
    'x': 11,
    'y': 2
}
# key视图支持一些集合操作,items也支持,而value不支持,因为某些值可能相等
print(a.keys() & b.keys())
print(a.keys() - b.keys())
print(a.items() & b.items())
print({key: a[key] for key in a.keys() - {'z', 'w'}})

删除序列相同元素并保持顺序

# 如果是可哈希的
def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)
aa = [1, 5, 2, 1, 9, 1, 5, 10]
print(list(dedupe(aa)))
print(set(aa))  # 不能维护元素的顺序
# 如果是不可哈希的
def dedupe2(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)
ab = [ {'x':1, 'y':2}, {'x':1, 'y':3}, {'x':1, 'y':2}, {'x':2, 'y':4}]
print(list(dedupe2(ab, key=lambda d: (d['x'], d['y']))))
print(list(dedupe2(ab, key=lambda d: d['x'])))

命名切片

record = '....................100 .......513.25 ..........'
SHARES = slice(20, 23)
PRICE = slice(31, 37)  # 更佳清晰可读
# cost = int(record[20:23]) * float(record[31:37])
cost = int(record[SHARES]) * float(record[PRICE])
print(cost)
aslice = slice(5, 50, 2)
print(aslice.start, aslice.stop, aslice.step)
s = 'HelloWorld'
print(aslice.indices(len(s)))  # indices(size) 方法将它映射到一个确定大小的序列上
for i in range(*aslice.indices(len(s))):
    print(s[i], end=' ')
print()

序列出现次数最多的元素

from collections import deque, defaultdict, OrderedDict, Counter, namedtuple, ChainMap
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'
]
word_counts = Counter(words)  # Counter字典
top_three = word_counts.most_common(3)  # 返回列表元组
print(top_three)

通过某个关键字排序一个字典列表

from operator import itemgetter, attrgetter
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}
]
rows_by_fname = sorted(rows, key=itemgetter('fname'))
# rows_by_fname = sorted(rows, key=lambda x : (x['fname']))
print(rows_by_fname)
# itemgetter也支持多个key,而且itemgetter速度比lambda快一点
# 除了适用于sorted,也适用于min和max
rows_by_lfname = sorted(rows, key=itemgetter('lname', 'fname'))

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

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(sorted(users, key=lambda u: u.user_id))  # 使用user_id排序
# 使用attrgetter代替lambda
print(sorted(users, key=attrgetter('user_id')))

通过某个字段将记录分组

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'},
]
rows.sort(key=itemgetter('date'))
# groupby() 函数扫描整个序列并且查找连续相同值,在每次迭代的时候,它会返回一个值和一个迭代器对象
# 需要排序
# defaultdict()也可以完成
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ', i)

过滤序列元素

mylist = [1, 4, -5, 10, -7, 2, 3, -1]
# 使用列表推导
print([n for n in mylist if n > 0])
# 使用生成器
print((n for n in mylist if n > 0))  # 减少内存占用
# 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)
# 列表生成式还能转换数据
print([n if n > 0 else 0 for n in mylist])
# 过滤工具就itertools.compress()
# 先创建一个 Boolean 序列,指示哪些元素符合条件
# 然后compress() 函数根据这个序列去选择输出对应位置为 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]
more5 = [n > 5 for n in counts]
print(more5)
print(list(compress(addresses, more5)))

从字典中提取子集

prices = {
    'ACME': 45.23,
    'AAPL': 612.78,
    'IBM': 205.55,
    'HPQ': 37.20,
    'FB': 10.75
}
# 字典推导式
p1 = {key: value for key, value in prices.items() if value > 200}

映射名称到序列元素

# collections.namedtuple()命名元组
Subscriber = namedtuple('Subscriber', 'addr joined')
# Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
sub = Subscriber('jonesy@example.com', '2012-10-19')
print(sub, sub.addr, sub.joined)
# 命名元组的一个主要用途是将你的代码从下标操作中解脱出来
def compute_cost(records):
    total = 0.0
    for rec in records:
        total += rec[1] * rec[2]
    return total

Stock = namedtuple('Stock', ['name', 'shares', 'price'])
def compute_cost2(records):
    total = 0.0
    for rec in records:
        s = Stock(*rec)
        total += s.shares * s.price
    return total
# 命名元组另一个用途就是作为字典的替代,因为字典存储需要更多的内存空间
# 但是不像字典那样,一个命名元组是不可更改的
# 需要改变属性的值,那么可以使用命名元组实例的 _replace() 方法,它会创建一个全新的命名元组并将对应的字段用新的值取
s = sub._replace(joined='2022-10-19')
print(s)
# replace() 方法还有一个很有用的特性就是当你的命名元组拥有可选或者缺失字段时候,它是一个非常方便的填充数据的方法。
Stock2 = namedtuple('Stock', ['name', 'shares', 'price', 'date', 'time'])
stock_prototype = Stock2('', 0, 0.0, None, None)
def dict_to_stock(s):
    return stock_prototype._replace(**s)
b = {'name': 'ACME', 'shares': 100, 'price': 123.45, 'date': '12/17/2012'}
print(dict_to_stock(b))

转换并同时计算数据

# 使用生成器
nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)
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)
min_shares2 = min(portfolio, key=lambda s: s['shares'])

合并多个字典或映射

# ChainMap,不创建新的字典
a = {'x': 1, 'z': 3 }
b = {'y': 2, 'z': 4 }
c = ChainMap(a, b)  # 大部分字典操作都是可以正常使用的
print(c)
print(c['x'])  # Outputs 1 (from a)
print(c['y'])  # Outputs 2 (from b)
print(c['z'])  # Outputs 3 (from a)
print(list(c.keys()))
print(list(c.values()))  # 如果出现重复键,那么第一次出现的映射值会被返回
# 对于字典的更新或删除操作总是影响的是列表中第一个字典
c['z'] = 10
c['w'] = 40
del c['x']
print(a)
print(b)
# new_child parents
# update合并字典
merged = dict(b)  # 创建一个完全不同的字典对象,原字典做了更新,这种改变不会反应到新的合并字典中
merged.update(a)
print(merged)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柴寺仓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值