【Effective Python】读书笔记-02列表与字典推导

1. 学会对序列做切片

省略

如果是从头开始切割列表,那就应该省略冒号左侧的下标 0,即[:下标]

如果一直取到列表末尾,那就应该省略冒号右侧的下标,即[下标:]

负数

用负数作下标表示从列表末尾往前算

忽略

如果起点与终点所确定的范围超出了列表的边界,那么系统会自动忽略不存在的元素

全新的列表

切割出来的列表是一份全新的列表。即便把某个元素换掉,也不会影响原列表中的相应位置

把不带起止下标的切片([:])放在赋值符号左边,表示是用右边那个列表的副本把左侧列表的全部内容替换掉(引用)

2. 不要在切片里同时指定起始下标和步进

步进切片:somelist[start:end:stride],每 stride 个元素里面取一个。

取奇数位置上的元素:x[::2]
取偶数位置上的元素:x[1::2]

建议:

  • 同时使用起止下标与步进会让切片很难懂,如果必须同时使用,可以分成两次来写,如果需要节省内存,可以使用itertools.islice方法
  • 如果必须指定步进,尽量使用正数,而且要把起止下标都留空

3. 通过带星号的 unpacking 操作来捕获多个元素,不要用切片

通过带星号的表达式(starred expression):

  • 把无法由普通变量接收的那些元素全都囊括进去
  • 可以出现在任意位置,但必须要有一个普通的接收变量与之搭配
  • 对于单层结构来说,同一级里面最多只能出现一次带星号的 unpacking
  • 带*号的部分总会形成一份列表,可能会耗尽计算机的内存
ages = [19, 21, 18, 9, 6, 3]

a, b, *other = ages
# 19 21 [18, 9, 6, 3]
print(a, b, other)

4. 用 sort 方法的 key 参数来表示复杂的排序逻辑

sort 方法:

  • 在默认情况下,sort 方法总是按照自然升序排列列表内的元素。(整数,从小到大)

用法:

user_list = [
    {'age': 18, 'name': 'zhanghaohan'},
    {'age': 18, 'name': 'zhanghaohan1'},
    {'age': 20, 'name': 'zhanghaohan2'},
    {'age': 21, 'name': 'zhanghaohan3'},
    {'age': 30, 'name': 'zhanghaohan4'},
    {'age': 19, 'name': 'zhanghaohan5'},
]
# 按照age排列
user_list.sort(key=lambda user: user['age'])
print(user_list)
# 按照name排列
user_list.sort(key=lambda user: user['name'])
print(user_list)

需求:需要以 name 为首要指标排序,如果 name 相同,则按照 name 排序
解决方案:返回元组。

key 函数在返回这个元组时,可以单独对这项指标取相反数,并保持其他指标不变,这就相当于让排序算法单独在这项指标上采用逆序。(str 类型不支持一元减操作符)

user_list.sort(key=lambda user: (user['name'], user['age']))
print(user_list)

如果需要对不同指标排序,可以多次调用 sort 方法(万不得已的时候)。

5. 不要过分依赖给字典添加条目时所用的顺序

字典不保证迭代顺序与插入顺序一致的原因(Python3.5 之前):

字典类型以前是用哈希表算法来实现的(这个算法通过内置的 hash 函数与一个随机的种子数来运行,而该种子数会在每次启动 Python 解释器时确定)。所以,这样的机制导致这些键值对在字典中的存放顺序不一定会与添加时的顺序相同,而且每次运行程序的时候,存放顺序可能都不一样。

现在的 Python 语言规范已经要求,字典必须保留添加键值对时所依照的顺序。

6. 用 get 处理键不在字典中的情况,不要使用 in 与 KeyError

dict 的内置方法get用于查询 dict 中是否有对应的键,如果没有就返回 None。

dict 类型提供了 setdefault 方法,查询字典里有没有这个键,如果有,就返回对应的值;如果没有,就先把用户提供的默认值跟这个键关联起来并插入字典,然后返回这个值。setdefault 方法会把默认值直接放到字典里。

每次调用 setdefault 时,都要构造一个新的默认值,即每次调用时,不管字典里有没有这个键,都得分配一个实例,有可能产生比较大的性能开销。

只有在少数几种情况下用 setdefault 处理缺失的键才是最简短的方式:

  • 与键相关联的默认值构造起来开销很低且可以变化,而且不用担心异常问题(例如 list 实例)

字典带有默认值多的场景:应该优先考虑用 defaultdict(带默认值的字典)取代 dict

7. 用 defaultdict 处理内部状态中缺失的元素,而不要用 setdefault

collections 模块提供了 defaultdict 类:

  • 在键缺失的情况下,自动添加这个键以及键所对应的默认值
  • 只需要在构造这种字典时提供一个函数就行了,每次发现键不存在时,该字典都会调用这个函数返回一份新的默认值
  • 传给 defaultdict 的那个函数只能是不需要参数的函数
  • 性能更好
from collections import defaultdict


class CountryMap:

    def __init__(self):
        # 传入一个用于生成默认值的函数
        # self.data = defaultdict(set)
        # 等价写法
        self.data = defaultdict(lambda: set())

    def add(self, country, city):
        self.data[country].add(city)


country_map = CountryMap()
# 虽然data里面暂时没有China这个key,但是会赋予默认值set
country_map.add('China', 'wu han')
# defaultdict(, {'China': {'wu han'}})
print(country_map.data)

关于性能对比:https://stackoverflow.com/questions/7423428/python-dict-get-vs-setdefault

8. 学会利用__missing__构造依赖键的默认值

如果要构造的默认值必须根据键名来确定,那么可以定义自己的 dict 子类并实现__missing__方法。

import random


class Test(dict):

    def __missing__(self, key):
        value = f'test:{random.randrange(10)}'
        self[key] = value
        return value


t = Test()
print(t['hello'])

你可能感兴趣的:(python,开发语言)