Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Letting Gemini Explain the Bug

00:00 Gemini spotted a critical bug in our todolist application, and we asked it to give us a better explanation about why that’s an issue. So it points you to the exporter.py file and then looking at the JSONExporter class.

00:18 So there you have the .__init__() method that has the arguments, self, output, and options, and the options parameter has a default value of two curly braces {}.

00:30 That means an empty dictionary. And now Gemini tells you what the problem with this is. In Python, default arguments are created only once when the function is first defined, not each time it’s called.

00:43 The options parameter’s default value is an empty dictionary, and because the dictionary is a mutable object, in other words, it can be changed, the same dictionary object is shared across all calls to JSONExporter that don’t explicitly provide their own options argument.

01:01 So that paragraph is a bit abstract, but the next paragraph makes it a bit more clear why this is a problem. If you create multiple JSONExporter instances without specifying options, they will all share the exact same options dictionary. Modifying the options of one instance will then unexpectedly change the options for all other instances that share that default.

01:25 And then Gemini goes on with a simple example. This example is interesting because it now doesn’t refer to our class that we are having there with the .__init__() method, but it kind of like takes this and puts it into a more basic example with a normal function and not a dictionary, but an empty list as a default argument.

01:46 But in general, it works the same. And I kind of like that it abstracts this idea. Let’s have a look at this simple example. So if you have a function add_item() where you can add an item, and then my_list is another parameter with an empty list as a default argument, and inside the function you are appending the item to the list and then printing the list.

02:09 What happens if you add an item apple and an item banana? The first call works as you might expect, the output is a list with apple in it.

02:21 But now when you call add_item() with banana as the argument and not providing a list, then you might expect, again, like a list with only banana in it because you are not providing any list.

02:33 And you say like, well, if I don’t provide a list, it should start with an empty list, right? So with the second call, the list from the first call is reused, and the output is apple and banana in the list.

02:45 So if you’re curious, you can try out this code example yourself, and maybe you have even run into a bug like this. It’s a little bit annoying, and it’s not uncommon that something happens, especially in bigger codebases.

02:59 What is the solution to that? The correct pattern is to use an immutable default value like None, and then create a new dictionary inside the function if no options are provided.

03:11 So the fix should look like this. If you have a look at the right side of the parameter list, then you have the options argument with the default value of None.

03:21 And then in line seven, if options is None, then you create a new dictionary. That way you ensure that the JSONExporter instance has its own independent options dictionary and doesn’t hand it over to other instances.

03:36 So now with this knowledge, you could go ahead and implement these changes yourself, but hey, where’s the fun in this? Let’s hop over to the next lesson and use Gemini to implement those changes.

Become a Member to join the conversation.