如何monkeypatch一个python库的类方法?

ttp71kqs  于 2023-01-27  发布在  Python
关注(0)|答案(1)|浏览(131)

我正在尝试修改better_profanity库以包含get_replacement_for_swear_word函数的附加参数。为此,我首先导入库的必要部分并测试其功能,然后:

from better_profanity import profanity, Profanity

text = "Nice c0ck"
censored = profanity.censor(text)
print(censored)

现在我得到了类方法的源代码,修改它并执行到__main___

from inspect import getsource
new_hide_swear_words = getsource(profanity._hide_swear_words).replace(
    'get_replacement_for_swear_word(censor_char)', 'get_replacement_for_swear_word(censor_char, cur_word)').replace(
        'ALLOWED_CHARACTERS', 'self.ALLOWED_CHARACTERS'
    )
# fixing the indent
new_hide_swear_words = '\n'.join(i[4:] for i in new_hide_swear_words.split('\n')) 
exec(new_hide_swear_words)

现在我在类中替换这个函数:

profanity._hide_swear_words = _hide_swear_words.__get__(profanity, Profanity)

注意,我把ALLOWED_CHARACTERS替换为self.ALLOWED_CHARACTERS,这是因为库的作者在定义类的同一个文件中导入了ALLOWED_CHARACTERS,所以当我交换函数并试图再次运行第一段代码时,它说这个变量没有定义,碰巧它也存储在self中,但是其他几个进口模块就没有这样的运气了。有什么办法解决这个问题吗?
Here是github上的类定义。

taor4pac

taor4pac1#

当运行exec(new_hide_swear_words)时,您在当前模块中定义了函数_hide_swear_words(这就是为什么您以后可以只使用_hide_swear_words来访问它)。
然而这意味着,函数完全存在于模块中,所以当你用profanity.censor(some_text)间接调用它时,它会在这个模块中运行函数,并在模块中查找所有依赖的全局符号。这就是为什么它不能访问变量ALLOWED_CHARACTERS或函数any_next_words_form_swear_words的原因。它们在亵渎模块中定义,而不是在你运行exec的模块中定义。
解决这个问题的一个方法是将所有符号导入到模块中。

from inspect import getsource

from better_profanity import Profanity, profanity
from better_profanity.constants import ALLOWED_CHARACTERS
from better_profanity.utils import *

new_hide_swear_words = getsource(profanity._hide_swear_words)
new_hide_swear_words = "\n".join(i[4:] for i in new_hide_swear_words.split("\n"))
exec(new_hide_swear_words)

profanity._hide_swear_words = _hide_swear_words.__get__(profanity, Profanity)

text = "Nice c0ck"
censored = profanity.censor(text)
print(censored)

另一种方法是在亵渎语言模块本身执行函数(那么所有的符号都已经定义好了),但是这也会有一些开销,例如,你必须导入模块并将其传递给exec函数,然后你需要从模块中提取函数(因为它将在该模块中定义)。

from importlib import import_module
from inspect import getsource
from better_profanity import Profanity, profanity

new_hide_swear_words = getsource(profanity._hide_swear_words)
# fixing the indent
new_hide_swear_words = "\n".join(i[4:] for i in new_hide_swear_words.split("\n"))
profanity_module = import_module(Profanity.__module__)
exec(new_hide_swear_words, vars(profanity_module))

profanity._hide_swear_words = profanity_module._hide_swear_words.__get__(
    profanity, Profanity
)

text = "Nice c0ck"
censored = profanity.censor(text)
print(censored)

profanity_module = import_module(Profanity.__module__)import better_profanity.better_profanity as profanity_module是一样的。

相关问题