我不会提供一个可复制的例子,因为对于这个特定的问题,我不能用一个小的伪例子来复制这个问题。然而,我相信发生了一些奇怪的事情,对我来说,这不是一个简单的拼写错误。也许这个问题会敲响警钟,也许有人会有一个解释。
让我们考虑一下我的项目的3个文件:
cli/main.py
main.py
nfb.py
在main.py
中,设置字典是从.json文件加载的。这个字典中的一个特定值是settings = {'ONLINE':{'APPLY_REJECTION': True}}
。然后,它从cli/main.py
文件中调用main
函数,该文件的参数是设置字典。
main.py
from cli.main import main
settings = load_settings(fname)
main(settings)
正如您可能从首字母缩写CLI中猜到的那样,调用的函数是一个简单的命令行界面,在该界面中,用户可以获得X种可能性(例如,键从1到9的9(,并根据输入调用另一个函数。然后重复输入阶段,直到按下退出键,例如0。
cli/main.py
def main(settings):
while True:
selection = input_menu(options)
if selection == 0:
break # exit key
elif selection == 1
x, y = nfb.run(a, b, c, d, settings)
这是非常示意性的。显然,在我的代码中,对输入进行了错误检查,定义了变量。。。然而,需要注意的一点是,字典settings
是从nfb.py
文件传递给run函数的。
在nfb.py
中,函数运行可能会将变量settings['ONLINE']['APPLY_REJECTION']
从True修改为False。一旦函数退出,程序就在等待新的用户输入(选择(,因为我们已经离开了nfb.run()
的范围,变量settings['ONLINE']['APPLY_REJECTION']
应该会返回True,以便将来调用nfb.run()
。
然而,这并不是我所观察到的。变量settings['ONLINE']['APPLY_REJECTION']
保持为False,在nfb.run()
的范围之外。
更令人惊讶的是,我在nfb.py
:中尝试过这样做
import copy
def run(a, b, c, d, settings):
backup_settings = copy.deepcopy(settings)
# do stuff
if condition:
settings['ONLINE']['APPLY_REJECTION'] = False
# restore settings
settings = backup_settings
通过几次打印,我可以确认在# restore settings
之前,变量settings['ONLINE']['APPLY_REJECTION']
被设置为False,在函数的最后一行settings = backup_settings
之后,变量settings['ONLINE']['APPLY_REJECTION']
被设置为True。
但是,如果在CLI的while循环中,我在选择(即用户输入(之前或之后打印相同的变量,我可以看到,在nfb.run()
函数中将该变量设置为False后,该变量仍设置为False,尽管有备份/恢复行!
cli/main.py
def main(settings):
while True:
print (settings['ONLINE']['APPLY_REJECTION'])
selection = input_menu(options)
if selection == 0:
break # exit key
elif selection == 1
x, y = nfb.run(a, b, c, d, settings)
上面的代码将第一次打印True,一旦nfb.run()
将变量设置为False,则打印False。
需要注意的是,绝对没有其他地方可以修改这个变量。此外,我对该问题的解决方案是在run
开始时从字典中提取变量,然后使用该提取变量,从而避免更改设置字典中的任何内容。
解决方法:
def run(a, b, c, d, settings):
apply_rejection = settings['ONLINE']['APPLY_REJECTION']
# do stuff
if condition:
apply_rejection = False
这个变通方法有效(没有其他更改!(,并表明应用于此变量settings['ONLINE']['APPLY_REJECTION']
的修改是nfb.run()
中的修改,而且正如我所声称的,没有其他行直接干扰任何地方的设置字典。
讨论:
在我看来,在我最初的方法中,nfb.run()
内部的settings
字典指向与外部的对象相同的对象,作为cli.main(settings)
函数的参数加载。更令人惊讶的是,一旦nfb.run()
中的变量settings
被重新分配了一个新值(在我的情况下是原始对象的深度副本(,这种联系似乎就被打破了。
有人关注过这篇长文,对这种行为有想法、有解释吗?
spam = {'foo':'bar'}
def eggs(arg):
arg['foo'] = 'baz'
print(spam)
eggs(spam)
print(spam)
输出
{'foo': 'bar'}
{'foo': 'baz'}
dict
是可变类型。我分享的链接准确地解释了这一点,尽管那里的示例具有不同的可变类型list
。