深入 dict 机制,转化类变量
前言
一般情况下,我们在初始化 dict (字典)时会直接 {key: value} 这么定义,亦或是使用 dict(key=value) 定义使用。而当处理对象变成了在一个类,在使用 obj.__dict__ 魔法函数时,我们拿到的将会是这个类的实例变量,定义在类体中的类变量,我们是无法直接获取到的。
纷争开始
要想实现以 dict 的方式读取类/实例变量,我们duck不必使用库或其他依赖。下面我们看个例子:
上述代码在实际运行时,将会得到 TypeError: ‘A’ object is not iterable 的错误。这是因为 dict 对象是需要可迭代的,也就是需要属于 iter,或者实现其所有方法。
我们再看一个例子:
当我们在 keys 这个方法上打上断点,以断点调试的方法运行程序时会发现,程序将会停在这一行代码上。这是因为,在默认情况下,我们在将对象直接传给 dict 的时候,dict 将会去这个类里寻找 keys 方法,以期望拿到字典的所有键。而这些键是由我们自己定义并返回的。再看下面的例子:
运行上述代码我们将会得到 Type ...
自建博客,关于建站方式的一些事儿
关于博客自建,网上已经有很多详细的教程了,但看到这些教程的前提是咱自个儿得知道博客的搭建方式是什么。是自有服务器搭建?还是挂载第三方搭建?是自己写代码还是别的方式等等。
本文将会列出几种常用的博客搭建的方式,下面正文开始。
WordPress
这是每一个博主都跳不过的搭建方法。
目前大多数的博客网站都是基于WordPress来搭建的。通过它我们可以很方便的搭建属于自己的博客网站。
一方面,它很重,需要进行繁多的配置。另一方面,自有服务器是基础。
得益于Docker容器化,现在可以很方便的部署WordPress程序,不再需要我们自己手动安装PHP以及其他依赖来部署。
写作 - 部署方式
点击 文章标签,新建文章。
输入文章内容,选择号分类和标签。
发布。
网站截图:
前端:
博客后台:
Vuepress/Vitepress
这是现在很流行的一种搭建方法。
现在有很多博主选择使用这种方式来搭建自己的博客,简洁,轻便。许多公司的技术、接口等文档也都是基于这种方式。
它需要我们有点代码基础。
它可以部署在自己的服务器上,也可以挂载到第三方服务(Github Pages等。)
挂 ...
Redis 设计与实现(11-事件)
Redis 服务器是一个 事件驱动程序,服务器需要处理以下两类事件:
文件事件(file event):Redis服务器通过套接字与客户端进行连接,而文件事件就是服务器对 套接字操作 的抽象。服务器与客户端的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作。
时间事件(time event):Redis服务器中的一些操作(比如serverCron函数)需要在给定的时间点执行,而时间事件就是服务器对这类 定时操作 的抽象。
文件事件Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为 文件事件处理器(fileevent handler):
文件事件处理器使用 I/O 多路复用(multiplexing)来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。
当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
虽然文 ...
Redis 设计与实现(10-AOF持久化)
与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。
AOF持久化保存数据库状态的方法是将服务器执行的SET、SADD、RPUSH三个命令保存到AOF文件中。被写入AOF文件的所有命令以Redis的命令请求协议格式保存。
持久化的实现AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。
命令追加当AOF持久化功能打开时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾:
struct redisServer{ //AOF 缓冲区 sds aof_buf;};
文件的写入与同步Redis的服务器进程就是一个事件循环(loop),循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,时间事件则负责执行像serverCron函数这样需要定时运行的函数。
由于服务器在处理文件事件时可能会执行写命令,使得命令被追加到aof_buf缓冲区里面,所以服务器每次结束一个事件循环之前,都 ...
Redis 设计与实现(9-RDB持久化)
目的为了方便起见,将服务器中的非空数据库以及它们的键值对统称为数据库状态。
为了解决服务器进程退出,数据库状态也会消失不见的问题,Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中。
RDB持久化功能所生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。
RDB文件的创建与载入有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE。
SAVE:阻塞Redis服务器进程,直到RDB文件创建完毕为止。
BGSAVE:派生出一个子进程,由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求。
创建RDB文件的实际工作由rdb.c/rdbSave函数完成,SAVE命令和BGSAVE命令会以不同的方式调用这个函数,两个方法的区别如下伪代码所示:
def save(): rdbSave()def bgsave(): ...
Redis 设计与实现(8-数据库)
底层结构Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDb结构,每个redisDb结构代表一个数据库:
struct redisServer{ //一个数组,保存着服务器中的所有数据库 redisDb *db; //服务器的数据库数量 int dbnum;};
在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库。
dbnum属性的值由服务器配置的database选项决定,默认情况下,该选项的值为16。
数据库切换每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。
默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT db命令来切换目标数据库。
在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,这个属性是一个指向redisDb结构的指针: ...
Redis 设计与实现(7-对象)
Redis并没有直接使用SDS、链表、字典、跳跃表、整数集合、压缩列表等这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种前面的数据结构。
Redis在执行命令前,可以根据对象的类型来判断一个对象是否可以执行给定的命令。
针对不同场景,为对象设置多种不同的数据结构实现,优化使用效率。
Redis对象系统实现了基于引用计数技术的内存回收机制。
通过引用计数技术实现了对象共享机制。
Redis的对象带有访问时间记录信息,用以过期清除。
对象的类型与编码每次在Redis的数据库中新创建一个键值对时,至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)。
Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、encoding属性和ptr属性:
typedef struct redisObject{ //类型 unsigned typ ...
Redis 设计与实现(6-压缩列表)
压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项/当一个哈希键只包含少量键值对,并且每个列表项/每个键值对的键和值 要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。
压缩列表的构成压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。
压缩列表组成部分说明:
类型
长度
用途
zlbytes
uint_32
4
记录整个压缩列表占用的内存字节数,在对压缩列表进行内存重分配或计算zlen的位置时使用。
zltail
uint_32
4
记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个偏移量,程序无需遍历整个压缩列表就可以确定表尾节点的地址。
zllen
uint_16
2
记录了压缩列表包含的节点数量:当这个属性的值小于uint16_max(65535)时,这个属性的值就是压缩列表包含节点的数 ...
Redis设计与实现(5-整数集合)
整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
整数集合的实现整数集合(intset)是Redis用于保存整数值的集合抽象数据结构,它可以保存类型为int16_t、int32_t或者int64_t的整数值,并且保证集合中不会出现重复元素。
每个intset.h/intset结构表示一个整数集合:
typedef struct intset{ //编码方式 uint32_t encoding; //集合包含元素数量 uint32_t length; //保存元素的数组 int8_t contents[]} intset;
contents数组是整数集合的底层实现:整数集合的每个元素都是contents数组的一个数组项(item),各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。
length属性记录了整数集合包含的元素数量,即contents数组的长度。
虽然intset结构将conten ...
Redis设计与实现(4-跳跃表)
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。
Redis使用跳跃表作为有序集合键的底层实现之一:
有序集合包含的元素数量比较多。
有序集合中元素的成员(member)是比较长的字符串。
Redis只在两个地方用到了跳跃表:
实现有序集合键。
在集群节点中用作内部数据结构。
跳跃表实现Redis的跳跃表由 redis.h/zskiplistNode 和 redis.h/zskiplist 两个结构定义,其中zskiplistNode结构用于表示跳跃表节点,而zskiplist结构则用于保存跳跃表节点的相关信息。
跳跃表节点跳跃表节点的实现由redis.h/zskiplistNode结构定义:
typedef struct zskiplistNode{ //层 struct zskiplistLevel { //前进指针 ...