Python 中被装饰的函数总是返回 None

2 投票
1 回答
3981 浏览
提问于 2025-04-18 12:24

我在使用Python的装饰器时遇到了一个奇怪的问题。简单来说,我想把一个函数包裹起来,这样每次请求时就能创建和销毁游标(别问为什么,这只是一个例子,用来说明问题!我还有其他的用途)。

下面是一个示例:

class DB(object):
    """Our DB methods and connections"""

    def __init__(self):
        self.con = oursql.connect(host=host, user=user, passwd=passwd,
                                  port=port, db=db)
        self.cursor = None

    def __del__(self):
        self.con.close()


def wrapper(func):
    """Wrapper for our database methods"""
    def _exec(*args):
        """Wherein the wrapping takes place"""
        db.cursor = db.con.cursor()
        func(*args)
        db.cursor.close()
    return _exec

@wrapper
def get_tables(db):
    """Returns a list of all tables in the database"""
    results = []
    db.cursor.execute('show tables')
    tables = db.cursor.fetchall()
    for table in tables:
        results.append(table[0])
    print results
    return results

if __name__ == '__main__':
    db = DB()
    print get_tables(db)

这个方法是可行的,但我得到的结果是被包裹的函数只返回None:

[list of tables from "print results" goes in here]
None <- returned by the "print get_tables(db)" line

1 个回答

6

你忽略了被包装函数的返回值:

db.cursor = db.con.cursor()
func(*args)
db.cursor.close()

在这里,你的函数没有明确返回值,所以Python给你一个默认值,None

你应该捕获这个返回值并把它返回:

db.cursor = db.con.cursor()
retval = func(*args)
db.cursor.close()
return retval

你可以在这里使用try:finally,这样即使出现错误,光标也能被关闭;这也让代码更简单,因为finally部分总是会执行,即使在try块中返回了:

db.cursor = db.con.cursor()
try:
    return func(*args)
finally:
    db.cursor.close()

另一个选择是把光标当作上下文管理器使用;在这种情况下,任何事务都会自动提交;如果出现异常,事务会被回滚。在这两种情况下,当退出上下文时,光标也会被关闭:

with db.con.cursor() as db.cursor:
    return func(*args)

可以查看我们的文档中关于光标作为上下文管理器的内容

撰写回答