-1

I create an empty list at the top level. Then, inside a function, I populate that list with single-char strings, and remove duplicates from the list.

knownBad = []

def main():
    badTemp = input("Enter all letters not in the word: ")
    for i in badTemp:
        knownBad.append(i)
    knownBad = list(dict.fromkeys(knownBad))#removes duplicates
    print(knownBad)

main()

When I do this, I get the Traceback error:

Traceback (most recent call last):
  File "c:\Users\mvery\OneDrive - Diocese of Greensburg Schools\Desktop\Python\demo.py", line 10, in <module>
    main()
  File "c:\Users\mvery\OneDrive - Diocese of Greensburg Schools\Desktop\Python\demo.py", line 6, in main     
    knownBad.append(i)
UnboundLocalError: local variable 'knownBad' referenced before assignment

This seems very strange, considering that the 'knownBad' variable is actually a list, which is mutable structures. The list 'knownBad' isn't really being 'assigned' in the function, as it's been created at the top level.

Is there something stupid I'm missing? Is line 7 just a very wrong way of going about stripping duplicates from a list? Could it maybe have something to do with creating a dictionary from the list, and then using that dictionary's fromkeys() method to alter the list?

I've discovered that the line that checks the list items for duplicates (line 7) seems to be causing the problem. When it's commented out, the error doesn't appear, and the program resolves as expected (of course without removing duplicates). I see how line 7 is a type of assignment to the 'knownBad' list, but the list still already exists. It isn't like the list is being created at line 7.

I found two solutions to the problem here, but I don't understand why the problem is occurring in the first place. This seems to fly in the face of the mutability of lists.

Thanks for any help!

mvery
  • 9
  • 1
  • 5
  • Ok, THAT helps me understand the mutability problem a little better! It sounds like you're saying that just because a list is local doesn't mean it isn't mutable? – mvery Mar 22 '23 at 02:14
  • @mvery no. Objects are either mutable or immutable. Objects do not have a scope - variables have a scope. These things exist independently of each other! – juanpa.arrivillaga Mar 22 '23 at 02:16
  • IOW: "This seems to fly in the face of the mutability of lists." Is wrong. It doesn't imply anything about the mutability of lists, the error is about you trying to *reference a local variable* that hasn't been assigned to yet. – juanpa.arrivillaga Mar 22 '23 at 02:19
  • you should generally avoid mutating arguments to your function, btw. pure functions are best. – juanpa.arrivillaga Mar 22 '23 at 02:19
  • @Shorn I mean, a `tuple` is actually pretty much exactly an immutable `list`. Not sure why you think this is debatable, unless you mean to say that lists are by their very nature implied to be mutable... – juanpa.arrivillaga Mar 22 '23 at 02:28
  • @Shorn immutability means *you can't change the value of the object at all*. The important thing about mutability/immutability is that it is *totally irrelevant here*. if you did `knownBad = 42` then the code would fail *for the same reasons in the exact same way* – juanpa.arrivillaga Mar 22 '23 at 02:29
  • " I see how line 7 is a type of assignment to the 'knownBad' list, but the list still already exists. It isn't like the list is being created at line 7" You don't **assign to a list**. You assign to a *variable*. Objects are not variables. Variables are not objects. Variables are *names that refer to objects*. This is an important point to grok. The *compiler* actually checks a function for *any assignment statement* to a variable in the function and marks that variable as *local by default*. – juanpa.arrivillaga Mar 22 '23 at 02:32
  • @juanpa.arrivillaga the discussion I looked at was about tuples as its own data type rather than just an immutable list. I see it as its own data type that can be used as an immutable list and thus not strictly an immutable list, but others have differing opinions. Depending on implementation you can create operations which do "change" the values by re-creating a new version with the new values and returning the new version separately, i.e. string. You can't mutate strings directly, but you can still do operations on string that produce a new string with the changed results. – Shorn Mar 22 '23 at 02:37
  • @juanpa.arrivillaga also my comment on immutability is not regarding the error, but rather the poster's comment reply on what the local scoping meant for mutability which is incorrect. – Shorn Mar 22 '23 at 02:38

1 Answers1

0

If a variable is assigned anywhere in a function (even in a part of the function that hasn't executed yet!), then that variable is treated as local everywhere in the function.

Your function does assign to the variable, with this line of code:

knownBad = list(dict.fromkeys(knownBad))#removes duplicates

Therefore knownBad is treated as a local variable, and so the previous line causes the error:

knownBad.append(i)

... because it expects knownBad to be a local variable, but it has not (yet) been assigned a value.

If you want to use the global variable, then you have to say this at the top of the function:

global knownBad
John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • Or they could just modify the list instead. – Kelly Bundy Mar 22 '23 at 02:07
  • @John Gordon - I suspected the answer was something like that- I just thought that the immutability of lists was a little more thorough than that. Thank you! – mvery Mar 22 '23 at 02:11
  • @KellyBundy- how would I modify the list? Isn't that what I'm doing in line 6? – mvery Mar 22 '23 at 02:12
  • @mvery No, you're trying to throw away the old list and assign the new list to the variable. Modifying the list could for example be done with `knownBad[:] = dict.fromkeys(knownBad)`. – Kelly Bundy Mar 22 '23 at 02:14
  • @mvery *"immutability of lists"*? *"more thorough"*? You're speaking in riddles :-) – Kelly Bundy Mar 22 '23 at 02:16
  • @KellyBundy they are referring to the fact that they have `knownBad.append(i)` in the loop, in line 6, before `knownBad = list(dict.fromkeys(knownBad))#removes duplicates` – juanpa.arrivillaga Mar 22 '23 at 02:17
  • @juanpa.arrivillaga Ah yes, I didn't notice they changed the subject when they replied to me. – Kelly Bundy Mar 22 '23 at 02:20