python数据库查询--开源项目Records源码分析

零:说明

Records是一个对python中关系型数据库查询的封装,简化了在python中进行数据库查询的操作。项目地址如下:GitHub - kennethreitz/records: SQL for Humans™

作者Kennethreitz是requests和python-guide 额作者,python领域的大牛人物之一,关于他还有一个励志的故事:谁说程序员不是潜力股?让这位世界前五名的天才程序员来颠覆你三观!

项目本身代码短小,总共只有一个文件不超过500行,如果是第一次尝试阅读python开源项目,这是一个很好的选择。正好最近在学习数据库方面的相关知识,也可以加深一下对关系型数据库的理解。

Records实现了对直接输入SQL语句(raw SQL)进行查询的返回结果的优化,并没有实现ORM之类高级的技巧,这种注重使用体验的优化,正如其名:SQL for humans。尽管如此,作者所使用的架构技巧,包括惰性查询,缓存的使用,还是有着一定的借鉴意义。

一:项目分析

这个项目中的代码并不是很多,因此打算将项目里的每个类都分析一下。虽然逻辑并不复杂,但是作者对每个类的设计和架构,对python中类相关技巧都使用,都有一些可以学习的地方。

  • Record

这是用来存储查询结果(也就是数据库中的一行)的类,用了很多类的特殊方法进行架构,因此重点分析这些特殊方法。

python提供特殊命名的方法来拦截运算,以双下划线(__X__)命名的钩子与内置运算一一对应。

部分特殊方法的参考文档

  1. __slots__

    __slots__包含类属性的字符串,可以限制类的合法属性集,只对新式类有作用。是为了节省实例的内存消耗,类中的方法不受其限制。新式类中原本含有一个__dict__属性,用来记录实例中所有的属性和方法,也是通过这个字典,可以让实例绑定任意的属性,而__slots__存在时,就不会有__dict__变量。
    __slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的,除非在子类中也定义__slots__,这样,子类允许定义的属性就是自身的__slots__加上父类的__slots__

  2. __getitem__()

__getitem__()是对[]的重载,同时,函数内使用了index()对列表中的内容反查其索引,实现了通过record[key]查找value的功能。如果是我大概会用一个索引迭代,但实际上不需要。

  1. __dir__()

    __dir__()方法指明了对实例使用内置函数dir()时需要返回的列表, 源代码中在基类的基础上加上了keys的值列表。

  2. 其他

    对于类实例的显示用了另一个他自己写的库tablib,用于对数据进行格式化。

    在as_dict()中用到了一个三目运算符

    A = X if conditon else Y 
    #等价于:
    if conditon:
        A = X
    else:_all_rows
        A = Y
    
  • RecordCollection

这个类是Record的集合类,用于存储查询(query)结果。

  1. 构造

    构造函数传入一个逐行的Record generator,存储在成员变量_rows 中。

    generator支持迭代,通过圆括号形成的生成器表达式生成,例如:

    (x for x in range(5)) # <generator object <genexpr> at 0x7fd55f3fe9b0>

  2. __iter__() & yield & next()

    __iter__()next()与迭代器有关,在Python中实现了__iter__()方法的对象是可迭代的,实现了next()方法的对象是迭代器(iterator)
    for循环为例,事实上,for i in obj的迭代就是通过内置函数iter(obj)调用它的__iter__()方法取得一个迭代器对象(但在源码中是返回了一个生成器,见下文说明)。

    源码中在__iter__()函数内部使用了yeild关键字。含有yield的函数称为生成器函数,生成器函数返回时使用yield关键字,与return的用法类似,但是,生成器函数返回时会保存包含其本地作用域的状态,之后将函数挂起,在下次调用函数时,从挂起的地方继续。事实上,含有yield的语句被调用时会产生一个生成器(generator)从而支持迭代。

    生成器和迭代器都通过next()方法不断生成下一个对象,如果迭代到达末尾,在next() 中会抛出StopIteration异常。

    <u>generator自身是一个iterator,但只支持一次迭代。</u>

  3. 缓存

    _all_rows变量用于缓存,缓存有几种不同的情况需要考虑:

    • 顺序迭代,在next()中一旦执行了_rows_.next()就缓存迭代结果。
    • 直接调用collection[index]操作获取指定行,在__getitem__()中会顺序迭代到指定位置,对在指定行之前的行全部缓存,同时,之后如果再调用iterator,就不需要调用iterator.next()而是直接从缓存中取用,这一点在__iter__()中也有体现。
    • 要显示对象本身的时候,会显示其中包含的行,此时要求对_rows 迭代求值,并且缓存。在源代码中,位于all()函数中,通过rows = list(self)调用类的__iter__()方法。
  • Database

