Bitten by Python's Pass-By-Reference
I just got bitten by an interesting bug related to Python's pass-by-reference feature. I had a class method (my_method) with a keyword argument (my_kwarg) that defaulted to {}. I thought that each time my_method was called, if my_kwarg wasn't passed then it would default to an empty dict.
In reality, when the class was read by Python it evaluated the {} and made the default value a reference to the dict in memory. So each time my_method was called without passing my_kwarg, it was defaulting to that memory reference which contained data from a previous use. Here's an example class:
class MyClass(object): def my_method(self, my_kwarg={}): return my_kwarg
And here's an iPython shell session illustrating what I mean:
In [2]: first_class = MyClass() In [3]: kwarg = first_class.my_method() In [4]: kwarg Out[4]: {} In [5]: kwarg.update({'foo': 'bar'}) In [6]: kwarg Out[6]: {'foo': 'bar'} In [7]: kwarg = first_class.my_method() In [8]: kwarg Out[8]: {'foo': 'bar'} In [9]: second_class = MyClass() In [10]: other_kwarg = second_class.my_method() In [11]: other_kwarg Out[11]: {'foo': 'bar'}
The fact that the kwarg wasn't defaulting to a fresh dict each time led to a lot of confusion before I realized what was happening! There are a couple of ways to solve the issue, but the easiest is probably this:
class MyClass(object): def my_method(self, my_kwarg=None): my_kwarg = my_kwarg or {} return my_kwarg
Comments
Brandon Konkle
I've been creating websites for over 10 years, and I've been using Django since early 2008. I focus on high quality, well-tested, maintainable code and reliable high-performance deployments. Web development is something that I am very excited about, and I love finding elegant and innovative ways to push web applications further.