51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

如何在 Python 2.7 中获取未调用函数的局部变量

背景 {#背景}

我们公司平台的开发环境中使用的是 Python 2.7 版本,最近工作中,我遇到这样一个需求:我需要在一个方法中访问另一个函数内部定义的变量。这些变量包括字符串和数字类型,来源于一个预定义的固定函数。

这个固定函数会在执行文件前由平台自动注入,且不能被修改,并且函数中包含的一些变量是动态的,只能注入之后才能知道具体是什么。本质就是这个函数本身的逻辑无法满足我的需求,我需要重写该函数,并且需要保留函数中的一些变量的值。我的目标是,在不执行这个固定函数的前提下,获取到其中局部定义的字符串和数字变量。

例如,固定函数如下:

def private_func(*args, **kwargs):
    client_cer = "xxxx cer xxxx"
    client_key = 'xxxx key xxxx'

    <span class="n">other_arg</span> <span class="o">=</span> <span class="n">client_key</span> <span class="o">+</span> <span class="n">client_cer</span>




在这个函数中,client_cerclient_key 是我要提取的变量,而像 other_arg 这种复杂计算结果的变量则可以忽略。

通过调研和实践,我找到了一种基于 静态分析 的优雅方法,借助 Python 的 inspectast 模块完成这一任务。

解决方案 {#解决方案}

思路 {#思路}

  1. 获取函数源码 :使用 inspect 模块提取函数源码。
  2. 解析源码为 AST(抽象语法树) :通过 ast 模块读取函数的语法结构。
  3. 遍历 AST,提取赋值变量:定位赋值语句,提取变量名和值,只保留字符串和数字类型的变量。

代码实现 {#代码实现}

以下是完整的代码实现,支持 Python 2.7 环境(Python 3 当然也是兼容的):

import inspect
import ast

def analyze_variables_from_source(func):
# 获取函数源码
source = inspect.getsource(func)
tree = ast.parse(source)  # 解析为 AST(抽象语法树)


    <span class="c1"># 提取赋值语句中的变量和值</span>
    <span class="n">variables</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">ast</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">tree</span><span class="p">):</span>
        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Assign</span><span class="p">):</span>  <span class="c1"># 如果是赋值语句</span>
            <span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">targets</span><span class="p">:</span>
                <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Name</span><span class="p">):</span>  <span class="c1"># 确保是变量名</span>
                    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">):</span>  <span class="c1"># 字符串值</span>
                        <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">s</span>
                    <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">):</span>  <span class="c1"># 数字值</span>
                        <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">n</span>
    <span class="k">return</span> <span class="n">variables</span>




使用示例 {#使用示例}

假设我们要提取 private_func 中的字符串和数字变量:

def private_func(*args, **kwargs):
    client_cer = "xxxx cer xxxx"
    client_key = 'xxxx key xxxx'

    <span class="n">other_arg</span> <span class="o">=</span> <span class="n">client_key</span> <span class="o">+</span> <span class="n">client_cer</span>  <span class="c1"># 忽略这类逻辑</span>




调用代码:

result = analyze_variables_from_source(private_func)
print(result)
# 输出: {'client_cer': 'xxxx cer xxxx', 'client_key': 'xxxx key xxxx'}

支持更多数据类型的扩展 {#支持更多数据类型的扩展}

除了字符串和数字,我们还可以扩展代码以支持 列表、字典等数据类型 的提取。以下是改进版代码:

import inspect
import ast

def analyze_variables_from_source_extended(func):
source = inspect.getsource(func)
tree = ast.parse(source)


    <span class="n">variables</span> <span class="o">=</span> <span class="p">{}</span>
    <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">ast</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">tree</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Assign</span><span class="p">):</span>  <span class="c1"># 跳过非赋值语句</span>
            <span class="k">continue</span>
        <span class="k">for</span> <span class="n">target</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">targets</span><span class="p">:</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Name</span><span class="p">):</span>  <span class="c1"># 跳过非变量名的目标</span>
                <span class="k">continue</span>

            <span class="n">value</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="n">value</span>
            <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">):</span>  <span class="c1"># 字符串</span>
                <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="o">.</span><span class="n">s</span>
            <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">):</span>  <span class="c1"># 数字</span>
                <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span><span class="o">.</span><span class="n">n</span>
            <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">List</span><span class="p">):</span>  <span class="c1"># 列表</span>
                <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
                    <span class="n">elt</span><span class="o">.</span><span class="n">s</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">elt</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">)</span> <span class="k">else</span>
                    <span class="n">elt</span><span class="o">.</span><span class="n">n</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">elt</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
                    <span class="k">for</span> <span class="n">elt</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">elts</span>
                <span class="p">]</span>
            <span class="k">elif</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Dict</span><span class="p">):</span>  <span class="c1"># 字典</span>
                <span class="n">keys</span> <span class="o">=</span> <span class="p">[</span>
                    <span class="n">key</span><span class="o">.</span><span class="n">s</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">)</span> <span class="k">else</span>
                    <span class="n">key</span><span class="o">.</span><span class="n">n</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
                    <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">keys</span>
                <span class="p">]</span>
                <span class="n">values</span> <span class="o">=</span> <span class="p">[</span>
                    <span class="n">val</span><span class="o">.</span><span class="n">s</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Str</span><span class="p">)</span> <span class="k">else</span>
                    <span class="n">val</span><span class="o">.</span><span class="n">n</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Num</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
                    <span class="k">for</span> <span class="n">val</span> <span class="ow">in</span> <span class="n">value</span><span class="o">.</span><span class="n">values</span>
                <span class="p">]</span>
                <span class="n">variables</span><span class="p">[</span><span class="n">target</span><span class="o">.</span><span class="n">id</span><span class="p">]</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">keys</span><span class="p">,</span> <span class="n">values</span><span class="p">))</span>
    <span class="k">return</span> <span class="n">variables</span>




假设 private_func 的变量变得更加多样化:

def private_func(*args, **kwargs):
    client_cer = "xxxx cer xxxx"
    client_key = 'xxxx key xxxx'
    numbers = [1, 2, 3, 4]
    config = {"host": "localhost", "port": 8080}

调用扩展版代码:

result = analyze_variables_from_source_extended(private_func)
print(result)
# 输出: 
# {
#     'client_cer': 'xxxx cer xxxx',
#     'client_key': 'xxxx key xxxx',
#     'numbers': [1, 2, 3, 4],
#     'config': {'host': 'localhost', 'port': 8080}
# }

优缺点分析 {#优缺点分析}

优点:

  1. 安全性:不会执行目标函数代码,完全基于静态分析,避免副作用。
  2. 灵活性:支持多种数据类型的提取,包括字符串、数字、列表和字典。
  3. 适用性广:适合在调试、代码检查器、静态分析器等场景中使用。

局限性:

  1. 无法处理动态计算 :例如通过外部调用、运行时计算得到的变量值(如 other_arg)无法直接解析。
  2. 复杂函数支持有限:如果函数包含嵌套定义或条件语句,需额外逻辑支持。

总结 {#总结}

在 Python 2.7 环境中,通过 inspectast 模块,我们能够优雅地提取未调用函数的局部变量,且完全避免执行代码的风险。此方法对简单的固定函数非常高效,适用于遗留系统和静态分析工具。

赞(1)
未经允许不得转载:工具盒子 » 如何在 Python 2.7 中获取未调用函数的局部变量