<p>理想情况下,我希望我正在搜索的全局检查功能能够在IDE中实现,并持续用于评估函数中全局的使用情况。但由于这似乎不存在,我创建了一个特殊函数,它将python函数作为输入,然后查看该函数的字节码指令,以查看是否存在任何<code>LOAD_GLOBAL</code>或<code>STORE_GLOBAL</code>指令。如果找到任何类型,它将尝试评估全局类型,并将其与用户提供的类型列表(int、float等)进行比较。然后打印出函数使用的所有全局变量的名称。你知道吗</p>
<p>这个解决方案还远远不够完美,而且很容易出现误报。例如,如果在导入numpy(<code>import numpy as np</code>)之前在函数中使用<code>np.unique(x)</code>,它将错误地将<code>np</code>标识为全局变量而不是模块。它也不会研究嵌套函数等</p>
<p>但是对于一些简单的例子,比如本文中的例子,它似乎工作得很好。我只是用它来扫描我的代码库中的所有函数,它发现了另一个我不知道的全局用法-所以至少对我来说它是有用的!你知道吗</p>
<p>函数如下:</p>
<pre><code>def CheckAgainstGlobals(function, vartypes):
"""
Function for checking if another function reads/writes data from/to global
variables. Only variables of the types contained within 'vartypes' and
unknown types are included in the output.
Inputs:
function - a python function
vartypes - a list of variable types (int, float, dict,...)
Example:
# Define a function
def testfcn(a):
a = 1 + b
return a
# Check if the function read/writes global variables.
CheckAgainstGlobals(testfcn,[int, float, dict, complex, str])
# Should output:
>> Global-check of function: testfcn
>> Loaded global variable: b (of unknown type)
"""
import dis
globalsFound = []
# Disassemble the function's bytecode in a human-readable form.
bytecode = dis.Bytecode(function)
# Step through each instruction in the function.
for instr in bytecode:
# Check if instruction is to either load or store a global.
if instr[0] == 'LOAD_GLOBAL' or instr[0] == 'STORE_GLOBAL':
# Check if its possible to determine the type of the global.
try:
type(eval(instr[3]))
TypeAvailable = True
except:
TypeAvailable = False
"""
Determine if the global variable is being loaded or stored and
check if 'argval' of the global variable matches any of the
vartypes provided as input.
"""
if instr[0] == 'LOAD_GLOBAL':
if TypeAvailable:
for t in vartypes:
if isinstance(eval(instr[3]), t):
s = ('Loaded global variable: %s (of type %s)' %(instr[3], t))
if s not in globalsFound:
globalsFound.append(s)
else:
s = ('Loaded global variable: %s (of unknown type)' %(instr[3]))
if s not in globalsFound:
globalsFound.append(s)
if instr[0] == 'STORE_GLOBAL':
if TypeAvailable:
for t in vartypes:
if isinstance(eval(instr[3]), t):
s = ('Stored global variable: %s (of type %s)' %(instr[3], t))
if s not in globalsFound:
globalsFound.append(s)
else:
s = ('Stored global variable: %s (of unknown type)' %(instr[3]))
if s not in globalsFound:
globalsFound.append(s)
# Print out summary of detected global variable usage.
if len(globalsFound) == 0:
print('\nGlobal-check of fcn: %s. No read/writes of global variables were detected.' %(function.__code__.co_name))
else:
print('\nGlobal-check of fcn: %s' %(function.__code__.co_name))
for s in globalsFound:
print(s)
</code></pre>
<p>当在声明函数之后直接在示例中的函数上使用时,它将发现有关全局变量<code>SomeDict</code>的用法的警告,但不会知道其类型:</p>
<pre><code>def UnintentionalValueChangeOfGlobal(a):
SomeDict['SomeKey'] = 100 + a
b = 0.5 * SomeDict['SomeKey']
return b
# Will find the global, but not know its type.
CheckAgainstGlobals(UnintentionalValueChangeOfGlobal,[int, float, dict, complex, str])
>> Global-check of fcn: UnintentionalValueChangeOfGlobal
>> Loaded global variable: SomeDict (of unknown type)
</code></pre>
<p>在定义<code>SomeDict</code>之后使用时,它还检测到全局是dict:</p>
<pre><code>SomeDict = {}
SomeDict['SomeKey'] = 0
b = UnintentionalValueChangeOfGlobal(10)
print(SomeDict['SomeKey'])
# Will find the global, and also see its type.
CheckAgainstGlobals(UnintentionalValueChangeOfGlobal,[int, float, dict, complex, str])
>> Global-check of fcn: UnintentionalValueChangeOfGlobal
>> Loaded global variable: SomeDict (of type <class 'dict'>)
</code></pre>
<p>注意:在当前状态下,函数无法检测到<code>SomeDict['SomeKey']</code>更改值。也就是说,它只检测加载指令,而不检测全局的前一个值是否被操纵。这是因为在本例中使用指令<code>STORE_SUBSCR</code>而不是<code>STORE_GLOBAL</code>。但是全局的使用仍然被检测到(因为它正在被加载),这对我来说已经足够了。你知道吗</p>