昨天我在我的Django应用程序时遇到一个错误,尽管我因为固定它,我还是不明白其原因,无论是我如何解决它。
嗯,其实我发现的根本原因,而写了这个问题,这要归功于SO“的问题有类似的标题”功能。见"Least Astonishment" and the Mutable Default Argument
Buuut,我还是不明白怎么会影响我的应用程序在它影响的方式。所以,让我们挖英寸
我有一个网页,该网页显示的项目清单。我通过Model.get
方法查询这些项目,从views.py文件。没有什么两样,基本上从模型获取数据库,调用视图中的模型,并提供可变所述提取值的模板。
当使用错误的源代码,我会刷新页面,该项目将随机出现两种或消失。我想通了DB查询将要么返回的项目,或者返回一个空列表。
下面是有故障的源代码(model.py):
@classmethod
def get(cls, school=None, additional_filters={}):
if school:
additional_filters['school'] = school
return MyModel.objects.filter(
**additional_filters
)
这里是我如何固定它:
@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告诉我有什么不对劲Default argument value is mutable
,因为我无法解释的漏洞可言,我跟着它的建议。
但我还是不明白为什么。即使是现在,看完后"Least Astonishment" and the Mutable Default Argument我还是不知道。
我现在明白了additional_filters
在每次调用修改由于到Python处理内存中的函数默认参数的方式。
我不解释是此行为的副作用。为什么查询返回任何适当的项目或一个空集?特别是考虑到该代码没有提供任何additional_filters
,在additional_filters
意味着只增加项目是school
它总是在每一个查询相同。
这就是我真的不明白的部分。我对这种方法的所有电话都是形式Model.get(request.context.school)
,自此additional_filters
是地图,而不是一个数组,它应该永远都包含在相同的值。
这个bug花了我一段时间来弄清楚,因为我无法重现它在我的当地环境,也没有临时环境,它仅影响生产环境,并使其非常非常难以定位。
FCGI维护的过程池。每个池的成员有一个空的字典作为默认关键字参数。 你最初是幸运的,请求到达与不变空字典的过程。但每当请求达到其已经改变了这本词典的过程中,那么它是不是一个空的字典了 - 你的过滤器积累。