简体   繁体   中英

Decorated function in Python always returns None

I have an odd issue with decorators in Python. Basically I am trying to wrap a function so it will create and teardown cursors with each request (don't ask - this is just an example that's useful for demonstrating the issue! I have other intended than just this).

Here's a sample:

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)

This works, but the results I get return only None from the wrapped function:

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

You are ignoring the return value of the wrapped function:

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

Here your function ends with no explicit return value, so Python gives you the default instead, None .

You want to capture the return value and return that:

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

You could use try: / finally here to ensure the cursor is closed even if an exception is raised; this simplifies the code too as the finally suite is always executed, even when returning in the try block:

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

Another option would be to use the cursor as a context manager; in that case any transaction will be committed automatically for you too; on an exception, the transaction would be rolled back. In either case, the cursor will also be closed when the context exits:

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

Seecursors as context managers in the oursql documentation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM