Python中能否使用exec()重构面向对象类方法?
用exec()重构Python类方法的踩坑与解决
先聊聊你遇到的情况:你写了一个解析HTML社交链接的Parser类,里面get_website、get_instagram、get_facebook三个方法逻辑高度重复,想借着学习LISP宏的思路用exec()循环简化代码,结果触发了SyntaxError;后来试lambda也踩了坑。这两个问题都是Python语法细节的问题,咱们一个个拆解:
一、exec()重构类方法的正确姿势
你报错的代码里有两个关键错误:
- 误用了
eval()而非exec():eval()只能计算单个表达式的结果,没办法执行def这类定义语句,必须用exec()来执行包含语句的代码字符串。 - 给代码字符串加了多余的缩进前缀:
exec()执行的代码是在类的命名空间内直接运行的,方法定义的缩进只需要符合代码片段自身的语法(Python标准的4空格缩进),不需要额外添加类层级的缩进(比如8个、12个空格)。
正确实现方式
直接在类定义块内执行exec(),生成的方法会自动成为类的成员:
import bs4 class Parser(): def __init__(self, html): self.html = html self.soup = bs4.BeautifulSoup(html, "html.parser") def __get_socials(self): socials = self.soup.select("div.external-links") assert 1 == len(socials) return socials[0] def __get_social(self, aria_label): social_tags = self.__get_socials() social_results = social_tags.select(f"a[aria-label='{aria_label}']") if 0 == len(social_results): return assert 1 == len(social_results) return social_results[0].get("href") # 在类内部批量生成方法 for stub in ["website", "instagram", "facebook"]: exec(f'def get_{stub}(self):\n return self.__get_social("{stub}")')
如果你想在类定义外部批量添加方法,也可以用setattr配合exec:
# 类定义完成后批量绑定方法 for stub in ["website", "instagram", "facebook"]: exec(f'''def get_{stub}(self): return self.__get_social("{stub}")''') setattr(Parser, f'get_{stub}', locals()[f'get_{stub}']) del locals()[f'get_{stub}'] # 清理临时变量
二、lambda表达式的错误修正
你写的lambda代码犯了两个语法错误:
- lambda是匿名函数,语法为
lambda 参数: 表达式,不需要也不能用return关键字,直接写要返回的表达式即可。 - 给实例绑定方法时,不需要把
self作为lambda的参数(实例绑定方法时,self会被自动传入)。
正确的lambda写法
在__init__方法内绑定lambda:
def __init__(self, html): self.html = html self.soup = bs4.BeautifulSoup(html, "html.parser") # 绑定社交平台的获取方法 self.get_website = lambda: self.__get_social("website") self.get_instagram = lambda: self.__get_social("instagram") self.get_facebook = lambda: self.__get_social("facebook")
注意:用lambda给实例绑定方法时,每个实例都会拥有独立的lambda副本;而用exec给类添加方法是所有实例共享同一个方法,这是两者的核心区别。
额外提示:更Pythonic的替代方案
虽然你是出于趣味尝试exec,但如果追求可读性和Python风格,更推荐用setattr结合工厂函数的方式,避免字符串拼接代码的风险:
def make_social_getter(stub): def getter(self): return self.__get_social(stub) return getter # 批量给类添加方法 for stub in ["website", "instagram", "facebook"]: setattr(Parser, f'get_{stub}', make_social_getter(stub))
内容的提问来源于stack exchange,提问作者ginjaemocoes
相关产品推荐
相关产品推荐

