WEBVTT 1 00:00:00.150 --> 00:00:01.790 Hi and welcome back. 2 00:00:01.790 --> 00:00:04.660 Mutable default arguments, 3 00:00:04.660 --> 00:00:08.480 it's a terrible idea, so here's a common pitfall in Python 4 00:00:08.480 --> 00:00:10.260 that you definitely want to avoid 5 00:00:10.260 --> 00:00:13.010 and that's the mutable default arguments. 6 00:00:13.010 --> 00:00:14.240 You know what mutable means 7 00:00:14.240 --> 00:00:16.120 and you know what default argument means, 8 00:00:16.120 --> 00:00:17.670 so let's join them both together 9 00:00:17.670 --> 00:00:19.633 and let's take this function here. 10 00:00:20.610 --> 00:00:22.507 I'm gonna say, create_account 11 00:00:23.440 --> 00:00:25.000 and it's gonna make a name of the account, 12 00:00:25.000 --> 00:00:26.470 which is gonna be a string, 13 00:00:26.470 --> 00:00:28.040 an account holder, which is going to be a string, 14 00:00:28.040 --> 00:00:30.570 that's probably the name of the account opener 15 00:00:30.570 --> 00:00:33.830 and also, account holders, which is going to be a list 16 00:00:33.830 --> 00:00:36.723 and it's going to equal an empty list at the start. 17 00:00:37.970 --> 00:00:40.300 So, the logic behind this is that 18 00:00:40.300 --> 00:00:42.770 you're gonna create a new account with this name, 19 00:00:42.770 --> 00:00:44.750 this is gonna be the account holder 20 00:00:44.750 --> 00:00:48.030 and it can have also other account holders as well 21 00:00:48.030 --> 00:00:50.000 and they'll be coming through in this list here, 22 00:00:50.000 --> 00:00:52.660 so you can give it a list of account holders, 23 00:00:52.660 --> 00:00:55.477 so we'll say, account_holders.append(holder). 24 00:00:57.460 --> 00:01:00.350 By default, when you create a new account, 25 00:01:00.350 --> 00:01:02.370 you'll append the holder of the account 26 00:01:02.370 --> 00:01:05.550 that you just added over to the account holders list 27 00:01:05.550 --> 00:01:08.460 and if there are any other account holders in that list, 28 00:01:08.460 --> 00:01:10.060 you will also use them. 29 00:01:10.060 --> 00:01:10.893 And then we're gonna return 30 00:01:10.893 --> 00:01:13.730 a dictionary representing our account, 31 00:01:13.730 --> 00:01:14.990 so that's gonna be the name of the account, 32 00:01:14.990 --> 00:01:16.333 the main account holder, 33 00:01:17.940 --> 00:01:20.690 that's gonna be the holder parameter here 34 00:01:20.690 --> 00:01:23.297 and then, it's gonna be account_holders, 35 00:01:23.297 --> 00:01:25.640 it's going to be the account holders list. 36 00:01:25.640 --> 00:01:29.120 Again, how we call this is we have, for example, 37 00:01:29.120 --> 00:01:32.197 an account one, let's create account, checking account 38 00:01:32.197 --> 00:01:35.030 and the holder is Rolf. 39 00:01:35.030 --> 00:01:37.320 Notice how we don't have to pass an argument 40 00:01:37.320 --> 00:01:38.460 to the account holders parameter 41 00:01:38.460 --> 00:01:41.083 because it has a default value, which is this list, 42 00:01:41.950 --> 00:01:44.730 so what's gonna happen is the name is gonna be checking, 43 00:01:44.730 --> 00:01:46.740 the holder's gonna be Rolf 44 00:01:46.740 --> 00:01:48.570 and to the account holders, which is an empty list, 45 00:01:48.570 --> 00:01:50.250 we're going to append Rolf, 46 00:01:50.250 --> 00:01:52.940 because that's the only holder in the account 47 00:01:52.940 --> 00:01:54.110 and then we're gonna return a dictionary, 48 00:01:54.110 --> 00:01:56.340 which is the main account holder, which is Rolf, 49 00:01:56.340 --> 00:01:57.420 and a list of account holders, 50 00:01:57.420 --> 00:01:59.690 which, at the moment, only includes Rolf. 51 00:01:59.690 --> 00:02:02.290 If we wanted to have multiple account holders, 52 00:02:02.290 --> 00:02:03.710 we could do this for example 53 00:02:03.710 --> 00:02:06.450 and then the account holders would be Jen and Rolf, 54 00:02:06.450 --> 00:02:09.610 because we append the opener, the main account holder 55 00:02:09.610 --> 00:02:12.140 over to the list of account holders. 56 00:02:12.140 --> 00:02:13.780 But let's say we don't want 57 00:02:13.780 --> 00:02:16.570 to pass in an argument to this list, 58 00:02:16.570 --> 00:02:17.760 we just want to create an account 59 00:02:17.760 --> 00:02:19.150 and this'll be handled for us, 60 00:02:19.150 --> 00:02:20.800 it would put Rolf into this list. 61 00:02:22.630 --> 00:02:24.950 Then let's create another account, 62 00:02:24.950 --> 00:02:26.930 let's say a savings account, but this time, 63 00:02:26.930 --> 00:02:30.610 Jen is the owner of the account and let's imagine 64 00:02:30.610 --> 00:02:33.230 they've got some sort of shared finances there 65 00:02:33.230 --> 00:02:35.140 and we choose the owner of the account 66 00:02:35.140 --> 00:02:36.740 and she's the only holder in that account, 67 00:02:36.740 --> 00:02:39.090 let's say Rolf sends her the money every month. 68 00:02:42.180 --> 00:02:43.730 a2 and a1 are both dictionaries, 69 00:02:43.730 --> 00:02:45.220 that's what this function returns, 70 00:02:45.220 --> 00:02:48.120 what are we going to see when we print a2? 71 00:02:48.120 --> 00:02:49.760 This is a trick question, of course, I know 72 00:02:49.760 --> 00:02:51.620 that you already know about functions and dictionaries, 73 00:02:51.620 --> 00:02:52.783 so why would I ask you this? 74 00:02:52.783 --> 00:02:55.970 This is a trick question, what is it gonna return? 75 00:02:55.970 --> 00:02:58.060 And important, try this out first 76 00:02:58.060 --> 00:02:59.310 if you don't want to be wrong. 77 00:02:59.310 --> 00:03:00.763 Hopefully you tried that out. 78 00:03:02.530 --> 00:03:05.310 As you can see here, what we see is the name is savings, 79 00:03:05.310 --> 00:03:07.400 the main account holder is Jen, 80 00:03:07.400 --> 00:03:10.253 but the account holders are both Rolf and Jen, 81 00:03:11.790 --> 00:03:13.053 so how did that happen? 82 00:03:13.930 --> 00:03:16.993 This function here never saw Rolf anywhere, 83 00:03:18.050 --> 00:03:19.530 Rolf didn't get passed to the function, 84 00:03:19.530 --> 00:03:21.280 it's not in any of the arguments, 85 00:03:21.280 --> 00:03:25.303 I've not hard-coded Rolf in here, so how did this happen? 86 00:03:26.930 --> 00:03:31.930 Well, what happened here is that the default parameter 87 00:03:32.300 --> 00:03:34.830 for the create_account function gets evaluated 88 00:03:34.830 --> 00:03:36.830 when the function is defined, 89 00:03:36.830 --> 00:03:40.590 not when the function is called, so this list here 90 00:03:40.590 --> 00:03:45.590 and what this parameter points to by default is this object, 91 00:03:47.420 --> 00:03:50.010 so this gets created when the function gets created, 92 00:03:50.010 --> 00:03:51.763 not when the function is called. 93 00:03:52.690 --> 00:03:54.150 I'll make this clearer for you, 94 00:03:54.150 --> 00:03:58.420 I'm going to, here, bring the id of account_holders 95 00:03:58.420 --> 00:04:01.130 and run this again and notice how, 96 00:04:01.130 --> 00:04:04.343 in both times we run the function, the id is the same. 97 00:04:05.760 --> 00:04:09.810 When we didn't pass an argument here, 98 00:04:09.810 --> 00:04:13.320 we used this list, we didn't use a new list, 99 00:04:13.320 --> 00:04:16.490 we used this list and that was defined 100 00:04:16.490 --> 00:04:18.070 when the function was defined, 101 00:04:18.070 --> 00:04:19.890 so it's always the same list 102 00:04:19.890 --> 00:04:22.190 for every time the function gets called, 103 00:04:22.190 --> 00:04:25.850 so when we modify the list, when we mutate it, 104 00:04:25.850 --> 00:04:27.030 that is still the same list 105 00:04:27.030 --> 00:04:28.030 the next time the function calls, 106 00:04:28.030 --> 00:04:30.050 so now it contains Rolf, first of all, 107 00:04:30.050 --> 00:04:32.220 then it's gonna modify to contain Jen again 108 00:04:32.220 --> 00:04:33.130 and if we do this again, 109 00:04:33.130 --> 00:04:35.030 it will be Rolf, Jen and someone else. 110 00:04:36.380 --> 00:04:38.670 So, there are two ways to solve this problem, 111 00:04:38.670 --> 00:04:40.670 this is a common problem, by the way, 112 00:04:40.670 --> 00:04:42.410 and not one that a lot of people realise 113 00:04:42.410 --> 00:04:43.590 and a lot of people start doing this 114 00:04:43.590 --> 00:04:46.210 and then they're like, what the hell is going on in my code? 115 00:04:46.210 --> 00:04:47.420 I do not understand it. 116 00:04:47.420 --> 00:04:49.463 So, two ways to solve this problem. 117 00:04:50.305 --> 00:04:53.870 The first one, of course, is to not have a default argument, 118 00:04:53.870 --> 00:04:57.030 simple enough, don't have a default argument, 119 00:04:57.030 --> 00:05:01.610 instead, pass in an empty list here, these empty lists 120 00:05:01.610 --> 00:05:03.490 get evaluated when the function gets called, 121 00:05:03.490 --> 00:05:06.160 so they are going to be empty every time, 122 00:05:06.160 --> 00:05:08.160 there's a new empty list, new empty list 123 00:05:09.620 --> 00:05:10.733 and option of course. 124 00:05:11.840 --> 00:05:16.840 The second option is to not make this a list, 125 00:05:17.120 --> 00:05:18.863 but make it equal to none, 126 00:05:20.930 --> 00:05:22.430 then you don't have to pass anything here, 127 00:05:22.430 --> 00:05:23.570 because it has a default argument, 128 00:05:23.570 --> 00:05:26.450 but of course, it's none, so you can't append to it, 129 00:05:26.450 --> 00:05:28.013 so then, what you'll do is you'll say, 130 00:05:29.576 --> 00:05:33.060 if not account_holders, so if account_holders is none, 131 00:05:33.060 --> 00:05:35.400 then say, account_holders is now a list 132 00:05:36.290 --> 00:05:38.180 and then you can append to it. 133 00:05:38.180 --> 00:05:39.970 So, what does that do? 134 00:05:39.970 --> 00:05:44.220 If you don't pass a list in here, it'll create a new one 135 00:05:44.220 --> 00:05:47.890 and if you do pass a list in here, say, for example, Jen, 136 00:05:47.890 --> 00:05:50.450 it will use this list and it will append to it 137 00:05:50.450 --> 00:05:53.410 and the account holders will end up being the holder 138 00:05:53.410 --> 00:05:55.653 and whomever else you defined in here. 139 00:05:56.730 --> 00:05:58.000 So those are your two options 140 00:05:58.000 --> 00:06:00.930 to avoid mutable argument problems, 141 00:06:00.930 --> 00:06:02.730 mutable arguments are, of course, 142 00:06:02.730 --> 00:06:05.440 any datatype that is mutable. 143 00:06:05.440 --> 00:06:07.250 You can have dictionaries, lists, 144 00:06:07.250 --> 00:06:11.460 you can have objects of your own types and so forth. 145 00:06:11.460 --> 00:06:14.150 If you want to avoid these problems, 146 00:06:14.150 --> 00:06:18.150 use immutable datatypes as your default values, 147 00:06:18.150 --> 00:06:22.730 integers, strings, floats, tupples and so on, 148 00:06:22.730 --> 00:06:24.670 that way, you cannot change one 149 00:06:24.670 --> 00:06:27.653 and it won't change across executions of the function. 150 00:06:28.630 --> 00:06:29.690 So that's it for this video, 151 00:06:29.690 --> 00:06:30.860 I just wanted to make you aware 152 00:06:30.860 --> 00:06:33.330 of this very, very common pitfall 153 00:06:33.330 --> 00:06:36.080 so that you don't fall for this trap. 154 00:06:36.080 --> 00:06:38.380 So that's it and I'll see you on the next one.