Python 中的修饰函数始终返回 None

use*_*843 2 python decorator nonetype python-decorators

我对 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)
Run Code Online (Sandbox Code Playgroud)

这是可行的,但我得到的结果仅从包装函数返回 None :

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

Mar*_*ers 6

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

db.cursor = db.con.cursor()
func(*args)
db.cursor.close()
Run Code Online (Sandbox Code Playgroud)

这里你的函数以没有显式返回值结束,因此 Python 为你提供了默认值None.

您想要捕获返回值并返回:

db.cursor = db.con.cursor()
retval = func(*args)
db.cursor.close()
return retval
Run Code Online (Sandbox Code Playgroud)

您可以在此处使用try:/finally来确保即使引发异常,游标也已关闭;这也简化了代码,因为finally套件总是被执行,即使在try块中返回时也是如此:

db.cursor = db.con.cursor()
try:
    return func(*args)
finally:
    db.cursor.close()
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用光标作为上下文管理器;在这种情况下,任何交易也将自动为您提交;如果出现异常,事务将被回滚。无论哪种情况,当上下文退出时,游标也将被关闭:

with db.con.cursor() as db.cursor:
    return func(*args)
Run Code Online (Sandbox Code Playgroud)

在 oursql 文档中将游标视为上下文管理器。