Die*_*4DD 1 python sqlite python-2.7
我有一个用 Python 2.7 编写的项目,其中主程序需要频繁访问 sqlite3 数据库以写入日志、测量结果、获取设置等。
目前我有一个 db 模块,其中包含 add_log()、get_setting() 等函数,其中的每个函数基本上如下所示:
def add_log(logtext):
try:
db = sqlite3.connect(database_location)
except sqlite3.DatabaseError as e:
db.close() # try to gracefully close the db
return("ERROR (ADD_LOG): While opening db: {}".format(e))
try:
with db: # using context manager to automatically commit or roll back changes.
# when using the context manager, the execute function of the db should be used instead of the cursor
db.execute("insert into logs(level, source, log) values (?, ?, ?)", (level, source, logtext))
except sqlite3.DatabaseError as e:
return("ERROR (ADD_LOG): While adding log to db: {}".format(e))
return "OK"
Run Code Online (Sandbox Code Playgroud)
(删除了一些附加代码和注释)。
看来我应该编写一个类扩展基本 sqlite 连接对象函数,以便仅创建连接一次(在主程序开始时),然后该对象包含以下功能
class Db(sqlite3.Connection):
def __init__(self, db_location = database_location):
try:
self = sqlite3.connect(db_location)
return self
except sqlite3.DatabaseError as e:
self.close() # try to gracefully close the db
def add_log(self, logtext):
self.execute("insert into logs(level, source, log) values (?, ?, ?)", (level, source, logtext))
Run Code Online (Sandbox Code Playgroud)
看起来这应该相当简单,但是我似乎无法让它发挥作用。
这里似乎有一些有用的建议: Python:如何成功继承 Sqlite3.Cursor 并添加我的自定义方法,但我似乎无法理解如何使用类似的构造来达到我的目的。
你并不遥远。
\n\n首先,类初始值设定项不能返回任何内容,但None(强调我的):
\n\n\n因为
\n__new__()和__init__()一起构造对象(__new__()创建它并__init__()自定义它),所以;不会返回非值。这样做会导致运行时引发 a 。None__init__()TypeError
其次,用初始化器中的对象覆盖对象self的当前实例。这使得 SQLite 连接对象的子类化变得毫无意义。Dbsqlite3.Connection
您只需要修复您的__init__方法即可使其工作:
class Db(sqlite3.Connection):\n\n # If you didn\'t use the default argument, you could omit overriding __init__ alltogether\n def __init__(self, database=database_location, **kwargs):\n super(Db, self).__init__(database=database, **kwargs)\n\n def add_log(self, logtext, level, source):\n self.execute("insert into logs(level, source, log) values (?, ?, ?)", (level, source, logtext))\nRun Code Online (Sandbox Code Playgroud)\n\n这允许您使用类的实例作为上下文管理器:
\n\nwith Db() as db:\n print [i for i in db.execute("SELECT * FROM logs")]\n db.add_log("I LAUNCHED THAT PUG INTO SPACE!", 42, "Right there")\nRun Code Online (Sandbox Code Playgroud)\n\nMaurice Meyer 在问题的评论中表示,诸如此类的方法execute()是游标方法,并且根据DB-API 2.0规范,这是正确的。
\n但是,sqlite3\ 的连接对象提供了一些游标方法的快捷方式:
\n\n\n这是一个非标准的快捷方式,它通过调用cursor方法创建一个中间光标对象,然后使用
\nexecute给定的参数调用cursor\xe2\x80\x99s方法。
扩展注释中的讨论:
\n上面代码示例中关于默认参数的注释是针对覆盖sqlite3.Connection\ 的__init__方法的要求。
类__init__中的Db只需要为初始化器的参数database_location定义默认值。\n如果您愿意在该类的每次实例化时传递这样的值,您的自定义连接类可能如下所示,并且仍然以相同的方式工作,除了该参数:databasesqlite3.Connection
class Db(sqlite3.Connection):\n\n def add_log(self, logtext, level, source):\n self.execute("insert into logs(level, source, log) values (?, ?, ?)", (level, source, logtext))\nRun Code Online (Sandbox Code Playgroud)\n\n然而,该方法与PEP 343__init__中定义的上下文管理器协议无关。
当涉及到类时,该协议需要实现魔术方法__enter__和__exit__
其sqlite3.Connection做法如下:
class Connection:\n\n def __enter__(self):\n return self\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n if exc_val is None:\n self.commit()\n else:\n self.rollback()\nRun Code Online (Sandbox Code Playgroud)\n\n注意:sqlite3.Connection由 C 模块提供,因此没有 Python 类定义。上面反映了如果这样做的话,这些方法大致会是什么样子。
假设您不想始终保持同一个连接打开,而是希望每个事务都有一个专用连接,同时维护上述类的通用接口Db。
\n你可以这样做:
# Keep this to have your custom methods available\nclass Connection(sqlite3.Connection):\n\n def add_log(self, level, source, log):\n self.execute("INSERT INTO logs(level, source, log) VALUES (?, ?, ?)", \n (level, source, log))\n\n\nclass DBM:\n\n def __init__(self, database=database_location):\n self._database = database\n self._conn = None\n\n def __enter__(self):\n return self._connection()\n\n def __exit__(self, exc_type, exc_val, exc_tb):\n # Decide whether to commit or roll back\n if exc_val:\n self._connection().rollback()\n else:\n self._connection().commit()\n # close connection\n try:\n self._conn.close()\n except AttributeError:\n pass\n finally:\n self._conn = None\n\n def _connection(self):\n if self._conn is None:\n # Instantiate your custom sqlite3.Connection\n self._conn = Connection(self._database)\n return self._conn\n\n # add shortcuts to connection methods as seen fit\n def execute(self, sql, parameters=()):\n with self as temp:\n result = temp.execute(sql, parameters).fetchall()\n return result\n\n def add_log(self, level, source, log):\n with self as temp:\n temp.add_log(level, source, log)\nRun Code Online (Sandbox Code Playgroud)\n\n这可以在上下文中并通过调用实例上的方法来使用:
\n\ndb = DBM(database_location)\n\nwith db as temp:\n print [i for i in temp.execute("SELECT * FROM logs")]\n temp.add_log(1, "foo", "I MADE MASHED POTATOES")\n\n# The methods execute and add_log are only available from\n# the outside because the shortcuts have been added to DBM\nprint [i for i in db.execute("SELECT * FROM logs")]\ndb.add_log(1, "foo", "I MADE MASHED POTATOES")\nRun Code Online (Sandbox Code Playgroud)\n\n有关上下文管理器的更多阅读,请参阅官方文档。我还会推荐Jeff Knupp 的精彩介绍。此外,上述PEP 343值得一看,了解该协议背后的技术规范和基本原理。
\n