除了使用檔案、物件序列化來儲存程式運行結果之外,資料庫的應用也是常見用來儲存資料,以備後續查詢、載入等用途的一個選項,在這邊介紹一下 DBM 與 DB-API 2.0。
DBM
dbm
為柏克萊大學發展的檔案型資料庫,Python 的 dbm
模組提供了對 Unix 程式庫的介面。dbm
物件就像個字典,在不需要關聯式資料庫,只需要快速存取鍵值的場合可以使用,dbm
物件的鍵值都必須是字串。Python 提供 DBM 的多數實現,如果你不確定要用哪一種,可以使用 anydbm
模組,它會檢查並選擇系統上可用的 DBM 實作。
在這邊直接轉貼〈dbm — Interfaces to Unix “databases”〉中的範例程式碼作個示範:
import dbm
# Open database, creating it if necessary.
with dbm.open('cache', 'c') as db:
# Record some values
db[b'hello'] = b'there'
db['www.python.org'] = 'Python Website'
db['www.cnn.com'] = 'Cable News Network'
# Note that the keys are considered bytes now.
assert db[b'www.python.org'] == b'Python Website'
# Notice how the value is now in bytes.
assert db['www.cnn.com'] == b'Cable News Network'
# Often-used methods of the dict interface work too.
print(db.get('python.org', b'not present'))
# Storing a non-string key or value will raise an exception (most
# likely a TypeError).
db['www.yahoo.com'] = 4
# db is automatically closed when leaving the with statement.
在這邊你看到了 with
這個語法,如註解中說明的,它會自動關閉檔案,現在暫且忽略它,之後我們還會詳細看到 with
的介紹。
DB-API 2.0(PEP 249)
為 Python 中存取資料庫的標準介面,就我的認知而言,其角色應該是類似於 Java 中的 JDBC。Python 中的 sqlite3
模組,提供了 DB-API 2.0 的實作,可用以存取 SQLite 資料庫。接下來的範例,會存取的資料庫表格如下:
以下直接列出範例程式碼,程式很簡單,應該一目瞭然,API 細節可參考〈sqlite3 — DB-API 2.0 interface for SQLite databases〉。
def connect(name):
create = not os.path.exists(name)
conn = sqlite3.connect(name)
if create:
cursor = conn.cursor()
cursor.execute("CREATE TABLE directors ("
"id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, "
"name TEXT UNIQUE NOT NULL)")
cursor.execute("CREATE TABLE dvds ("
"id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, "
"title TEXT NOT NULL, "
"year INTEGER NOT NULL, "
"duration INTEGER NOT NULL, "
"director_id INTEGER NOT NULL, "
"FOREIGN KEY (director_id) REFERENCES directors)")
conn.commit()
return conn
def add_dvd(conn, title, year, duration, director):
director_id = get_and_set_director(conn, director)
cursor = conn.cursor()
cursor.execute("INSERT INTO dvds "
"(title, year, duration, director_id) "
"VALUES (?, ?, ?, ?)",
(title, year, duration, director_id))
conn.commit()
def get_and_set_director(conn, director):
director_id = get_director_id(conn, director)
if director_id is not None:
return director_id
cursor = conn.cursor()
cursor.execute("INSERT INTO directors (name) VALUES (?)",
(director,))
conn.commit()
return get_director_id(conn, director)
def get_director_id(conn, director):
cursor = conn.cursor()
cursor.execute("SELECT id FROM directors WHERE name=?",
(director,))
fields = cursor.fetchone()
return fields[0] if fields is not None else None
def all_dvds(conn):
cursor = conn.cursor()
sql = ("SELECT dvds.title, dvds.year, dvds.duration, "
"directors.name FROM dvds, directors "
"WHERE dvds.director_id = directors.id"
" ORDER BY dvds.title")
cursor.execute(sql)
return [(str(fields[0]), fields[1], fields[2], str(fields[3]))
for fields in cursor]
def all_directors(conn):
cursor = conn.cursor()
cursor.execute("SELECT name FROM directors ORDER BY name")
return [str(fields[0]) for fields in cursor]
以下是個存取資料庫的例子:
db_name = 'dvd_library.sqlite3'
conn = connect(db_name)
add_dvd(conn, 'Python Tutorial 2013', 2013, 1, 'Justin')
print(all_directors(conn))
print(all_dvds(conn))
練習 8:pickle、shelve 與 DB-API 2.0
在 Lab 檔案中有個 lab/exercises/exercise8,當中有 pickle_ex.py、shelve_ex.py、sql_ex.py 三個檔案,分別是先前介紹 pickle
、shelve
與 DB-API 2.0 時的三個程式範例,不過程式碼內容不完整,請任選你想要練習的對象,按加上頭列出的範例程式碼,就不齊全的部份補齊。