1.深入 Python —— 切片(Slice)原理
2.lodash源码解析:chunk、slice、toInteger、toFinite、toNumber
3.leveldb之数据存储结构
深入 Python —— 切片(Slice)原理
在深入探讨 Python 切片原理之前,我们先检验一下对切片的政务微信源码基本理解。请尝试回答以下几个问题:
不借助Python解释器,能迅速给出答案的,说明你对切片掌握得不错。许多人可能对其中一个问题不完全确定。没关系,本文将从底层实现角度全面掌握切片机制,学完之后,再回头来看这些问题就不再是难题。
让我们从字节码层面开始。这段字节码分为两部分,前半部分构建列表 a,后半部分通过 a 切片得到列表 b。与本文主题相关的封包调试工具源码关键字节码指令在后半部分,它们是:
BUILD_SLICE
在执行 BUILD_SLICE 之前,解释器将切片的两个关键参数 start 和 stop 压入栈,然后执行 BUILD_SLICE 指令。传入参数为 2,这意味着构建的切片对象只包含两个参数,即没有指定第三个参数 step。
这段代码简明,首先根据传入参数个数判断切片是否包含 step,如果有,它会成为最后一个被压入栈的参数。接下来,从栈中取出 start 和 stop,并将这三个参数传入 PySlice_New 函数创建切片对象,再将此对象放回栈中。
现在,让我们进一步探索切片对象的内部结构:
现在明白了吗?当我们对序列进行切片时,解释器会根据传入的 start、stop、手动修改url链接源码step 创建切片对象,该对象与要切片的原序列之间没有直接关联。
Python 提供内置函数 slice 来创建切片对象:
这两种获取切片方式等价:
BINARY_SUBSCR
这个指令称为二元下标,即 a[0] 的方式是一元下标。那么,通过切片对象对序列进行切片与通过索引对序列取值是否有关联?继续查看源码:
从栈中取出的对象是前面构建的切片对象,而 container 对象则是要切片的原列表,它们被传给了 PyObject_GetItem 函数。
答案显而易见,二元下标即切片通过 PyObject_GetItem 函数处理,它同样用于处理一元下标!
PyObject_GetItem 实现了多态性,根据要切片的对象不同,调用对象的特定函数以进行不同的处理。列表的处理将在后续讨论,现在我们需要理解,序列的下标可以是整数或切片对象,它们的烈火战神页游源码处理接口相同。
切片参数的处理涉及 start、stop 和 step 的值,这些值可以是整数,可以是负数,start 和 stop 可能超过列表长度。特殊 step、stop 值决定了切片结果,这些处理在 PySlice_GetIndicesEx 函数中完成。理解切片行为的核心在于理解这个函数的逻辑。
记住以下几点:
深入列表切片处理:
切片适用于所有序列对象:列表、字符串、元组。我们日常最常使用的列表切片就在这里进行深入探讨,其他两种处理方式也类似。
通过查看列表对象源码,我们发现, o->ob_type->tp_as_mapping->mp_subscript 和 list.__getitem__ 指向同一个函数——list_subscript,列表切片正是手机机器人源码在这里处理的:
其中的 list_slice 函数在 step 等于 1 时简化版:
总结:
本文从源码层面深入分析了切片对象、对 start、stop、step 值的处理,以及虚拟机生成列表切片的全过程。理解 Python 对 start、stop、step 的处理逻辑后,文章开始处的问题将不再能给出答案。
lodash源码解析:chunk、slice、toInteger、toFinite、toNumber
深入解析lodash源码,旨在探索最流行的npm库逻辑,本文将依次解读chunk、slice、toInteger、toFinite、toNumber以及相关辅助函数。
chunk函数帮助将数组分块,具体实现需考虑输入数组长度与指定块大小。
slice功能用于截取数组段落,遵循数组原生方法,简洁高效。
toInteger函数将数值转换为整数,处理边缘情况确保准确。
toFinite实现将数值转换为有限浮点数,确保数学运算的稳定性。
toNumber方法将任何值转换为浮点数,适用于复杂数据类型。
isObject检查是否为对象,提供基础类型判断。
isSymbol判断是否为符号,用于更细粒度的类型识别。
getTag通过标签获取对象类型,实现更精确的类型识别。
纯JS实现:在寻找lodash源码时,发现了You-Dont-Need-Lodash-Underscore仓库,它使用纯JS实现了Lodash/Underscore的诸多方法,适用于特定场景,减少引入lodash的开销。
总结:通过解析lodash源码,不仅深入了解了其功能实现,还对比了纯JS实现方式,有助于在特定需求下做出合理选择。
leveldb之数据存储结构
leveldb中的数据存储结构设计巧妙,尽管在源码中编码和反编码较为复杂,但理解时可以将其当作黑盒子。本文主要讨论几个关键组件:Slice、Varint/、InternalKey、Comparator、SSTable、DataBlock、IndexBlock、FilterBlock、MetaIndexBlock以及Log和WriteBatch。
Slice是一个轻量级的数据结构,类似Go语言的切片,用于方便传递和引用数据子串,尤其在处理C++标准库中的std::string时,Slice更轻便,不需复制子串。
Varint/是变长编码,用于节省存储空间,如位整型,通过MSB和后续7位表示数据,最长可编码到5字节。这种编码方式使得数字存储更加紧凑。
InternalKey是存储用户数据的关键,由user_key、sequence和type组成,sequence用于版本控制和数据合并,type区分值类型和删除标记。删除时,leveldb通过日志追加而非直接修改,确保数据一致性。
Comparator接口用于自定义key的比较逻辑,而InternalKeyComparator结合user_comparator,通过用户键和序列进行排序,保证新数据在旧数据的前面。
SSTable由DataBlock、MetaIndexBlock和IndexBlock组成,DataBlock采用前缀压缩和重启点设计,提高了空间效率。IndexBlock则用于记录DataBlock的映射,采用跳点策略来压缩key。
FilterBlock在构建Block的同时生成BloomFilter,用于快速过滤查找。MetaIndexBlock存储元信息到MetaBlock的映射。
Footer用于文件校验和解析,包含索引和元数据信息。MemTable使用skiplist结构,支持高效查找,通过墓碑标记删除,保持数据一致性。
Log负责持久化数据,避免内存丢失。WriteBatch用于批量操作,保证原子性,并进行序列化,便于数据恢复。