You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python库better_profanity的类方法打猴子补丁并解决变量未定义问题?

问题描述

我想要修改better_profanity库,为get_replacement_for_swear_word函数添加额外参数。操作步骤如下:

  1. 导入库并测试基础功能:
from better_profanity import profanity, Profanity

text = "Nice c0ck"
censored = profanity.censor(text)
print(censored)
  1. 获取类方法源码,修改后通过exec导入到当前作用域:
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'
    )
# 修复缩进
new_hide_swear_words = '\n'.join(i[4:] for i in new_hide_swear_words.split('\n')) 
exec(new_hide_swear_words)
  1. 替换类中的目标方法:
profanity._hide_swear_words = _hide_swear_words.__get__(profanity, Profanity)

其中我不得不将ALLOWED_CHARACTERS替换为self.ALLOWED_CHARACTERS,因为原库作者在类定义文件中导入了该变量,但替换方法后运行代码时会提示该变量未定义。好在这个变量同时存在于类实例的self中,但如果遇到其他仅在原模块中导入、未存储在self里的变量,该如何解决?


解决方案

方法1:在exec时注入依赖的模块变量

原方法依赖的模块级变量(比如ALLOWED_CHARACTERS)可以直接从原库的对应模块导入,然后在执行exec时将这些变量传入全局作用域,这样修改后的方法就能直接访问这些变量,无需替换为self.xxx。

示例代码:

from inspect import getsource
from better_profanity import profanity, Profanity
# 直接导入原方法依赖的模块变量
from better_profanity.constants import ALLOWED_CHARACTERS

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)'
)
# 修复缩进,无需替换ALLOWED_CHARACTERS
new_hide_swear_words = '\n'.join(i[4:] for i in new_hide_swear_words.split('\n')) 
# 将所需变量传入exec的全局作用域
exec(new_hide_swear_words, globals())
# 替换原实例的方法
profanity._hide_swear_words = _hide_swear_words.__get__(profanity, Profanity)

原理:exec的第二个参数指定了执行代码时的全局命名空间,导入的ALLOWED_CHARACTERS会被包含其中,修改后的方法就能像原方法一样直接引用该变量。

方法2:继承Profanity类重写目标方法

这是更稳妥、可维护性更高的方案,直接创建子类并重写_hide_swear_words方法,避免依赖原方法的源码字符串修改,同时可以自由访问原模块的变量或类的属性。

示例代码:

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

class CustomProfanity(Profanity):
    def _hide_swear_words(self, text: str, censor_char: str) -> str:
        processed_text = []
        i = 0
        text_length = len(text)
        
        while i < text_length:
            current_char = text[i]
            # 跳过允许的非字母数字字符
            if current_char in ALLOWED_CHARACTERS:
                processed_text.append(current_char)
                i += 1
                continue
            
            # 查找连续的字母数字序列(可能包含脏话)
            j = i
            while j < text_length and text[j] not in ALLOWED_CHARACTERS:
                j += 1
            cur_word = text[i:j].lower()
            
            # 检查是否是脏话,修改get_replacement_for_swear_word的调用参数
            if cur_word in self._bad_words:
                replacement = self.get_replacement_for_swear_word(censor_char, cur_word)
                processed_text.append(replacement)
            else:
                processed_text.append(text[i:j])
            
            i = j
        
        return ''.join(processed_text)

# 使用自定义的实例替换默认profanity
profanity = CustomProfanity()
# 测试功能
text = "Nice c0ck"
censored = profanity.censor(text)
print(censored)

原理:子类可以直接导入原库的模块变量,也可以通过self访问父类的属性,完全复刻原方法逻辑后,按需修改get_replacement_for_swear_word的调用即可,不会出现变量未定义的问题,且不受原库版本更新时源码格式变化的影响。


内容的提问来源于stack exchange,提问作者user9102437

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 07:25:32