一日一技:跟着Kenneth Reitz大神学习读取类属性的三种方法
在看Kenneth Reitz大神的Records项目时,注意到在Readme中,读取数据有三种写法:
Values can be accessed many ways: row.user_email, row['user_email'], or row[3]
假设一个数据表如下所示:
username |
active |
name |
user_email |
timezone |
---|---|---|---|---|
model-t |
True |
Henry Ford |
model-t@gmail.com |
2016-02-06 22:28:23.894202 |
那么,如果你想读取user_email的值,除了可以使用 row.user_email
和 row['user_email']
以外,由于 user_email
在第3列(username是第0列),所以还可以使用 row[3]
来读取。
今天我们就来研究一下,他是如何实现这个功能的。
假设我们现在有一个类:
class People: def __init__(self, name, age): self.name = name self.age = age
def walk(self): print('i am walk')
基于这个类初始化一个对象kingname:
kingname = People('kingname', 26)print(kingname.age)
运行效果如下图所示:
可以看到,我们已经实现了类似于records项目中的 row.user_email
的写法。
但是当我们想像字典一样取读取的时候,就会报错,如下图所示。
此时,为了让一个对象可以像字典一样被读取,我们需要实现它的 __getitem__
方法:
class People: def __init__(self, name, age): self.info = {'name': name, 'age': age}
def walk(self): print('i am walk')
def __getitem__(self, key): return self.info.get(key, '不存在')
此时就可以像字典一样去读取了,运行效果如下图所示:
但新的问题又来了,不能直接使用 kinganme.age
读取数据了,这样写会导致报错,如下图所示:
为了解决这个问题,我们再来实现这个类的 __getattr__
方法:
class People: def __init__(self, name, age): self.info = {'name': name, 'age': age}
def walk(self): print('i am walk')
def __getitem__(self, key): return self.info.get(key, '不存在')
def __getattr__(self, key): return self.info.get(key, '不存在')
运行效果如下图所示:
那么如何实现records里面的 row[3]
这种写法呢?
这就需要先解释一下,records这个库是用来做什么的,以及它的数据是如何存放的。
K大写的records,是用于来更加方便地读写SQL数据库,避免繁琐地写各种SQL语句。
我们今天要研究的这个写法,在 Record
这个类里面。这个类用来保存MySQL中的一行数据。Record
这个类包含两个属性,分别为 _keys
和 _values
,其中 _keys
用于记录所有的字段名, _values
用于记录一行的所有值。字段名和值是按顺序一一对应的。
例如 _keys
里面下标为3表示字段名 user_email
,那么 _values
里面下标为3的数据就是 user_email
的值。
正是由于这样一个一一对应的关系,所以 row[3]
才能实现 row['user_email']
、 row.user_email
相同的效果。
回到我们的People类,为了实现相同的目的,我们再次修改代码:
class People: def __init__(self, name, age): self._keys = ['name', 'age'] self._values = [name, age]
def walk(self): print('i am walk')
def __getitem__(self, key): if isinstance(key, int): if 0 <= key < len(self._keys): return self._values[key] return '索引超出边界'
index = self._keys.index(key) if index == -1: return 'Key不存在' return self._values[index]
def __getattr__(self, key): return self.__getitem__(key)
运行效果如下图所示:
需要说明的是,无论是使用 kingname['key']
还是 kingname[1]
,他们都会进入到 __getitem__
方法中去。
我这里给出的例子相较于 records
项目的代码做了简化,不过关键的部分都已经囊括了进来。
最后,推荐有兴趣的同学通读records这个项目的源代码,这个项目只有一个文件,包括注释和空行才500多行代码,但你将会从K大的代码中学到非常多的东西。
在本公众号回复:records 就可以获得这个项目的Github地址。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- C++ 实现通过类名来进行实例化(反射机制?)
- Flutter基础widgets教程-ExpansionPanel篇
- muduo TcpServer粗略过程
- muduo 超时重连
- EventLoop,TcpClient,TcpServer 中的生命周期
- functional 和 bind 用法
- Flutter基础widgets教程-FittedBox篇
- muduo Dispatcher消息分发器 通过多态和模板进行向上类型转换
- Flutter基础widgets教程-FloatingActionButton篇
- VBS基础篇 - 对象(3) - FileSystemObject对象
- vbs 实现文件夹拷贝--采用堆栈不递归
- vbs - 一个简单的栈 -- 只能存储类对象
- Flutter基础widgets教程-FlutterLogo篇
- 配置压缩版mysql 5.7以上版本
- nutz 自定义sql的使用