我怎样才能制作检查包来改变调用者当地人
How can I make inspect package to alter caller locals
我正在尝试编写一个 save/load 命令,就像 MATLAB 中的命令一样(能够将局部变量保存到磁盘或将它们加载到当前上下文中,或者在 MATLAB 的术语中工作 space)。
我写了下面的代码,但是好像不行,因为外部作用域的变量没有被替换,可能是某处发生了内存拷贝。
代码如下:
import shelve
import logging
import inspect
logger = logging.getLogger()
def save_locals(filename, keys=None):
my_shelf = shelve.open(filename, 'n') # 'n' for new
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = caller_locals.keys()
for key in keys:
try:
my_shelf[key] = caller_locals[key]
except TypeError:
#
# __builtins__, my_shelf, and imported modules can not be shelved.
#
print('ERROR shelving: {0}'.format(key))
my_shelf.close()
def load_locals(filename, keys=None):
my_shelf = shelve.open(filename)
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = list(my_shelf.keys())
for key in keys:
try:
caller_locals[key] = my_shelf[key]
except ValueError:
print('cannot get variable %s'.format(key))
这是失败的测试:
from unittest import TestCase
from .io import save_locals, load_locals
class TestIo(TestCase):
def test_save_load(self):
sanity = 'sanity'
an_int = 3
a_float = 3.14
a_list = [1, 2, 3]
a_dict = [{'a': 5, 'b': 3}]
save_locals('temp')
an_int = None
a_float = None
a_list = None
a_dict = None
load_locals('temp')
self.assertIn('an_int', locals())
self.assertIn('a_float', locals())
self.assertIn('a_list', locals())
self.assertIn('a_dict', locals())
self.assertEqual(an_int, 3)
self.assertEqual(a_float, 3.14)
self.assertEqual(a_list, [1, 2, 3])
self.assertEqual(a_dict, [{'a': 5, 'b': 3}])
当我在 load_locals
内部断点时,我可以看到它改变了 f_locals
字典,但是当函数 returns 它们没有改变时。
不,您不能即时更新局部变量。原因是因为局部符号 table 被保存为 C 数组以进行优化,并且 locals()
和 frame.f_locals
最终都返回了局部符号 table 的副本。官方的回应是修改 locals() 有未定义的行为。 This 线程对此进行了一些讨论。
它最终变得格外奇怪,因为每次调用 locals()
或 frame.f_locals
returns 相同的字典,它会在不同的时间重新同步。这里只是调用 frame.f_locals
重置本地
def test_locals():
frame = inspect.stack()[1][0]
caller_locals = frame.f_locals
caller_locals['an_int'] = 5
print(caller_locals)
_ = frame.f_locals
print(caller_locals)
def call_test_locals():
an_int = 3
test_locals()
call_test_locals()
输出:
{'an_int': 5}
{'an_int': 3}
行为将取决于 Python 实现和可能的其他边缘情况,但有几个示例,其中 (1) 变量已定义但未更新; (2) 变量未定义并被更新; (3) 定义变量,随后删除,不更新。
def test_locals():
frame = inspect.stack()[1][0]
caller_locals = frame.f_locals
caller_locals['an_int'] = 5
def call_test_locals1():
an_int = 3
print('calling', locals())
test_locals()
print('done', locals())
def call_test_locals2():
print('calling', locals())
test_locals()
print('done', locals())
def call_test_locals3():
an_int = 3
del an_int
print('calling', locals())
test_locals()
print('done', locals())
print('\n1:')
call_test_locals1()
print('\n2:')
call_test_locals2()
print('\n3:')
call_test_locals3()
输出:
1:
calling {'an_int': 3}
done {'an_int': 3}
2:
calling {}
done {'an_int': 5}
3:
calling {}
done {}
如果您是 运行 Python 2,您可以使用 exec
将字符串执行到本地命名空间,但它在 Python 中不起作用3,一般来说可能是个坏主意。
import shelve
import logging
import inspect
logger = logging.getLogger()
def save_locals(filename, keys=None):
my_shelf = shelve.open(filename, 'n') # 'n' for new
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = caller_locals.keys()
for key in keys:
try:
my_shelf[key] = caller_locals[key]
except TypeError:
#
# __builtins__, my_shelf, and imported modules can not be shelved.
#
print('ERROR shelving: {0}'.format(key))
my_shelf.close()
def load_locals_string(filename, keys=None):
my_shelf = shelve.open(filename)
if keys is None:
keys = list(my_shelf.keys())
return ';'.join('{}={!r}'.format(key, my_shelf[key]) for key in keys)
和
from unittest import TestCase
from .io import save_locals, load_locals
class TestIo(TestCase):
def test_save_load(self):
sanity = 'sanity'
an_int = 3
a_float = 3.14
a_list = [1, 2, 3]
a_dict = [{'a': 5, 'b': 3}]
save_locals('temp')
an_int = None
a_float = None
a_list = None
a_dict = None
exec load_locals_string('temp')
self.assertIn('an_int', locals())
self.assertIn('a_float', locals())
self.assertIn('a_list', locals())
self.assertIn('a_dict', locals())
self.assertEqual(an_int, 3)
self.assertEqual(a_float, 3.14)
self.assertEqual(a_list, [1, 2, 3])
self.assertEqual(a_dict, [{'a': 5, 'b': 3}])
在Python2中,exec
使用PyFrame_LocalsToFast
将变量复制回局部作用域,但在Python3中不能,因为exec
是一个函数。 Martijn Pieters 有一个很好的 post 关于它。
我正在尝试编写一个 save/load 命令,就像 MATLAB 中的命令一样(能够将局部变量保存到磁盘或将它们加载到当前上下文中,或者在 MATLAB 的术语中工作 space)。
我写了下面的代码,但是好像不行,因为外部作用域的变量没有被替换,可能是某处发生了内存拷贝。
代码如下:
import shelve
import logging
import inspect
logger = logging.getLogger()
def save_locals(filename, keys=None):
my_shelf = shelve.open(filename, 'n') # 'n' for new
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = caller_locals.keys()
for key in keys:
try:
my_shelf[key] = caller_locals[key]
except TypeError:
#
# __builtins__, my_shelf, and imported modules can not be shelved.
#
print('ERROR shelving: {0}'.format(key))
my_shelf.close()
def load_locals(filename, keys=None):
my_shelf = shelve.open(filename)
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = list(my_shelf.keys())
for key in keys:
try:
caller_locals[key] = my_shelf[key]
except ValueError:
print('cannot get variable %s'.format(key))
这是失败的测试:
from unittest import TestCase
from .io import save_locals, load_locals
class TestIo(TestCase):
def test_save_load(self):
sanity = 'sanity'
an_int = 3
a_float = 3.14
a_list = [1, 2, 3]
a_dict = [{'a': 5, 'b': 3}]
save_locals('temp')
an_int = None
a_float = None
a_list = None
a_dict = None
load_locals('temp')
self.assertIn('an_int', locals())
self.assertIn('a_float', locals())
self.assertIn('a_list', locals())
self.assertIn('a_dict', locals())
self.assertEqual(an_int, 3)
self.assertEqual(a_float, 3.14)
self.assertEqual(a_list, [1, 2, 3])
self.assertEqual(a_dict, [{'a': 5, 'b': 3}])
当我在 load_locals
内部断点时,我可以看到它改变了 f_locals
字典,但是当函数 returns 它们没有改变时。
不,您不能即时更新局部变量。原因是因为局部符号 table 被保存为 C 数组以进行优化,并且 locals()
和 frame.f_locals
最终都返回了局部符号 table 的副本。官方的回应是修改 locals() 有未定义的行为。 This 线程对此进行了一些讨论。
它最终变得格外奇怪,因为每次调用 locals()
或 frame.f_locals
returns 相同的字典,它会在不同的时间重新同步。这里只是调用 frame.f_locals
重置本地
def test_locals():
frame = inspect.stack()[1][0]
caller_locals = frame.f_locals
caller_locals['an_int'] = 5
print(caller_locals)
_ = frame.f_locals
print(caller_locals)
def call_test_locals():
an_int = 3
test_locals()
call_test_locals()
输出:
{'an_int': 5}
{'an_int': 3}
行为将取决于 Python 实现和可能的其他边缘情况,但有几个示例,其中 (1) 变量已定义但未更新; (2) 变量未定义并被更新; (3) 定义变量,随后删除,不更新。
def test_locals():
frame = inspect.stack()[1][0]
caller_locals = frame.f_locals
caller_locals['an_int'] = 5
def call_test_locals1():
an_int = 3
print('calling', locals())
test_locals()
print('done', locals())
def call_test_locals2():
print('calling', locals())
test_locals()
print('done', locals())
def call_test_locals3():
an_int = 3
del an_int
print('calling', locals())
test_locals()
print('done', locals())
print('\n1:')
call_test_locals1()
print('\n2:')
call_test_locals2()
print('\n3:')
call_test_locals3()
输出:
1:
calling {'an_int': 3}
done {'an_int': 3}
2:
calling {}
done {'an_int': 5}
3:
calling {}
done {}
如果您是 运行 Python 2,您可以使用 exec
将字符串执行到本地命名空间,但它在 Python 中不起作用3,一般来说可能是个坏主意。
import shelve
import logging
import inspect
logger = logging.getLogger()
def save_locals(filename, keys=None):
my_shelf = shelve.open(filename, 'n') # 'n' for new
caller_locals = inspect.stack()[1][0].f_locals
if keys is None:
keys = caller_locals.keys()
for key in keys:
try:
my_shelf[key] = caller_locals[key]
except TypeError:
#
# __builtins__, my_shelf, and imported modules can not be shelved.
#
print('ERROR shelving: {0}'.format(key))
my_shelf.close()
def load_locals_string(filename, keys=None):
my_shelf = shelve.open(filename)
if keys is None:
keys = list(my_shelf.keys())
return ';'.join('{}={!r}'.format(key, my_shelf[key]) for key in keys)
和
from unittest import TestCase
from .io import save_locals, load_locals
class TestIo(TestCase):
def test_save_load(self):
sanity = 'sanity'
an_int = 3
a_float = 3.14
a_list = [1, 2, 3]
a_dict = [{'a': 5, 'b': 3}]
save_locals('temp')
an_int = None
a_float = None
a_list = None
a_dict = None
exec load_locals_string('temp')
self.assertIn('an_int', locals())
self.assertIn('a_float', locals())
self.assertIn('a_list', locals())
self.assertIn('a_dict', locals())
self.assertEqual(an_int, 3)
self.assertEqual(a_float, 3.14)
self.assertEqual(a_list, [1, 2, 3])
self.assertEqual(a_dict, [{'a': 5, 'b': 3}])
在Python2中,exec
使用PyFrame_LocalsToFast
将变量复制回局部作用域,但在Python3中不能,因为exec
是一个函数。 Martijn Pieters 有一个很好的 post 关于它。