Python/Django random DB query results due to “Default argument value is mutable” in method
昨天我在我的django应用程序中遇到了一个bug,尽管我修复了它,但我仍然不理解它的原因,也不知道如何解决它。
实际上,我在写这个问题的时候找到了根本原因,这要归功于"标题相似的问题"功能。参见"最小惊异"和可变默认参数
但是,我仍然不明白这会以什么方式影响我的应用程序。所以,让我们深入研究。
我有一个网页,它显示一个项目列表。我通过views.py文件中的
当使用错误的源代码时,我将刷新页面,这些项将随机出现或消失。我认为数据库查询要么返回项目,要么返回空列表。
以下是错误的源代码(model.py):
1 2 3 4 5 6 7 8 | @classmethod def get(cls, school=None, additional_filters={}): if school: additional_filters['school'] = school return MyModel.objects.filter( **additional_filters ) |
以下是我修复它的方法:
1 2 3 4 5 6 7 8 9 10 11 | @classmethod def get(cls, school=None, additional_filters=None): if not additional_filters: additional_filters = {} if school: additional_filters['school'] = school return MyModel.objects.filter( **additional_filters ) |
我这样做是因为pycharm ide告诉我
但我还是不明白为什么。即使现在,在阅读了"最不惊讶"和可变的默认参数之后,我仍然没有。
现在我明白了,由于Python处理内存中默认函数参数的方式,每次调用时都会修改
我不能解释的是这种行为的副作用。为什么查询返回了正确的项或空集?特别是考虑到代码没有提供任何
我真的不明白这一点。我对这个方法的所有调用都是
这个bug花了我一段时间才弄清楚,因为我不能在本地环境或登台环境中复制它,它只影响生产环境,使它很难定位。
fcgi维护一个进程池。每个池成员都有一个空dict作为默认关键字参数。您最初很幸运,请求通过未更改的空dict到达进程。但是每当一个请求到达已经修改了字典的进程时,它就不再是一个空的dict了——您的过滤器会累积起来。