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

Python3.7中exec()函数在类/函数内失效的原因及解决方案咨询

问题分析:exec()在Python类方法与全局代码中的行为差异

问题重现

你碰到的这个情况很典型:exec()在全局代码里能正常给变量赋值,但放到类的方法内部时,逻辑完全一致却没法正确更新变量。先把你的代码和配套数据整理清楚:

代码示例

#!/usr/bin/env python
import pandas as pd

################################# exec() WITHIN A CLASS ###################################
class Test:
    def __init__(self):
        pass
    def f(self):
        pose, frame, min_complexE, min_lEfree, best_structvar, best_conf, complexE, ligandE_bound, proteinE_bound, \
            min_lEfree = [None] * 10
        table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0]) # 1st line is a comment
        table_columns = table.columns
        for i, row in table.iterrows():
            # Variable assignemnt
            for varname in ["pose", "frame", "min_complexE", "min_lEfree", "best_structvar", "best_conf", "complexE", "ligandE_bound", "proteinE_bound", "min_lEfree", "Eint"]:
                if varname in table_columns:
                    exec("%s = %s" % (varname, row[varname]), globals(), globals())
                else:
                    exec("%s = None" % (varname), globals(), globals())
        print("proteinE_bound within class =", proteinE_bound)
    def caller1(self):
        self.f()
    def caller2(self):
        self.caller1()
Test().caller2()

################################# exec() IN RAW CODE ###################################
pose, frame, min_complexE, min_lEfree, best_structvar, best_conf, complexE, ligandE_bound, proteinE_bound, \
    min_lEfree = [None] * 10
table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0]) # 1st line is a comment
table_columns = table.columns
for i, row in table.iterrows():
    # Variable assignemnt
    for varname in ["pose", "frame", "min_complexE", "min_lEfree", "best_structvar", "best_conf", "complexE", "ligandE_bound", "proteinE_bound", "min_lEfree", "Eint"]:
        if varname in table_columns:
            exec("%s = %s" % (varname, row[varname]), globals(), globals())
        else:
            exec("%s = None" % (varname), globals(), globals())
print("proteinE_bound as raw code =", proteinE_bound)

配套数据文件 sample_scores.txt

# Contains all results. For the best result for each compound please refer to file BEST_RESULTS.
molname Eint complexE ligandE_bound proteinE_bound stereoisomer ionstate tautomer pose frame
LEM00001847 -63.000496 -17406.593934 -84.868633 -17258.724804 1 1 1 1 571
LEM00001847 -62.412897 -17474.918135 -64.778724 -17347.726515 1 1 1 1 171
LEM00001847 -61.249384 -17423.452346 -82.875735 -17279.327226 1 1 1 1 531

原因分析

核心问题出在Python的作用域规则和exec()的参数使用上,我之前踩过这个坑:

  1. 作用域不匹配:

    • 全局代码里,你定义的proteinE_bound等变量属于全局作用域,调用exec(..., globals(), globals())时,明确指定了全局命名空间作为执行环境,赋值直接修改全局变量,后续打印自然能拿到更新后的值。
    • 但在类方法f()内部,你一开始定义的proteinE_bound是局部变量(属于方法的局部作用域),可你却让exec()把值写到全局命名空间里。方法内部的局部变量和全局变量是完全独立的两个变量——你以为在更新局部的proteinE_bound,实际上是在全局作用域创建/更新了另一个同名变量,而方法里的局部变量还是初始的None,所以打印时输出的是局部的那个None。
  2. Python局部作用域的限制:
    Python编译函数/方法时,会扫描所有变量赋值语句,把被赋值的变量标记为局部变量。exec()的代码是运行时动态执行的,编译阶段Python不知道你要给哪些局部变量赋值,所以即使你想通过exec()修改局部变量,也绕不开这个机制。

解决方案

针对这个场景,有几种更合理的替代方案,比强行用exec()修改局部变量更安全:

方案1:使用字典存储变量(推荐)

放弃直接创建多个局部变量,改用一个字典来存储所有需要的字段,不管是类方法还是全局代码里都能正常工作:

import pandas as pd

class Test:
    def __init__(self):
        pass
    def f(self):
        # 用字典统一管理变量,替代多个独立变量
        vars_dict = {
            "pose": None,
            "frame": None,
            "min_complexE": None,
            "min_lEfree": None,
            "best_structvar": None,
            "best_conf": None,
            "complexE": None,
            "ligandE_bound": None,
            "proteinE_bound": None,
            "Eint": None
        }
        table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0])
        table_columns = table.columns
        for _, row in table.iterrows():
            for varname in vars_dict.keys():
                vars_dict[varname] = row[varname] if varname in table_columns else None
        print("proteinE_bound within class =", vars_dict["proteinE_bound"])
    
    def caller1(self):
        self.f()
    
    def caller2(self):
        self.caller1()

Test().caller2()

# 全局代码版本同样适用
vars_dict = {
    "pose": None,
    "frame": None,
    "min_complexE": None,
    "min_lEfree": None,
    "best_structvar": None,
    "best_conf": None,
    "complexE": None,
    "ligandE_bound": None,
    "proteinE_bound": None,
    "Eint": None
}
table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0])
table_columns = table.columns
for _, row in table.iterrows():
    for varname in vars_dict.keys():
        vars_dict[varname] = row[varname] if varname in table_columns else None
print("proteinE_bound as raw code =", vars_dict["proteinE_bound"])

方案2:如果一定要用exec(),修改局部作用域(不推荐)

如果你坚持要用exec(),可以通过locals()获取局部作用域的字典,但要注意:Python官方不推荐依赖locals()的修改,因为函数内部locals()的行为可能不稳定。修改后的类方法代码如下:

def f(self):
    pose, frame, min_complexE, min_lEfree, best_structvar, best_conf, complexE, ligandE_bound, proteinE_bound, \
        min_lEfree = [None] * 10
    table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0])
    table_columns = table.columns
    local_vars = locals()  # 获取当前局部作用域的字典
    for i, row in table.iterrows():
        for varname in ["pose", "frame", "min_complexE", "min_lEfree", "best_structvar", "best_conf", "complexE", "ligandE_bound", "proteinE_bound", "min_lEfree", "Eint"]:
            if varname in table_columns:
                exec("%s = %s" % (varname, row[varname]), globals(), local_vars)
            else:
                exec("%s = None" % (varname), globals(), local_vars)
    # 必须从local_vars里取变量,因为局部变量可能没被同步
    print("proteinE_bound within class =", local_vars["proteinE_bound"])

方案3:利用pandas的行转属性(更简洁)

既然你在用pandas,直接把行数据转换成对象属性会更简洁:

import pandas as pd
from types import SimpleNamespace

class Test:
    def __init__(self):
        pass
    def f(self):
        table = pd.read_table("sample_scores.txt", delim_whitespace=True, skiprows=[0])
        # 把行数据转换成可通过属性访问的对象
        for _, row in table.iterrows():
            data = SimpleNamespace(**row.to_dict())
            # 补充缺失的字段
            required_vars = ["pose", "frame", "min_complexE", "min_lEfree", "best_structvar", "best_conf", "Eint"]
            for var in required_vars:
                if not hasattr(data, var):
                    setattr(data, var, None)
        print("proteinE_bound within class =", data.proteinE_bound)
    
    def caller1(self):
        self.f()
    
    def caller2(self):
        self.caller1()

Test().caller2()

总结

尽量避免在函数/方法内部用exec()修改局部变量,这不仅容易踩作用域的坑,代码可读性也差。用字典或者SimpleNamespace来管理动态变量是更稳妥的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:47:41