数据持久化是指永久的将Python数据存储在磁盘上。数据持久化技术是实现动态软件项目的必须手段,在软件项目中通过数据持久化可以存储海量的数据。因为软件显示的内容是从数据库中读取的,所以开发者可以通过修改数据库内容而实现动态交互功能。在Python软件开发应用中,数据库在实现过程中起了一个中间媒介的作用。在本章的内容中,将向读者介绍Python数据库开发方面的核心知识,为读者步入本书后面知识的学习打下基础。
2.1 操作SQLite3数据库
从Python 3.x版本开始,在标准库中已经内置了sqlite3模块,可以支持SQLite3数据库的访问和相关的数据库操作。在需要操作SQLite3数据库数据时,只需在程序中导入sqlite3模块即可。
2.1.1 sqlite3模块介绍
通过使用sqlite3模块,可以满足开发者在 Python 程序中使用 SQLite数据库的需求。在sqlite3模块中包含如下所示的常量成员。
- sqlite3.version:该sqlite3模块的字符串形式的版本号,这不是 SQLite数据库的版本号。
- sqlite3.version_info:该sqlite3模块的整数元组形式的版本号,这不是SQLite数据库库的版本号。
- sqlite3.sqlite_version:运行时SQLite库的版本号,是一个字符串形式。
- sqlite3.sqlite_version_info:运行时SQLite数据库的版本号,是一个整数元组形式。
- sqlite3.PARSE_DECLTYPES:该常量用于connect()函数中的detect_types参数,设置它使得sqlite3模块解析每个返回列的声明的类型。将解析出声明类型的第一个单词,比如,"integer primary key"将解析出"integer",而"number(10)"将解析出"number"。
- sqlite3.PARSE_COLNAMES:该常量用于connect()函数中的detect_types参数,设置它使得SQLite接口解析每个返回列的列名。ga将查找[mytype]形式的字符串,然后决定'mytype'是列的类型。将会尝试在转换器字典中找到对应于'mytype'的转换器,然后将转换器函数应用于返回的值。在Cursor.description中找到的的列名只是列名的第一个单词,例如如果在SQL中有类似'as "x [datetime]"'的成员,那么第一个单词将会被解析成列名,直到有空格为止:列名只是简单的"x"。
- isolation_level:获取或设置当前隔离级别。None表示自动提交模式,或者可以是"DEFERRED"、"IMMEDIATE"或"EXCLUSIVE"之一。
- in_transaction:如果为True则表示处于活动状态(有未提交的更改)。
在sqlite3模块中包含如下所示的方法成员。
- sqlite3.connect(database [,timeout ,other optional arguments]):用于打开一个到 SQLite 数据库文件 database 的链接。可以使用 ":memory:" 在 RAM 中打开一个到 database 的数据库连接,而不是在磁盘上打开。如果数据库成功打开,则返回一个连接对象。当一个数据库被多个连接访问,且其中一个修改了数据库时,此时 SQLite 数据库将被锁定,直到事务提交。参数timeout表示连接等待锁定的持续时间,直到发生异常断开连接。参数timeout的默认是 5.0(5 秒)。如果给定的数据库名称 filename 不存在,则该调用将创建一个数据库。如果不想在当前目录中创建数据库,那么可以指定带有路径的文件名,这样就能在任意地方创建数据库。
- connection.cursor([cursorClass]):用于创建一个 cursor,将在 Python 数据库编程中用到。该方法接受一个单一的可选的参数 cursorClass。如果提供了该参数,则它必须是一个扩展自 sqlite3.Cursor 的自定义的 cursor 类。
- cursor.execute(sql [, optional parameters]):用于执行一个 SQL 语句。该 SQL 语句可以被参数化(即使用占位符代替 SQL 文本)。sqlite3 模块支持两种类型的占位符:问号和命名占位符(命名样式)。例如:
cursor.execute("insert into people values (?, ?)", (who, age))
例如在下面的实例文件e.py中,演示了使用方法cursor.execute()执行指定SQL语句的过程。
源码路径:codes\2\2-1\e.py
import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table people (name_last, age)")
who = "Yeltsin"
age = 72
# This is the qmark style:
cur.execute("insert into people values (?, ?)", (who, age))
# And this is the named style:
cur.execute("select * from people where name_last=:who and age=:age", {"who": who, "age": age})
print(cur.fetchone())
执行后会输出:
('Yeltsin', 72)
- connection.execute(sql [, optional parameters]):是上面执行的由光标(cursor)对象提供的方法的快捷方式,通过调用光标(cursor)方法创建了一个中间的光标对象,然后通过给定的参数调用光标的execute方法。
- cursor.executemany(sql, seq_of_parameters):用于对 seq_of_parameters 中的所有参数或映射执行一个 SQL 命令。例如在下面的实例文件f.py中,演示了使用方法cursor.executemany()执行指定SQL命令的过程。
源码路径:codes\2\2-1\f.py
import sqlite3
class IterChars:
def __init__(self):
self.count = ord('a')
def __iter__(self):
return self
def __next__(self):
if self.count > ord('z'):
raise StopIteration
self.count += 1
return (chr(self.count - 1),) # this is a 1-tuple
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.execute("create table characters(c)")
theIter = IterChars()
cur.executemany("insert into characters(c) values (?)", theIter)
cur.execute("select c from characters")
print(cur.fetchall())
执行后会输出:
[('a',), ('b',), ('c',), ('d',), ('e',), ('f',), ('g',), ('h',), ('i',), ('j',), ('k',), ('l',), ('m',), ('n',), ('o',), ('p',), ('q',), ('r',), ('s',), ('t',), ('u',), ('v',), ('w',), ('x',), ('y',), ('z',)]
- connection.executemany(sql[, parameters]):是一个由调用光标(cursor)方法创建的中间的光标对象的快捷方式,然后通过给定的参数调用光标的 executemany 方法。
- cursor.executescript(sql_script):一旦接收到脚本就会执行多个 SQL 语句。首先执行 COMMIT 语句,然后执行作为参数传入的 SQL 脚本。所有的 SQL 语句应该用分号“;”分隔。例如在下面的实例文件g.py中,演示了使用方法cursor.executescript ()执行多个 SQL 语句的过程。
源码路径:codes\2\2-1\g.py
import sqlite3
con = sqlite3.connect(":memory:")
cur = con.cursor()
cur.executescript("""
create table person(
firstname,
lastname,
age
);
create table book(
title,
author,
published
);
insert into book(title, author, published)
values (
'Dirk Gently''s Holistic Detective Agency',
'Douglas Adams',
1987
);
""")
- connection.executescript(sql_script):是一个由调用光标(cursor)方法创建的中间的光标对象的快捷方式,然后通过给定的参数调用光标的executescript方法。
- connection.total_changes():返回自数据库连接打开以来被修改、插入或删除的数据库总行数。
- connection.commit():用于提交当前的事务。如果未调用该方法,那么自上一次调用 commit() 以来所做的任何动作对其他数据库连接来说是不可见的。
- connection.create_function(name, num_params, func):用于创建一个自定义的函数,随后可以在SQL语句中以函数名name来调用它。参数num_params表示此方法接受的参数数量(如果num_params为-1,函数可以取任意数量的参数),参数func是一个可以被调用的SQL函数。例如在下面的实例文件b.py中,演示了使用方法create_function()执行指定函数的过程。
源码路径:codes\2\2-1\b.py
import sqlite3
import hashlib
def md5sum(t):
return hashlib.md5(t).hexdigest()
con = sqlite3.connect(":memory:")
con.create_function("md5", 1, md5sum)
cur = con.cursor()
cur.execute("select md5(?)", (b"foo",))
print(cur.fetchone()[0])
执行后会输出:
acbd18db4cc2f85cedef654fccc4a4d8
- connection.create_aggregate(name, num_params, aggregate_class):用于创建一个用户定义的聚合函数。聚合类必须实现step方法,参数num_params(如果num_params为-1,的参数)表示该方法可以接受参数的数量。参数也可以是finalize方法,表示可以返回SQLite支持的任何类型,例如bytes、str、int、float和None。例如在下面的实例文件c.py中,演示了使用方法create_aggregate()创建用户定义的聚合函数的过程。
源码路径:codes\2\2-1\c.py
import sqlite3
class MySum:
def __init__(self):
self.count = 0
def step(self, value):
self.count += value
def finalize(self):
return self.count
con = sqlite3.connect(":memory:")
con.create_aggregate("mysum", 1, MySum)
cur = con.cursor()
cur.execute("create table test(i)")
cur.execute("insert into test(i) values (1)")
cur.execute("insert into test(i) values (2)")
cur.execute("select mysum(i) from test")
print(cur.fetchone()[0])
执行后会输出:
3
- connection.create_collation(name, callable):功能是用指定的name和callable创建一个排序规则,会传递两个字符串参数给可调用对象。如果第一个比第二个小则返回-1,如果相等则返回0,如果第一个比第二个大则返回1。需要注意可调用对象将会以Python bytestring的方式得到它的参数,一般为UTF-8编码。例如在下面的实例文件d.py中,演示了使用方法create_collation()用自定义排序规则以"错误方式"进行排序的过程。
源码路径:codes\2\2-1\d.py
import sqlite3
def collate_reverse(string1, string2):
if string1 == string2:
return 0
elif string1 < string2:
return 1
else:
return -1
con = sqlite3.connect(":memory:")
con.create_collation("reverse", collate_reverse)
cur = con.cursor()
cur.execute("create table test(x)")
cur.executemany("insert into test(x) values (?)", [("a",), ("b",)])
cur.execute("select x from test order by x collate reverse")
for row in cur:
print(row)
con.close()
执行后会输出:
('b',)
('a',)
- connection.interrupt():从另一个线程中调用该方法来中止该连接正在执行的查询,查询会中止,调用者会得到一个异常。
- connection.set_authorizer(authorizer_callback):用于注册一个回调。每当尝试访问数据库中表的列时会调用该回调,如果访问被访问,回调应该返回SQLITE_OK;如果SQL语句以错误中止,则回调应该返回SQLITE_DENY;如果列被当成NULL值,回调应该返回SQLITE_IGNORE。回调的第一个参数表示何种操作被授权。根据第一个参数,第二和第三个参数将提供或是None。第四个参数是数据库的名称(“main”,“temp”等)。第5个参数是最内部的触发器或视图的名字,它们负责访问请求;如果访问请求直接来自于输入的SQL代码则为None。
- connection.set_progress_handler(handler, n):用于注册一个回调。如果希望在长时间操作过程中从SQLite得到调用,这是非常有用的。如果希望在清除之前安装的过程处理器,则以None为handler参数调用该方法。
- connection.set_trace_callback(trace_callback):为SQLite后端实际执行的每个SQL语句调用寄存器trace_callback。传递给回调的唯一参数是正在执行的语句(作为字符串)。
- connection.enable_load_extension(enabled):允许或不允许 SQLite 引擎从共享库加载 SQLite 扩展。SQLite 扩展可以定义新的函数、聚合或全新的虚拟表实现。在默认情况下会禁用加载扩展。
- connection.load_extension(path):用于从一个共享库加载 SQLite 扩展。在使用该方法之前必须用enable_load_extension()来允许扩展加载,在默认情况下禁用加载扩展。
- connection.connection.rollback():用于回滚自上一次调用 commit() 以来对数据库所做的更改。
- connection.close():用于关闭数据库连接。在此需要注意,这不会自动调用 commit()。如果在之前未调用 commit()方法,就会直接关闭数据库连接,我们所做的所有更改将全部丢失。
- cursor.fetchone():用于获取查询结果集中的下一行,返回一个单一的序列,当没有更多可用的数据时则返回 None。
- cursor.fetchmany([size=cursor.arraysize]):用于获取查询结果集中的下一行组,返回一个列表。当没有更多的可用的行时,则返回一个空的列表。该方法尝试获取由参数size指定的尽可能多的行。
- cursor.fetchall():用于获取查询结果集中所有(剩余)的行,返回一个列表。当没有可用的行时,则返回一个空的列表。
- cursor.close():现在关闭光标(而不是每次调用__del__时),光标将从这一点向前不可用。如果使用光标进行任何操作,则会出现ProgrammingError异常。
- register_converter(typename, callable):功能是注册可调用对象,用来将来自数据库的字符串转换成为自定义的Python类型。在数据库中所有可支持的typename类型的值都可以调用该可调用对象。开发者在使用时需要注意,typename的大小写和查询中类型的名称必须匹配。
- complete_statement(sql):如果字符串sql包含一个或多个以分号结束的完整的SQL语句则返回True。不会验证SQL的语法正确性,只是检查没有未关闭的字符串常量以及语句是以分号结束。例如在下面的实例文件a.py中,演示了使用方法complete_statement(sql)生成一个 sqlite shell的过程。
源码路径:codes\2\2-1\a.py
import sqlite3
con = sqlite3.connect(":memory:")
con.isolation_level = None
cur = con.cursor()
buffer = ""
print("Enter your SQL commands to execute in sqlite3.")
print("Enter a blank line to exit.")
while True:
line = input()
if line == "":
break
buffer += line
if sqlite3.complete_statement(buffer):
try:
buffer = buffer.strip()
cur.execute(buffer)
if buffer.lstrip().upper().startswith("SELECT"):
print(cur.fetchall())
except sqlite3.Error as e:
print("An error occurred:", e.args[0])
buffer = ""
con.close()
执行后会输出:
Enter your SQL commands to execute in sqlite3.
Enter a blank line to exit.
- enable_callback_tracebacks(flag):在默认情况下不会在用户定义的函数、聚合、转换器、授权者回调等地方得到回溯对象(调用栈对象)。如果想要调试它们,需要将参数flag设置为True然后调用此方法,之后可以在sys.stderr通过回调得到回溯。将参数flag设置为False后可以再次禁用该功能。
- Row.keys():用于返回列名称的列表。在查询后,它是在Cursor.description中每个元组的第一个成员。例如在下面的实例文件h.py中,演示了使用Row对象和keys()方法的过程。
源码路径:codes\2\2-1\h.py
import sqlite3
conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute('''create table stocks
(date text, trans text, symbol text,
qty real, price real)''')
c.execute("""insert into stocks
values ('2018-01-05','BUY','RHAT',100,35.14)""")
conn.commit()
c.close()
conn.row_factory = sqlite3.Row
c = conn.cursor()
print(c.execute('select * from stocks'))
r = c.fetchone()
print(type(r))
print(tuple(r))
print(len(r))
print(r[2])
print(r.keys())
print(r['qty'])
for member in r:
print(member)
执行后会输出:
<sqlite3.Cursor object at 0x0000021B3B291F10>
<class 'sqlite3.Row'>
('2018-01-05', 'BUY', 'RHAT', 100.0, 35.14)
5
RHAT
['date', 'trans', 'symbol', 'qty', 'price']
100.0
2018-01-05
BUY
RHAT
100.0
35.14
2.1.2 使用sqlite3模块操作SQLite3数据库
根据DB-API 2.0规范规定,Python语言操作SQLite3数据库的基本流程如下所示。
(1)导入相关库或模块( sqlite3)。
(2)使用connect()连接数据库并获取数据库连接对象。
(3)使用con.cursor()获取游标对象。
(4)使用游标对象的方法( execute()、executemany()、fetchall()等)来操作数据库,实现插入、修改和删除操作,并查询获取显示相关的记录。在Python程序中,连接函数sqlite3.connect()有如下两个常用参数。
q database:表示要访问的数据库名;
q timeout:表示访问数据的超时设定。
其中,参数database表示用字符串的形式指定数据库的名称,如果数据库文件位置不是当前目录,则必须要写出其相对或绝对路径。还可以用“:memory:”表示使用临时放入内存的数据库。当退出程序时,数据库中的数据也就不存在了。
(5)使用close()关闭游标对象和数据库连接。数据库操作完成之后,必须及时调用其close()方法关闭数据库连接,这样做的目的是减轻数据库服务器的压力。
例如在下面的实例文件sqlite.py中,演示了使用sqlite3模块操作SQLite3数据库的过程。
源码路径:codes\2\2-1\sqlite.py
import sqlite3 #导入内置模块
import random #导入内置模块
#初始化变量src,设置用于随机生成字符串中的所有字符
src = 'abcdefghijklmnopqrstuvwxyz'
def get_str(x,y): #生成字符串函数get_str()
str_sum = random.randint(x,y)#生成x和y之间的随机整数
astr = '' #变量astr赋值
for i in range(str_sum): #遍历随机数
astr += random.choice(src)#累计求和生成的随机数
return astr #返回和
def output(): #函数output()用于输出数据库表中的所有信息
cur.execute('select * from biao')#查询表biao中的所有信息
for sid,name,ps in cur: #查询表中的3个字段sid、name和ps
print(sid,' ',name,' ',ps) #显示3个字段的查询结果
def output_all(): #函数output_all()用于输出数据库表中的所有信息
cur.execute('select * from biao')#查询表biao中的所有信息
for item in cur.fetchall(): #获取查询到的所有数据
print(item) #打印显示获取到的数据
def get_data_list(n): #函数get_data_list()用于生成查询列表
res = [] #列表初始化
for i in range(n): #遍历列表
res.append((get_str(2,4),get_str(8,12)))#生成列表
return res #返回生成的列表
if __name__ == '__main__':
print("建立连接...") #打印提示
con = sqlite3.connect(':memory:') #开始建立和数据库的连接
print("建立游标...")
cur = con.cursor() #获取游标
print('创建一张表biao...') #打印提示信息
#在数据库中创建表biao,设置了表中的各个字段
cur.execute('create table biao(id integer primary key autoincrement not null,name text,passwd text)')
print('插入一条记录...') #打印提示信息
#插入1条数据信息
cur.execute('insert into biao (name,passwd)values(?,?)',(get_str(2,4),get_str(8,12),))
print('显示所有记录...') #打印提示信息
output() #显示数据库中的数据信息
print('批量插入多条记录...') #打印提示信息
#插入多条数据信息
cur.executemany('insert into biao (name,passwd)values(?,?)',get_data_list(3))
print("显示所有记录...") #打印提示信息
output_all() #显示数据库中的数据信息
print('更新一条记录...') #打印提示信息
#修改表biao中的一条信息
cur.execute('update biao set name=? where id=?',('aaa',1))
print('显示所有记录...') #打印提示信息
output() #显示数据库中的数据信息
print('删除一条记录...') #打印提示信息
#删除表biao中的一条数据信息
cur.execute('delete from biao where id=?',(3,))
print('显示所有记录:') #打印提示信息
output() #显示数据库中的数据信息
在上述实例代码中,首先定义了两个能够生成随机字符串的函数,生成的随机字符串作为数据库中存储的数据。然后定义output()和output-all()方法,功能是分别通过遍历cursor、调用cursor的方式来获取数据库表中的所有记录并输出。然后在主程序中,依次通过建立连接,获取连接的cursor,通过cursor的execute()和executemany()等方法来执行SQL语句,以实现插入一条记录、插入多条记录、更新记录和删除记录的功能。最后依次关闭游标和数据库连接。执行后会输出:
建立连接...
建立游标...
创建一张表biao...
插入一条记录...
显示所有记录...
1 bld zbynubfxt
批量插入多条记录...
显示所有记录...
(1, 'bld', 'zbynubfxt')
(2, 'owd', 'lqpperrey')
(3, 'vc', 'fqrbarwsotra')
(4, 'yqk', 'oyzarvrv')
更新一条记录...
显示所有记录...
1 aaa zbynubfxt
2 owd lqpperrey
3 vc fqrbarwsotra
4 yqk oyzarvrv
删除一条记录...
显示所有记录:
1 aaa zbynubfxt
2 owd lqpperrey
4 yqk oyzarvrv
2.1.3 SQLite和Python的类型
SQLite可以支持的类型有:NULL、INTEGER、REAL、TEXT和BLOB,所以下表2-1中的Python类型可以直接发送给SQLite。
表2-1 SQLite可以直接使用的Python的类型
Python类型 | SQLite类型 |
None | NULL |
int | INTEGER |
float | REAL |
str | TEXT |
bytes | BLOB |
在默认情况下,SQLite将表2-2中的类型转换成Python类型。
表2-2 转换成Python类型
SQLite类型 | Python类型 |
NULL | None |
INTEGER | int |
REAL | float |
TEXT | 在默认情况下取决于text_factory和str |
BLOB | bytes |
在SQLite处理Python数据的过程中,有可能需要处理其他更多种类型的数据,而这些数据类型SQLite并不支持,此时需要用到类型扩展技术来实现我们的功能。在Python语言的sqlite3模块中,其类型系统可以用两种方式来扩展数据类型:通过对象适配,可以在SQLite数据库中存储其它的Python类型,通过转换器让sqlite3模块将SQLite类型转成不同的Python类型。
1. 使用适配器来存储额外的Python类型的SQLite数据库
因为SQLite只支默认持有限的类,要使用其他Python类型与SQLite进行交互,就必须适应它们为sqlite3模块支持的SQLite类型之一:NoneType、int、float、str或bytes。通过使用如下所示的两种方法,可以使sqlite3模块适配一个Python类型到一个支持的类型。
(1)编写类进行适应
开发者可以编写一个自定义类,假设编写了如下所示的一个类:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
想要在某个SQLite 列中存储类Point,首先得选择一个支持的类型,这个类型可以用来表示Point。假定使用str,并用分号来分隔坐标。需要给类加一个__conform__(self, protocl)方法,该方法必须返回转换后的值。参数protocol为PrepareProtocol类型。例如在下面的实例文件i.py中,演示了将自定义类Point适配SQLite3数据库的过程。
源码路径:codes\2\2-1\i.py
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def adapt_point(point):
return "%f;%f" % (point.x, point.y)
sqlite3.register_adapter(Point, adapt_point)
con = sqlite3.connect(":memory:")
cur = con.cursor()
p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print(cur.fetchone()[0])
执行后会输出:
4.000000;-3.200000
(2)注册可调用的适配器
例如有一种可能性是创建一个函数,用来将类型转成字符串表现形式,然后使用函数register_adapter()来注册该函数。例如在下面的实例文件j.py中,演示了使用函数register_adapter()注册适配器函数的过程。
源码路径:codes\2\2-1\j.py
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def adapt_point(point):
return "%f;%f" % (point.x, point.y)
sqlite3.register_adapter(Point, adapt_point)
con = sqlite3.connect(":memory:")
cur = con.cursor()
p = Point(4.0, -3.2)
cur.execute("select ?", (p,))
print(cur.fetchone()[0])
执行后会输出:
4.000000;-3.200000
在sqlite3模块中,为 Python内置的datetime.date和datetime.datetime类型设置了两个默认适配器。假设想要将datetime.datetime对象不以ISO形式存储,而是存成Unix时间戳,则可以通过下面的实例文件k.py实现。
源码路径:codes\2\2-1\k.py
import sqlite3
import datetime
import time
def adapt_datetime(ts):
return time.mktime(ts.timetuple())
sqlite3.register_adapter(datetime.datetime, adapt_datetime)
con = sqlite3.connect(":memory:")
cur = con.cursor()
now = datetime.datetime.now()
cur.execute("select ?", (now,))
print(cur.fetchone()[0])
执行后会输出:
1513239416.0
2. 将自定义Python类型转成SQLite类型
在Python程序中,可以编写适配器将自定义Python类型转成SQLite类型。再次以前面的Point类进行举例,假设在SQLite中以字符串的形式存储以分号分隔的x、y坐标。我们可以先定义如下所示的转换器函数convert_point(),用于接收字符串参数,并从中构造一个Point对象。转换器函数总是使用bytes对象调用,无论将数据类型发送到SQLite的哪种数据类型。
def convert_point(s):
x, y = map(float, s.split(b";"))
return Point(x, y)
接下来需要让sqlite3模块知道从数据库中实际选择的是一个点,这可以通过如下两种方法实现这个功能。
- 隐式地通过声明的类型
- 显式地通过列名
例如下面的实例文件l.py演示了上述两种方法的实现过程。
源码路径:codes\2\2-1\l.py
import sqlite3
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return "(%f;%f)" % (self.x, self.y)
def adapt_point(point):
return ("%f;%f" % (point.x, point.y)).encode('ascii')
def convert_point(s):
x, y = list(map(float, s.split(b";")))
return Point(x, y)
# Register the adapter
sqlite3.register_adapter(Point, adapt_point)
# Register the converter
sqlite3.register_converter("point", convert_point)
p = Point(4.0, -3.2)
#########################
# 1) Using declared types
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.cursor()
cur.execute("create table test(p point)")
cur.execute("insert into test(p) values (?)", (p,))
cur.execute("select p from test")
print("with declared types:", cur.fetchone()[0])
cur.close()
con.close()
#######################
# 1) Using column names
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(p)")
cur.execute("insert into test(p) values (?)", (p,))
cur.execute('select p as "p [point]" from test')
print("with column names:", cur.fetchone()[0])
cur.close()
con.close()
执行后会输出:
with declared types: (4.000000;-3.200000)
with column names: (4.000000;-3.200000)
3. 默认适配器和转换器
在Python语言的datetime模块中,有对date和datetime类型的默认的适配器,将ISO dates/ISO timestamps发送给SQLite。默认的转换器以"date"为名注册给datetime.date,以"timestamp"为名注册给datetime.datetime。这样在大多数情况下,可以在Python中使用date/timestamp时不需要额外的动作,适配器的格式兼容于SQLite的date/time函数。
例如在下面的实例文件m.py中,演示了使用默认适配器和转换器的过程。
源码路径:codes\2\2-1\m.py
import sqlite3
import datetime
con = sqlite3.connect(":memory:", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cur = con.cursor()
cur.execute("create table test(d date, ts timestamp)")
today = datetime.date.today()
now = datetime.datetime.now()
cur.execute("insert into test(d, ts) values (?, ?)", (today, now))
cur.execute("select d, ts from test")
row = cur.fetchone()
print(today, "=>", row[0], type(row[0]))
print(now, "=>", row[1], type(row[1]))
cur.execute('select current_date as "d [date]", current_timestamp as "ts [timestamp]"')
row = cur.fetchone()
print("current_date", row[0], type(row[0]))
print("current_timestamp", row[1], type(row[1]))
执行后会输出:
2017-12-14 => 2017-12-14 <class 'datetime.date'>
2017-12-14 16:37:55.125396 => 2017-12-14 16:37:55.125396 <class 'datetime.datetime'>
current_date 2017-12-14 <class 'datetime.date'>
current_timestamp 2017-12-14 08:37:55 <class 'datetime.datetime'>
注意:如果存储在SQLite中的时间戳的小数部分大于6个数字,它的值将由时间戳转换器截断至微秒的精度。