Python中能否使用exec()重构面向对象类方法?实践报错问题咨询
用exec()动态生成Python类方法的正确姿势
当然可以用exec()来动态生成类的方法!你的报错主要是因为对exec执行代码的缩进规则理解有误,以及lambda表达式的写法错误。下面一步步帮你解决问题:
一、修复exec()的语法错误
你之前手动给exec里的函数定义加了缩进,这是导致SyntaxError的核心原因——exec执行的代码块有自己的上下文,不需要额外添加类内部的缩进,直接写标准的函数定义语法即可,类会自动把生成的函数绑定为自身方法。
正确写法1:类定义外部生成并绑定
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): return self.__get_social("{stub}")''') # 将生成的函数绑定为类的方法 setattr(Parser, f'get_{stub}', locals()[f'get_{stub}']) # 清理局部变量,避免污染命名空间 del locals()[f'get_{stub}']
正确写法2:类定义内部直接执行exec
也可以把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): return self.__get_social("{stub}")''')
二、修复lambda表达式的语法错误
lambda是表达式,不能使用return关键字,直接写返回的表达式即可。另外,在实例的__init__里绑定方法时,不需要把self作为lambda的参数(因为方法绑定到实例后,调用时会自动传入self):
def __init__(self, html): self.html = html self.soup = bs4.BeautifulSoup(html, "html.parser") # 正确的lambda绑定方式 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,但如果是实际项目,更推荐用functools.partial来绑定参数,既简洁又避免exec字符串拼接的潜在风险:
from functools import partial class Parser(): # ... 其他方法保持不变 ... def __init__(self, html): self.html = html self.soup = bs4.BeautifulSoup(html, "html.parser") # 用partial绑定固定参数 self.get_website = partial(self.__get_social, "website") self.get_instagram = partial(self.__get_social, "instagram") self.get_facebook = partial(self.__get_social, "facebook")
内容的提问来源于stack exchange,提问作者ginjaemocoes
相关产品推荐
相关产品推荐

