检测python函数中的所有全局变量? [英] Detect all global variables within a python function?
问题描述
例如:
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屋!