这是执行数据库查询与数据库连接的类。

  1. SQLAlchemy

    数据库查询主要依赖于SQLAlchemy库的调用,这是一个python的ORM框架。ORM即对象关系映射,简而言之,就是把数据库的一个个table(表),映射为编程语言的class(类),这里主要介绍在源码中使用到的几个方法。SQLAlchemy实际上可以将数据库查询完全转化为对象的调用,但是在records这个库里,只是对原生的数据库查询返回进行了封装,所有并没有使用到这些功能。

    详细使用方法:SQLAlchemy 1.1 Documentation

    • create_engine()方法进行数据库连接,返回一个database对象。之后通过engine.connect()获取connection, 然后通过其执行sql, 叫做connection执行,其实通过engine.execute()也可以执行sql,不过connection执行与使用transaction模式有关, 如果不涉及transaction, 两种方法效果是一样的。

    • inspect()方法创建了一个Inspector对象。通过这个对象,可以实现对数据库中的schematableconstraint等结构的查询。例如,insp(engine).get_table_names()

    • connection.excute("sql")返回一个p = ResultProxy对象,通过p.keys()可以得到查询的列名,而通过迭代p可以获得查询的每一行,以此建立一个RecordCollection

    • text()用于提供附带参数的查询功能,例如:

      t = text("SELECT * FROM users WHERE id=:user_id")
      result = connection.execute(t, user_id=12)
      
  2. 上下文管理器

    有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python提供了一些解决的方案(以文件读写为例)。

    • 利用try-finally语句块来进行包装,这种方法易于理解,但是当逻辑复杂时就会十分混乱:

      writer = open(filename, mode)
      try:
          writer.write(“”)
      finally:
          writer.close()
      
    • 通过with语法重写以上逻辑,就可以简化为:

      with open(filename, mode) as writer:
          writer.write(“”) 
      

      事实上,with语句基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。

      __exit__()还可以处理异常,在with后面的代码块抛出任何异常时,__exit__()方法被执行。异常抛出时,与之关联的type,value和stack trace传给__exit__(self, exc_type, exc_val, exc_stack)方法。因此,可以在清理资源,关闭文件等等操作后,将异常重新抛出。

  3. query_file

    通过open()操作读取一个文件,然后通过query()函数执行内部sql语句。

  4. transition

    通过connection.begin()方法实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容

  • python学习笔记 声明:学习笔记主要是根据廖雪峰官方网站python学习学习的,另外根据自己平时的积累进行修正...
    renyangfar阅读 3,025评论 0 10
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,451评论 6 428
  • 本文翻译自Functional Programming Howto 本文将介绍Python中函数式编程的特性。在对...
    大蟒传奇阅读 2,603评论 4 14
  • 教程总纲:http://www.runoob.com/python/python-tutorial.html 进阶...
    健康哥哥阅读 2,013评论 1 3
  • 天气在这个时候渐渐温暖起来,新年的余味还在我的鼻尖上打着转,相聚那一刻的动情,每个人都失去了应该有的表达能力,只是...
    赵不弃阅读 364评论 2 1