检测python函数中的所有全局变量? [英] Detect all global variables within a python function?

查看:736
本文介绍了检测python函数中的所有全局变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图分析一些凌乱的代码,这恰好在函数中使用全局变量(我试图重构代码,以便函数只使用局部变量)。有什么方法可以检测函数中的全局变量?



例如:

  def f(x):
x = x + 1
z = x + y
return z

这里的全局变量是 y ,因为它没有作为参数给出,也没有在函数内创建。 / p>

我试图使用字符串解析来检测函数内的全局变量,但是它变得有点混乱;我想知道是否有更好的方法来做到这一点?



编辑:如果有人感兴趣,这是我用来检测全局变量的代码关于kindall的答案和Paolo对这个问题的回答:从Python脚本中捕获stdout ):

  from dis导入dis 

def capture(f) :

Decorator捕获标准输出

def captured(* args,** kwargs):
import sys
from cStringIO import StringIO

#设置环境
backup = sys.stdout

try:
sys.stdout = StringIO()#capture output
f(* args,** kwargs)
out = sys.stdout.getvalue()#释放输出
finally:
sys.stdout.close()#关闭流
sys.stdout =备份#恢复原始标准输出

返回#捕获的输出包装在一个字符串中

返回捕获

def return_globals(f):

打印函数f

x = dis_(f)
中的所有全局变量,对于x.splitlines()中的i:
如果i中的LOAD_GLOBAL:
print i

dis_ = capture(dis)

dis_(f)

dis 默认情况下不会返回输出,所以如果您想操纵 dis 作为字符串,您必须使用由Paolo编写的捕获装饰器,并在此处发布:

检查字节码。

  from dis import $ 
dis(f)

结果:

  2 0 LOAD_FAST 0(x)
3 LOAD_CONST 1(1)
6 BINARY_ADD
7 STORE_FAST 0(x)

3 10 LOAD_FAST 0(x)
13 LOAD_GLOBAL 0(y)
16 BINARY_ADD
17 STORE_FAST 1(z)

4 20 LOAD_FAST 1(z)
23 RETURN_VALUE

全局变量将具有 LOAD_GLOBAL 操作码,而不是 LOAD_FAST 。 (如果函数改变了任何全局变量,也会有 STORE_GLOBAL 操作码。)



,你甚至可以编写一个函数来扫描函数的字节码并返回它使用的全局变量列表。事实上:

  from dis import HAVE_ARGUMENT,opmap 
$ b $ def getglobals(func):
GLOBAL_OPS = opmap [LOAD_GLOBAL],opmap [STORE_GLOBAL]
EXTENDED_ARG = opmap [EXTENDED_ARG]

func = getattr(func,im_func,func)
code = func.func_code
names = code.co_names

op =(ord(c)for c in code.co_code)
globs = set()
extarg = 0

for c in op:
if c in GLOBAL_OPS:
globs.add(names [next(op)+ next(op)* 256 + extarg] )
elif c == EXTENDED_ARG:
extarg =(next(op)+ next(op)* 256)* 65536
continue
elif c> = HAVE_ARGUMENT:
next(op)
next(op)

extarg = 0

return sorted(globs)

print getglobals(f )#['y']


I am trying to analyze some messy code, that happens to use global variables quite heavily within functions (I am trying to refactor the code so that functions only use local variables). Is there any way to detect global variables within a function?

For example:

def f(x):
    x = x + 1
    z = x + y
    return z

Here the global variable is y since it isn't given as an argument, and neither is it created within the function.

I tried to detect global variables within the function using string parsing, but it was getting a bit messy; I was wondering if there was a better way to do this?

Edit: If anyone is interested this is the code I am using to detect global variables (based on kindall's answer and Paolo's answer to this question: Capture stdout from a script in Python):

from dis import dis

def capture(f):
    """
    Decorator to capture standard output
    """
    def captured(*args, **kwargs):
        import sys
        from cStringIO import StringIO

        # setup the environment
        backup = sys.stdout

        try:
            sys.stdout = StringIO()     # capture output
            f(*args, **kwargs)
            out = sys.stdout.getvalue() # release output
        finally:
            sys.stdout.close()  # close the stream 
            sys.stdout = backup # restore original stdout

        return out # captured output wrapped in a string

    return captured

def return_globals(f):
    """
    Prints all of the global variables in function f
    """
    x = dis_(f)
    for i in x.splitlines():
        if "LOAD_GLOBAL" in i:
            print i

dis_ = capture(dis)

dis_(f)

dis by default does not return output, so if you want to manipulate the output of dis as a string, you have to use the capture decorator written by Paolo and posted here: Capture stdout from a script in Python

解决方案

Inspect the bytecode.

from dis import dis
dis(f)

Result:

  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD
              7 STORE_FAST               0 (x)

  3          10 LOAD_FAST                0 (x)
             13 LOAD_GLOBAL              0 (y)
             16 BINARY_ADD
             17 STORE_FAST               1 (z)

  4          20 LOAD_FAST                1 (z)
             23 RETURN_VALUE

The global variables will have a LOAD_GLOBAL opcode instead of LOAD_FAST. (If the function changes any global variables, there will be STORE_GLOBAL opcodes as well.)

With a little work, you could even write a function that scans the bytecode of a function and returns a list of the global variables it uses. In fact:

from dis import HAVE_ARGUMENT, opmap

def getglobals(func):
    GLOBAL_OPS = opmap["LOAD_GLOBAL"], opmap["STORE_GLOBAL"]
    EXTENDED_ARG = opmap["EXTENDED_ARG"]

    func = getattr(func, "im_func", func)
    code = func.func_code
    names = code.co_names

    op = (ord(c) for c in code.co_code)
    globs = set()
    extarg = 0

    for c in op:
        if c in GLOBAL_OPS:
            globs.add(names[next(op) + next(op) * 256 + extarg])
        elif c == EXTENDED_ARG:
            extarg = (next(op) + next(op) * 256) * 65536
            continue
        elif c >= HAVE_ARGUMENT:
            next(op)
            next(op)

        extarg = 0

    return sorted(globs)

print getglobals(f)               # ['y']

这篇关于检测python函数中的所有全局变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