你的第一份Python庫源碼瀏覽:records庫

尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️

加入LINE好友

你的第一份Python庫源碼閱讀:records庫

基本介紹

records是kennethreitz的for Humans™系列,使用原生sql去操作大多數的關係型數據庫(Postgresql, MySQL, SQLite, Oracle和 MS-SQL),並且支持多種格式輸出,如csv、excel、json等。

https://github.com/kennethreitz/records

代碼不超過1000行,如果是第一次嘗試閱讀python開源項目,這是一個很好的選擇。作者Kennethreitz是requests的作者,python領域的大牛人物之一,關於他還有一個勵志的故事[Kenneth Reitz的逆襲之路]

使用方法

records庫的使用非常簡單且人性化,定義數據庫連接串和sql語句,然後將返回值作為rows列印出來,或者輸出為文件,沒有複雜的orm邏輯,做到邏輯很清晰

import records
db = records.Database('postgres://...')
rows = db.query('select * from active_users') # or db.query_file('sqls/active-users.sql')
>>> rows[0]
<Record {"username": "model-t", "active": true,
"name": "Henry Ford", "user_email": "[email protected]"}>
for r in rows:
print(r.name, r.user_email)

依賴庫

records有一些pip依賴,每個依賴項的作用如下:

1)psycopg2==2.6.1
'''
psycopg2是python操作PostgreSQL數據庫的庫,但是在records的代碼里並沒有看到哪里有使用,sqlalchemy畢竟已經支持了postgresql,
而psycopg2是官方指定推薦的python driver
'''
2)py==1.4.31
'''
library with cross-python path, ini-parsing, io, code, log facilities
'''
3)pytest==2.8.7
'''
python單元測試
'''
4)tablib==0.11.1
'''
tablib:用於導出xls、csv、yaml等格式,records中的主要功能之二:導出sql查詢的結果
參考鏈接:http://www.open-open.com/lib/view/open1410700050414.html
官方文檔:https://pypi.python.org/pypi/tablib
'''
5)sqlalchemy==1.0.11
'''
用於數據庫操作,python中最有名的ORM框架,records的主要功能:基於sqlalchemy進行封裝
sqlalchemy: Python中,最有名的ORM框架是SQLAlchemy
參考文章:1
參考文章:http://www.jb51.net/article/49789.htm
參考文章:http://blog.csdn.net/mmx/article/details/48064109
import text: sqlalchemy推薦使用text()函數封裝一下sql字符串
create_engine:建立數據庫連接引擎
import declarative_base: 使用sqlalchemy.ext.declarative 來生成表, 所有的表都必須有主鍵.
'''
'''
6)docopt==0.6.2
'''
docopt根據你寫的文檔描述,自動為你生成解析器,可以非常容易的為你的python程序創建命令行界面,用於很多庫的命令行指引。
'''

源碼分析工具

建議的源碼分析工具:pycharm3.x (運行調試)+ Source Insight 4.0(展示類/變量/方法結構圖)

Source insight是一款很不錯的閱讀源碼的工具,支持很多語言,有些人說好像3版本默認不支持python,需要配置,我下載的 Source Insight 4.0,沒這個問題,打開就可以用。

代碼結構

Database類:

封裝基本數據庫操作,主要使用query方法,調用SQLAlchemy的方法,獲取結果後調用Record類獲得Record生成器,再調用RecordCollection獲得所有的結果

* 調用sqlalchemy中的declarative_base獲取table的所有表名
metadata = declarative_base().metadata
metadata.reflect(create_engine(self.db_url))
return metadata.tables.keys()
* 做到query_file和transaction,可以使用本地sql文件,支持事務。
* _enter__和__exit__配合,做到with數據庫上下文管理器
Python

Record類:

接收database查詢後的keys和rows,初始化時,檢測是否長度一致,然後對其包裝,使其支持迭代,支持直接to_dict轉為dict對象,支持直接export導出。

除了基本的[0]索引形式,Record方法使其支持字符串查詢,屬性查詢,支持get屬性查詢
1)支持以字符串的形式索引查找
if key in self.keys():
i = self.keys().index(key)
return self.values()[i]
2)支持以屬性的形式查詢
try:
return self[key]
except KeyError as e:
raise AttributeError(e)
3)支持get查詢:
try:
return self[key]
except KeyError:
return default
4)通過tablib庫,做到dataset屬性:轉為各種格式輸出(json/txt/csv)
def dataset(self):
data = tablib.Dataset()
data.headers = self.keys()
row = _reduce_datetimes(self.values())
data.append(row)
return data

RecordCollection類:

部分方法和Record類相同,但RecordCollection做到了first方法,獲取第一個row,如果不存在,則默認default為none,如果defalut本身就是實例或者exception的子類,直接拋出異常,另外,做到了一次實例化後多次查詢時的緩存。

i == 0
while True:
# Other code may have iterated between yields,
# so always check the cache.
if i < len(self):
yield self[i]
else:
# Throws StopIteration when done.
yield next(self)
i += 1

全局變量和方法:

1)_reduce_datetimes方法,在tablib轉為json等格式輸出時,轉化row中的時間字段。
row = list(row)
for i in range(len(row)):
if hasattr(row[i], 'isoformat'):
row[i] = row[i].isoformat()
return tuple(row)
2)cli主方法,通過docopt獲取命令行輸入的參數,做合法檢測等
arguments = docopt(cli_docs)
# Create the Database.
db = Database(arguments['--url'])
query = arguments['<query>']
params = arguments['<params>']

基礎概念:

1)獲取系統環境變量,records中,初始化時傳入數據庫url,如果沒傳,才去找系統變量中的url
self.db_url = db_url or DATABASE_URL
DATABASE_URL = os.environ.get('DATABASE_URL')
'''
os.environ 獲取系統環境變量
參考文檔:http://blog.csdn.net/junweifan/article/details/7615591
'''
2)限制class的屬性
__slots__ = ('_keys', '_values')
3)變量和方法名匯總
"""
變量:
1. 前帶_的變量: 標明是一個私有變量, 只用於標明, 外部類還是可以訪問到這個變量
2. 前帶兩個_ ,後帶兩個_ 的變量: 標明是內置變量,
3. 大寫加下劃線的變量: 標明是 不會發生改變的全局變量
函數:
1. 前帶_的變量: 標明是一個私有函數, 只用於標明,
2. 前帶兩個_ ,後帶兩個_ 的函數: 標明是特殊函數
參考文章:
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820042500060e2921830a4adf94fb31bcea8d6f5c000
http://blog.163.com/zhulp0372@yeah/blog/static/11589447920132541933516/
http://blog.csdn.net/debugm/article/details/8179482
"""
4)repr和str的區別
http://www.cnpythoner.com/post/251.html
5)_ _getitem__和__setitem__為特殊方法,做重新定義
用於做到某些get的屬性的校驗需求,參考示例:http://www.jb51.net/article/87447.htm
此處用於判斷:key是int型還是string型,不同的get邏輯
print(row[1])
print(row["name"])
6)重載 __getattr__ 和 __setattr__
來攔截對成員的訪問,需要注意的是 __getattr__ 只有在訪問不存在的成員時才會被調用
參考示例:http://www.360doc.com/content/14/0322/02/9482_362601063.shtml
print(row.name) //獲取屬性,仍然調用__getitem__
7)OrderedDict
""" OrderedDict提供了一個有序的字典結構,記錄了每個鍵值對添加的順序,
如果初始化的時候同時傳入多個參數,它們的順序是隨機的,不會按照位置順序存儲。"""
return OrderedDict(items) if ordered else dict(items)
8)@property裝飾器
負責把dataset方法變成屬性調用
9)__next__和__iter__
構造迭代器

測試demo



# -*- coding: utf-8 -*-
# import records
from source_code.records import records
from sqlalchemy import *
def demo():
a = 1
if a == 1:
raise IOError("SSS")
def demo1():
db = records.Database('mysql://root:root@localhost:3306/ceshi')
rows = db.query("select * from cece")
row = rows[0]
print(row.keys)
print(row.values)
print(row[1])
print(row["name"])
print("---")
print(row.as_dict())
def demo2():
mysql_engine = create_engine('mysql://root:root@localhost:3306/ceshi')
connection = mysql_engine.connect()
result = connection.execute("select * from cece")
print(result.keys())
if __name__ == '__main__':
demo1()
demo2()

‘,

About 尋夢園
尋夢園是台灣最大的聊天室及交友社群網站。 致力於發展能夠讓會員們彼此互動、盡情分享自我的平台。 擁有數百間不同的聊天室 ,讓您隨時隨地都能找到志同道合的好友!