我认为 Nae 的另一个答案总结了这一点;我不知道 Python 语言中有任何内置机制来检测分配,所以如果你想要一个类似中断的事件系统在分配时触发,我不知道它是否可行。
但是,您似乎很安静地决心要找到一种“检测”分配的方法,所以我想描述一种可能让您更接近的方法。
有内置函数globals() 和locals() 分别在全局和局部范围内创建变量字典。 (除了vars() 之外,还进一步解释了here)。
值得注意的一点是,如果从函数内部调用locals(),其行为会有所不同:
如果locals() 在inside 一个函数中被调用,它会在那个时刻构造函数命名空间的字典并返回它——任何进一步的名称分配都不反映在返回的字典中,并且任何到字典的分配不反映在实际的本地命名空间中
如果locals() 在外部 一个函数被调用,它会返回作为当前命名空间的实际字典。对命名空间的进一步更改反映在字典中,对字典的更改反映在命名空间中:
这是一种检测变量变化的“hacky”方法:
def b_is_modified():
print("b is modified!")
if __name__ == '__main__':
old = locals().get('b')
a = 3
b = 4
b = 5
new = locals().get('b')
if id(new) != id(old) and new is not None:
b_is_modified()
这不过是一种(混淆的?)检查b 的值是否已从执行点更改为另一点的方法,并且没有检测到它的回调事件或触发操作。但是,如果您想扩展此方法,请继续阅读。
答案的其余部分解释了如何通过将b 重写为以下内容来检查更改:
if __name__ == '__main__':
monitor = ScopeVariableMonitor(locals())
a = 3
b = 4
monitor.compare_and_update() # Detects creation of a and b
b = 5
monitor.compare_and_update() # Detects changes to b
以下内容将“检测”变量的任何更改,我还提供了一个在函数内部使用它的示例,以重申从 locals() 返回的字典不会更新。
ScopeVariableMonitor-class 只是一个示例,它将代码组合在一个地方。本质上,它是比较update()s 之间变量的存在和值的变化。
class ScopeVariableMonitor:
def __init__(self, scope_vars):
self.scope_vars = scope_vars # Save a locals()-dictionary instance
self.old = self.scope_vars.copy() # Make a shallow copy for later comparison
def update(self, scope_vars=None):
scope_vars = scope_vars or self.scope_vars
self.old = scope_vars.copy() # Make new shallow copy for next time
def has_changed(self, var_name):
old, new = self.old.get(var_name), self.scope_vars.get(var_name)
print('{} has changed: {}'.format(var_name, id(old) != id(new)))
def compare_and_update(self, var_list=None, scope_vars=None):
scope_vars = scope_vars or self.scope_vars
# Find new keys in the locals()-dictionary
new_variables = set(scope_vars.keys()).difference(set(self.old.keys()))
if var_list:
new_variables = [v for v in new_variables if v in var_list]
if new_variables:
print('\nNew variables:')
for new_variable in new_variables:
print(' {} = {}'.format(new_variable, scope_vars[new_variable]))
# Find changes of values in the locals()-dictionary (does not handle deleted vars)
changed_variables = [var_name for (var_name, value) in self.old.items() if
id(value) != id(scope_vars[var_name])]
if var_list:
changed_variables = [v for v in changed_variables if v in var_list]
if changed_variables:
print('\nChanged variables:')
for var in changed_variables:
print(' Before: {} = {}'.format(var, self.old[var]))
print(' Current: {} = {}\n'.format(var, scope_vars[var], self.old[var]))
self.update()
“有趣”的部分是compare_and_update()-方法,如果提供了变量名称列表,例如['a', 'b'],它只会查找对变量的更改。 scope_vars-参数在函数范围内时是必需的,但在全局范围内则不需要;原因如上所述。
def some_function_scope():
print('\n --- Now inside function scope --- \n')
monitor = ScopeVariableMonitor(locals())
a = 'foo'
b = 42
monitor.compare_and_update(['a', 'b'], scope_vars=locals())
b = 'bar'
monitor.compare_and_update(scope_vars=locals())
if __name__ == '__main__':
monitor = ScopeVariableMonitor(locals())
var_list = ['a', 'b']
a = 5
b = 10
c = 15
monitor.compare_and_update(var_list=var_list)
print('\n *** *** *** \n') # Separator for print output
a = 10
b = 42
c = 100
d = 1000
monitor.has_changed('b')
monitor.compare_and_update()
some_function_scope()
输出:
New variables:
a = 5
b = 10
*** *** ***
b has changed: True
New variables:
d = 1000
Changed variables:
Before: b = 10
Current: b = 42
Before: a = 5
Current: a = 10
Before: c = 15
Current: c = 100
--- Now inside function scope ---
New variables:
a = foo
b = 42
Changed variables:
Before: b = 42
Current: b = bar
结论
我的回答只是一种更一般的做法:
b = 1
old_b = b
# ...
if b != old_b:
print('b has been assigned to')
来自locals() 的字典将保存所有变量,包括函数和类;不仅仅是像 a、b 和 c 这样的“简单”变量。
在上面的实现中,“旧”和“新”值之间的检查是通过比较之前 shallow 副本的 id() 与当前值的 id() 来完成的。这种方法允许比较任何值,因为id() 将返回虚拟内存地址,但这远不是一个好的、通用的比较方案。
我很好奇您想要实现什么以及为什么要检测分配:如果您分享您的目标,那么也许我可以想出另一种方式以另一种方式实现它。