在外部库中重写Python实例方法

2 投票
1 回答
873 浏览
提问于 2025-04-16 16:03

我写了一个用于数据库连接的上下文管理器。
我想要重写一个外部类的方法(在cx_oracle.Cursor中的'execute'),目的是在把查询发送到数据库之前,去掉任何多余的绑定变量(否则会导致数据库错误)。至于这些多余的变量为什么会出现,这个问题暂时不讨论。

下面的代码可以正常工作,但我在想我是否正确使用了_getattribute_这个方法(之前其实没有机会用到它)。

如果有人有更好的建议,告诉我怎么从这个外部库重写这个方法,我非常欢迎。

谢谢。我使用的是python 2.7

import cx_Oracle
from contextlib import contextmanager

class Cursor(cx_Oracle.Cursor):
    """A wrapper for cx_Oracle cursors that will drop extraneous bind variables passed to in"""

    def __init__(self, curs):
        self.curs = curs

    def execute(self, sql, *args, **kwargs):
        params = {}
        print("in execute, args[0] = [%s]" % str(args[0]))
        if len(args) == 1 and isinstance(args[0], dict):
            for bv in args[0].keys():
                if ':%s' % bv in sql:
                    params[bv] = args[0][bv]
            print('params = %s' % str(params))
        else:
            return self.curs.execute(sql, *args, **kwargs)

        return self.curs.execute(sql, params)


    def __getattribute__(self, name):
        if name == 'execute':
            return object.__getattribute__(self, name)
        elif name == 'curs':
            return object.__getattribute__(self, 'curs')
        else:
            curs = object.__getattribute__(self, 'curs')
            return cx_Oracle.Cursor.__getattribute__(curs, name)


@contextmanager
def db_conn():

    pool = cx_Oracle.SessionPool("user", "pwd", "database", min=2, max=10, increment=1, threaded=True)
    conn = pool.acquire()

    try:
        yield Cursor(conn.cursor())
    except:
        conn.rollback()
        raise
    else:
        conn.commit()
    finally:
        pool.release(conn)


if __name__ == '__main__':

    with db_conn() as curs:
        curs.execute('select * from dual where 1 = :a', {'a':1, 'b':2})
        print(curs.fetchall())

1 个回答

1

如果你使用 __getattr__ 方法,而不是 __getattribute__,那么在自己的类里面就不需要特别处理属性名称了。只有在找不到属性的时候,它才会去查看 __getattr__

像这样:

def __getattr__(self, name):
    return getattr(self.curs, name)

你需要把 class Cursor(cx_Oracle.Cursor): 改成 class Cursor(object):。通过继承的方式来实现你想要的功能有点复杂,因为游标是通过数据库连接的方法创建的。你也可以继承 SessionPool,这样它的 cursor 方法就能返回你自定义的游标实例,但这样做可能不太值得。

撰写回答