(2-1)使用数据库保存数据:操作SQLite3数据库

数据持久化是指永久的将Python数据存储在磁盘上。数据持久化技术是实现动态软件项目的必须手段,在软件项目中通过数据持久化可以存储海量的数据。因为软件显示的内容是从数据库中读取的,所以开发者可以通过修改数据库内容而实现动态交互功能。在Python软件开发应用中,数据库在实现过程中起了一个中间媒介的作用。在本章的内容中,将向读者介绍Python数据库开发方面的核心知识,为读者步入本书后面知识的学习打下基础。

2.1  操作SQLite3数据库

从Python 3.x版本开始,在标准库中已经内置了sqlite3模块,可以支持SQLite3数据库的访问和相关的数据库操作。在需要操作SQLite3数据库数据时,只需在程序中导入sqlite3模块即可。

2.1.1  sqlite3模块介绍

通过使用sqlite3模块,可以满足开发者在 Python 程序中使用 SQLite数据库的需求。在sqlite3模块中包含如下所示的常量成员。

  1. sqlite3.version:该sqlite3模块的字符串形式的版本号,这不是 SQLite数据库的版本号。
  2. sqlite3.version_info:该sqlite3模块的整数元组形式的版本号,这不是SQLite数据库库的版本号。
  3. sqlite3.sqlite_version:运行时SQLite库的版本号,是一个字符串形式。
  4. sqlite3.sqlite_version_info:运行时SQLite数据库的版本号,是一个整数元组形式。
  5. sqlite3.PARSE_DECLTYPES:该常量用于connect()函数中的detect_types参数,设置它使得sqlite3模块解析每个返回列的声明的类型。将解析出声明类型的第一个单词,比如,"integer primary key"将解析出"integer",而"number(10)"将解析出"number"。
  6. sqlite3.PARSE_COLNAMES:该常量用于connect()函数中的detect_types参数,设置它使得SQLite接口解析每个返回列的列名。ga将查找[mytype]形式的字符串,然后决定'mytype'是列的类型。将会尝试在转换器字典中找到对应于'mytype'的转换器,然后将转换器函数应用于返回的值。在Cursor.description中找到的的列名只是列名的第一个单词,例如如果在SQL中有类似'as "x [datetime]"'的成员,那么第一个单词将会被解析成列名,直到有空格为止:列名只是简单的"x"。
  7. isolation_level:获取或设置当前隔离级别。None表示自动提交模式,或者可以是"DEFERRED"、"IMMEDIATE"或"EXCLUSIVE"之一。
  8. in_transaction:如果为True则表示处于活动状态(有未提交的更改)。

在sqlite3模块中包含如下所示的方法成员。

  1. sqlite3.connect(database [,timeout ,other optional arguments]):用于打开一个到 SQLite 数据库文件 database 的链接。可以使用 ":memory:" 在 RAM 中打开一个到 database 的数据库连接,而不是在磁盘上打开。如果数据库成功打开,则返回一个连接对象。当一个数据库被多个连接访问,且其中一个修改了数据库时,此时 SQLite 数据库将被锁定,直到事务提交。参数timeout表示连接等待锁定的持续时间,直到发生异常断开连接。参数timeout的默认是 5.0(5 秒)。如果给定的数据库名称 filename 不存在,则该调用将创建一个数据库。如果不想在当前目录中创建数据库,那么可以指定带有路径的文件名,这样就能在任意地方创建数据库。
  2. connection.cursor([cursorClass]):用于创建一个 cursor,将在 Python 数据库编程中用到。该方法接受一个单一的可选的参数 cursorClass。如果提供了该参数,则它必须是一个扩展自 sqlite3.Cursor 的自定义的 cursor 类。
  3. 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)
  1. connection.execute(sql [, optional parameters]):是上面执行的由光标(cursor)对象提供的方法的快捷方式,通过调用光标(cursor)方法创建了一个中间的光标对象,然后通过给定的参数调用光标的execute方法。
  2. 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',)]
  1. connection.executemany(sql[, parameters]):是一个由调用光标(cursor)方法创建的中间的光标对象的快捷方式,然后通过给定的参数调用光标的 executemany 方法。
  2. 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
    );
""")
  1. connection.executescript(sql_script):是一个由调用光标(cursor)方法创建的中间的光标对象的快捷方式,然后通过给定的参数调用光标的executescript方法。
  2. connection.total_changes():返回自数据库连接打开以来被修改、插入或删除的数据库总行数。
  3. connection.commit():用于提交当前的事务。如果未调用该方法,那么自上一次调用 commit() 以来所做的任何动作对其他数据库连接来说是不可见的。
  4. 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
  1. 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
  1. 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',)
  1. connection.interrupt():从另一个线程中调用该方法来中止该连接正在执行的查询,查询会中止,调用者会得到一个异常。
  2. connection.set_authorizer(authorizer_callback):用于注册一个回调。每当尝试访问数据库中表的列时会调用该回调,如果访问被访问,回调应该返回SQLITE_OK;如果SQL语句以错误中止,则回调应该返回SQLITE_DENY;如果列被当成NULL值,回调应该返回SQLITE_IGNORE。回调的第一个参数表示何种操作被授权。根据第一个参数,第二和第三个参数将提供或是None。第四个参数是数据库的名称(“main”,“temp”等)。第5个参数是最内部的触发器或视图的名字,它们负责访问请求;如果访问请求直接来自于输入的SQL代码则为None。
  3. connection.set_progress_handler(handler, n):用于注册一个回调。如果希望在长时间操作过程中从SQLite得到调用,这是非常有用的。如果希望在清除之前安装的过程处理器,则以None为handler参数调用该方法。
  4. connection.set_trace_callback(trace_callback):为SQLite后端实际执行的每个SQL语句调用寄存器trace_callback。传递给回调的唯一参数是正在执行的语句(作为字符串)。
  5. connection.enable_load_extension(enabled):允许或不允许 SQLite 引擎从共享库加载 SQLite 扩展。SQLite 扩展可以定义新的函数、聚合或全新的虚拟表实现。在默认情况下会禁用加载扩展。
  6. connection.load_extension(path):用于从一个共享库加载 SQLite 扩展。在使用该方法之前必须用enable_load_extension()来允许扩展加载,在默认情况下禁用加载扩展。
  7. connection.connection.rollback():用于回滚自上一次调用 commit() 以来对数据库所做的更改。
  8. connection.close():用于关闭数据库连接。在此需要注意,这不会自动调用 commit()。如果在之前未调用 commit()方法,就会直接关闭数据库连接,我们所做的所有更改将全部丢失。
  9. cursor.fetchone():用于获取查询结果集中的下一行,返回一个单一的序列,当没有更多可用的数据时则返回 None。
  10. cursor.fetchmany([size=cursor.arraysize]):用于获取查询结果集中的下一行组,返回一个列表。当没有更多的可用的行时,则返回一个空的列表。该方法尝试获取由参数size指定的尽可能多的行。
  11. cursor.fetchall():用于获取查询结果集中所有(剩余)的行,返回一个列表。当没有可用的行时,则返回一个空的列表。
  12. cursor.close():现在关闭光标(而不是每次调用__del__时),光标将从这一点向前不可用。如果使用光标进行任何操作,则会出现ProgrammingError异常。
  13. register_converter(typename, callable):功能是注册可调用对象,用来将来自数据库的字符串转换成为自定义的Python类型。在数据库中所有可支持的typename类型的值都可以调用该可调用对象。开发者在使用时需要注意,typename的大小写和查询中类型的名称必须匹配。
  14. 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.
  1. enable_callback_tracebacks(flag):在默认情况下不会在用户定义的函数、聚合、转换器、授权者回调等地方得到回溯对象(调用栈对象)。如果想要调试它们,需要将参数flag设置为True然后调用此方法,之后可以在sys.stderr通过回调得到回溯。将参数flag设置为False后可以再次禁用该功能。
  1. 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模块知道从数据库中实际选择的是一个点,这可以通过如下两种方法实现这个功能。

  1. 隐式地通过声明的类型
  2. 显式地通过列名

例如下面的实例文件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个数字,它的值将由时间戳转换器截断至微秒的精度。

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

感谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值