Python:替换模块类中的函数
Posted
技术标签:
【中文标题】Python:替换模块类中的函数【英文标题】:Python: replacing a function within a class of a module 【发布时间】:2018-11-08 23:25:37 【问题描述】:我正在尝试替换类中定义的函数,以便在不更改实际代码的情况下修改其功能(如内部工作原理)。 我以前从未这样做过,因此在更换它时遇到了一些问题。
更改代码将使我能够访问我的 Python 库中的包,这不是一个很好的选择。
例如,如果模块被称为 testMOD,
class testMOD(object):
def testFunc(self, variable):
var = variable
self.something = var + 12
然后我会导入 testMOD,定义一个类 (mytest = testMOD()),并访问类中定义的函数 testFunc,并将其更改为已定义的函数。
例如,
from somemodule import testMOD
mytest = testMOD()
def alternativeFunc(self, variable):
var = variable
self.something = var + 1.2
# A problem here
mytest.testFunc = alternativeFunc
如您所见,如果我只是用我定义的函数手动覆盖(?)类中的函数,它将无法正常工作。
它没有给出任何语法错误,但是,问题是被替换的函数认为'self'是函数的另一个变量,并说它需要'variable'变量的另一个参数(我猜不是个好名字)。
我想让替换函数与被替换函数完全相同,但需要额外的代码或一些小的修改。然而,“自我”几乎没有像在课堂上那样工作。
有没有办法正确实现定义的函数来替换导入类的函数?
【问题讨论】:
【参考方案1】:我建议 4 种解决方案,从最差到最好(恕我直言),但当然这也取决于您的具体限制:
替换实例方法(1):我利用函数是Python中的描述符这一事实,这样我就可以使用AlternativeFunc
上的__get__
方法来获取它作为实例mytest
的方法并覆盖实例mytest
的testFunc
方法(不覆盖类方法):
class testMOD(object):
def testFunc(self, variable):
var = variable
self.something = var + 12
print('Original:', self.something)
def alternativeFunc1(self, variable):
var = variable
self.something = var + 1.2
print('Alternative1:', self.something)
mytest1 = testMOD()
mytest1.testFunc(10) # Original: 22
mytest1.testFunc = alternativeFunc1.__get__(mytest1, testMOD)
mytest1.testFunc(10) # Alternative1: 11.2
mytestX = testMOD()
mytestX.testFunc(10) # Original: 22
替换实例方法(2):这次我用types.MethodType
,比第一种方案可读性强一点:
import types
class testMOD(object):
def testFunc(self, variable):
var = variable
self.something = var + 12
print('Original:', self.something)
def alternativeFunc1(self, variable):
var = variable
self.something = var + 1.2
print('Alternative1:', self.something)
mytest1 = testMOD()
mytest1.testFunc(10) # Original: 22
funcType = types.MethodType
mytest1.testFunc = funcType(alternativeFunc1, mytest1)
mytest1.testFunc(10) # Alternative1: 11.2
mytestX = testMOD()
mytestX.testFunc(10) # Original: 22
对类方法执行猴子修补。与第一种方法不同,它改变了类的任何实例的行为:
class testMOD(object):
def testFunc(self, variable):
var = variable
self.something = var + 12
print('Original:', self.something)
def alternativeFunc2(self, variable):
var = variable
self.something = var + 1.2
print('Alternative2:', self.something)
mytest2 = testMOD()
mytest2.testFunc(10) # Original: 22
testMOD.testFunc = alternativeFunc2
mytest2.testFunc(10) # Alternative2: 11.2
mytestX = testMOD()
mytestX.testFunc(10) # Alternative2: 11.2
创建一个继承自testMOD
的类来覆盖方法:
class testMODNew(testMOD):
def testFunc(self, variable):
var = variable
self.something = var + 1.2
print('Alternative3:', self.something)
mytest3 = testMODNew()
mytest3.testFunc(10) # Alternative3: 11.2
【讨论】:
当类是内部类,即对象只在内部实例化到某个外部模块时,我们应该怎么做?现在我能看到的唯一选择是修改源代码并维护我自己的 fork。 方法3是否只适用于某些版本的Python?我在 3.6.8 中尝试过,它没有像你那样更新现有实例。【参考方案2】:你可以monkey patch这个方法如下:
class TestMOD(object):
def testFunc(self, variable):
var = variable
self.something = var + 12
print(f'original self.something')
def alternativeFunc(self, variable):
var = variable
self.something = var + 1.2
print(f'alternative self.something')
if __name__ == '__main__':
test_original = TestMOD()
test_original.testFunc(12)
TestMOD.testFunc = alternativeFunc
test_alternate = TestMOD()
test_alternate.testFunc(12)
输出:
original 24
alternative 13.2
【讨论】:
感谢您的回答!当我以为我所做的与你所做的完全一样时,我真的很困惑,然后我意识到我必须更改实际的类,而不是继承的类变量。 值得一提:不需要创建新的TestMOD实例。 Monkey 修补会更改类本身,这也会影响任何现有实例。调用test_original.testFunc(12)
也会产生相同的效果。
@EdwardFalk 猴子补丁也改变了类描述。例如,更新是否会显示为 ??TestMod ?【参考方案3】:
检查 Python 中的类继承以创建您自己的自定义类:
from somemodule import TestMOD
class YourCustomClass(TestMOD):
# change the function
def test_func(self, variable):
#
#
your_class = YourCustomClass()
your_class.test_func(x)
【讨论】:
【参考方案4】:由于最初的问题要求一种方法来调用父类中的函数,然后还做一些额外的事情,我想我要指出的是,简单地替换函数可能会有问题;如果父类以任何方式被修改(它所属的模块被更新),那么您可能必须相应地修改代码。此外,他们可能不想重新创建原始函数只是为了在末尾添加一点。
我绝对同意创建一个从 testMod 继承的类是最好的选择,我只是建议从 testMod 调用函数然后修改结果。
class testMOD(object):
def testFunc(self, variable):
var = variable
return var + 12
class testMODNew(testMOD):
def testFunc(self, variable):
return testMOD.testFunc(self,variable) - 10.8
mytest4 = testMODNew()
print('Alternative4:', mytest4.testFunc(10)) # Alternative4: 11.2
可以进行其他更改,例如,如果您希望具有该类的对象跟踪该方法被调用的次数:
class testMODNew(testMOD):
__testFuncCount__ = 0
def testFunc(self, variable):
self.__testFuncCount__ += 1
return testMOD.testFunc(self,variable)
def getTestFuncCount(self):
return self.__testFuncCount__
mytest5 = testMODNew()
print('Original:',mytest5.testFunc(10)) #Original: 10
print('Original:',mytest5.testFunc(10)) #Original: 10
print('testFunc was called', mytest5.getTestFuncCount(), 'times.')
#testFunc was called 2 times
【讨论】:
【参考方案5】:这是一种 hack,但您可以使用 lambda 函数:
mytest.testFunc = lambda *args, **kwargs: alternativeFunc(mytest, *args, **kwargs)
【讨论】:
以上是关于Python:替换模块类中的函数的主要内容,如果未能解决你的问题,请参考以下文章