Python:如何向类的实例中注入新函数?

2 投票
1 回答
7086 浏览
提问于 2025-04-16 10:09

我尝试去修改 __mro__,但是它是只读的。

我的使用场景是这样的:

从 pyodbc(一个数据库API)创建的连接对象,有一个叫做 'autocommit' 的属性。最近,我在 pyodbc 的基础上包了一层 SQLAlchemy 的数据库连接池,以更好地管理资源。这个新的数据库连接池会返回一个 _ConnectionFairy,这是一个连接代理类,它不再提供 autocommit 这个属性。

我很想不去动第三方的代码。所以,继承 _ConnectionFairy 其实不是一个好选择(我可能需要重写连接池的类,以改变它创建连接代理的方式。想要查看源代码,可以点击 这里)。

一个不太优雅的解决方案是把所有出现的

conn.autocommit = True

都改成

# original connection object is accessible via .connection
conn.connection.autocommit = True 

所以,我想知道是否有可能给 _ConnectionFairy 的实例注入一组 getter、setter 和属性。

1 个回答

13

你可以用下面的语法“扩展”几乎任何类:

def new_func(self, param):
    print param

class a:
    pass

a.my_func = new_func
b = a()
b.my_func(10)

更新

如果你想为某些方法创建一种包装,可以使用getattr和setattr来保存原始方法,并用你自己的实现替换它。我在我的项目中做过类似的事情,不过方式稍有不同:

这里有一个例子:

class A:
    def __init__(self):
        setattr(self, 'prepare_orig', getattr(self,'prepare'))
        setattr(self, 'prepare', getattr(self,'prepare_wrapper'))

    def prepare_wrapper(self,*args,**kwargs):
        def prepare_thread(*args,**kwargs):
            try:
                self.prepare_orig(*args,**kwargs)
            except:
                print "Unexpected error:", sys.exc_info()[0]
        t = threading.Thread(target=prepare_thread, args=args, kwargs=kwargs)
        t.start()

    def prepare(self):
        pass

这段代码的意思是,其他开发者只需要在派生类中实现prepare方法,它就会在后台自动执行。这不是你问的内容,但我希望这能在某种程度上对你有所帮助。

撰写回答