如何在使用每个功能连接到数据库时保持干爽?
How to stay dry while connecting to database with every fucntion?
我正在使用 discordpy 机器人,我在每个函数中连接和断开与数据库的连接,我认为这不是一个好方法,但我尝试使用装饰器连接和断开与数据库的连接,并将光标发送为函数的一个参数。
这是我的代码:
def connectToDB(func):
async def wrapper(*args, **kwargs):
db = sqlite3.connect(DB_DIR)
cursor = db.cursor()
print(*args, **kwargs)
res = await func(*args, **kwargs, cursor=cursor)
db.commit()
cursor.close()
db.close()
return res
return wrapper
class MusicBot(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(name="myplaylist", aliases=['mypl'])
@connectToDB
async def myPlaylist(self, ctx, name=None, currentPage=0, cursor=None):
currentPage = int(currentPage)
if name:
await self.getPlaylistByName(ctx, name, cursor)
return
result = cursor.execute(f"SELECT playlist_name, playlist_items, playlist_length, date"
f" FROM PLAYLIST WHERE user = ?", (str(ctx.author),))
playlists = result.fetchall()
embed = getPlaylistsEmbed(ctx, playlists, currentPage)
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(MusicBot(bot))
但这并没有优化代码,因为 discordpy 必须知道你想要命令的参数是什么,所以在 wrapper 中我已经说过 def wrapper(*args, **kwargs):
没有指定任何参数所以 discordpy 假设我只需要默认参数。
为了解决这个问题,我尝试过:
- 使用 exec 更改每个 运行 所需的参数,并检查以获取参数,如下所示:
def connectToDB(f):
global func
func = f
params = inspect.signature(func)
exec(
f"async def wrapper{str(params)}:"
" db = sqlite3.connect(DB_DIR)"
" cursor = db.cursor()"
" print(*args, **kwargs)"
f" res = await func{str(params)}"
" db.commit()"
" cursor.close()"
" db.close()"
" return res", globals()
)
return wrapper
这是行不通的,因为在每个命令的 运行 结束后它将保留其最后的更改。
2. 像这样分配装饰器
@connectToDB
@commands.command(name="myplaylist", aliases=['mypl'])
3. 这个很蠢。制作装饰器工厂以获取参数
def connectToDB(*args, **kwargs):
print(args, kwargs)
def decorator(func):
async def wrapper(*_args, **_kwargs):
db = sqlite3.connect(DB_DIR)
cursor = db.cursor()
print(*_args, **_kwargs)
res = await func(*_args, **_kwargs, cursor=cursor)
db.commit()
cursor.close()
db.close()
return res
return wrapper
return decorator
@commands.command(name="myplaylist", aliases=['mypl'])
@connectToDB(self=None, ctx=None, name=None, page=0, msg=None, cursor=None)
async def myPlaylist(self, ctx, name=None, page=0, msg=None, cursor=None):
...
当尝试将装饰器与依赖于内省函数的库一起使用时,这个问题经常发生。解决方案是 functools.wraps()
.
def connectToDB(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
# setup database...
res = await func(*args, **kwargs, cursor=cursor)
# teardown database...
return res
return wrapper
functools.wraps
调用 functools.update_wrapper
,这将更新“包装函数使其看起来像包装函数”,复制元数据以便内省工具报告原始函数的属性,而不是包装器。
我正在使用 discordpy 机器人,我在每个函数中连接和断开与数据库的连接,我认为这不是一个好方法,但我尝试使用装饰器连接和断开与数据库的连接,并将光标发送为函数的一个参数。
这是我的代码:
def connectToDB(func):
async def wrapper(*args, **kwargs):
db = sqlite3.connect(DB_DIR)
cursor = db.cursor()
print(*args, **kwargs)
res = await func(*args, **kwargs, cursor=cursor)
db.commit()
cursor.close()
db.close()
return res
return wrapper
class MusicBot(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command(name="myplaylist", aliases=['mypl'])
@connectToDB
async def myPlaylist(self, ctx, name=None, currentPage=0, cursor=None):
currentPage = int(currentPage)
if name:
await self.getPlaylistByName(ctx, name, cursor)
return
result = cursor.execute(f"SELECT playlist_name, playlist_items, playlist_length, date"
f" FROM PLAYLIST WHERE user = ?", (str(ctx.author),))
playlists = result.fetchall()
embed = getPlaylistsEmbed(ctx, playlists, currentPage)
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(MusicBot(bot))
但这并没有优化代码,因为 discordpy 必须知道你想要命令的参数是什么,所以在 wrapper 中我已经说过 def wrapper(*args, **kwargs):
没有指定任何参数所以 discordpy 假设我只需要默认参数。
为了解决这个问题,我尝试过:
- 使用 exec 更改每个 运行 所需的参数,并检查以获取参数,如下所示:
def connectToDB(f):
global func
func = f
params = inspect.signature(func)
exec(
f"async def wrapper{str(params)}:"
" db = sqlite3.connect(DB_DIR)"
" cursor = db.cursor()"
" print(*args, **kwargs)"
f" res = await func{str(params)}"
" db.commit()"
" cursor.close()"
" db.close()"
" return res", globals()
)
return wrapper
这是行不通的,因为在每个命令的 运行 结束后它将保留其最后的更改。
2. 像这样分配装饰器
@connectToDB
@commands.command(name="myplaylist", aliases=['mypl'])
3. 这个很蠢。制作装饰器工厂以获取参数
def connectToDB(*args, **kwargs):
print(args, kwargs)
def decorator(func):
async def wrapper(*_args, **_kwargs):
db = sqlite3.connect(DB_DIR)
cursor = db.cursor()
print(*_args, **_kwargs)
res = await func(*_args, **_kwargs, cursor=cursor)
db.commit()
cursor.close()
db.close()
return res
return wrapper
return decorator
@commands.command(name="myplaylist", aliases=['mypl'])
@connectToDB(self=None, ctx=None, name=None, page=0, msg=None, cursor=None)
async def myPlaylist(self, ctx, name=None, page=0, msg=None, cursor=None):
...
当尝试将装饰器与依赖于内省函数的库一起使用时,这个问题经常发生。解决方案是 functools.wraps()
.
def connectToDB(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
# setup database...
res = await func(*args, **kwargs, cursor=cursor)
# teardown database...
return res
return wrapper
functools.wraps
调用 functools.update_wrapper
,这将更新“包装函数使其看起来像包装函数”,复制元数据以便内省工具报告原始函数的属性,而不是包装器。