Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #49068 > unrolled thread

What's wrong with this code? (UnboundLocalError: local variable referenced before assignment)

Started bypablobarhamalzas@gmail.com
First post2013-06-24 12:47 -0700
Last post2013-06-24 21:19 +0100
Articles 11 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) pablobarhamalzas@gmail.com - 2013-06-24 12:47 -0700
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) pablobarhamalzas@gmail.com - 2013-06-24 12:53 -0700
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Peter Otten <__peter__@web.de> - 2013-06-24 22:05 +0200
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Antoon Pardon <antoon.pardon@rece.vub.ac.be> - 2013-06-24 22:10 +0200
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) John Gordon <gordon@panix.com> - 2013-06-24 20:12 +0000
      Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Joshua Landau <joshua.landau.ws@gmail.com> - 2013-06-24 21:33 +0100
      Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Dave Angel <davea@davea.name> - 2013-06-24 16:43 -0400
        Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) John Gordon <gordon@panix.com> - 2013-06-24 21:41 +0000
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) pablobarhamalzas@gmail.com - 2013-06-24 13:19 -0700
      Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Joshua Landau <joshua.landau.ws@gmail.com> - 2013-06-24 21:33 +0100
    Re: What's wrong with this code? (UnboundLocalError: local variable referenced before assignment) Joshua Landau <joshua.landau.ws@gmail.com> - 2013-06-24 21:19 +0100

#49068 — What's wrong with this code? (UnboundLocalError: local variable referenced before assignment)

Frompablobarhamalzas@gmail.com
Date2013-06-24 12:47 -0700
SubjectWhat's wrong with this code? (UnboundLocalError: local variable referenced before assignment)
Message-ID<b3d3518a-f24a-4c32-a41a-b99145753528@googlegroups.com>
Hi there! I'm quite new to programming, even newer in python (this is actually the first thing I try on it), and every other topic I've seen on forums about my problem doesn't seem to help. 

So, the following lines are intended to draw a white square (which it does), turn it to blue when you click on it, and back to white when you click on it again (and so on). Here's what I wrote (python 3 syntax):


from tkinter import *

root = Tk()
root.geometry("500x500")

w = Canvas(root, width=500, height=500)
w.pack()

coords = (x1, y1, x2, y2) = (100, 100, 200, 200)

rect = w.create_rectangle(coords, fill="white")    
isWhite = True
        
def change(event):
    if event.x > x1 and event.x < x2 and event.y > y1 and event.y < y2:
        if isWhite:
            w.itemconfig(rect, fill="blue")
            isWhite = False
        else:
            w.itemconfig(rect, fill="white")   
            isWhite = True
                              
w.bind("<Button-1>", change)
      
root.mainloop()  


The problem occurs when clicking on the white square. The following error appears:
"if isWhite:
UnboundLocalError: local variable 'isWhite' referenced before assignment"

However, the isWhite variable is clearly defined at "True" a few lines before.
Also, if I remove the lines that change isWhite to False if it's True and viceversa, the program doesn't throw any error, but obviously doesn't do what I want it to do (it only changes the square color once, as isWhite stays set to True).

What can the problem be? I'm sure it's something really simple, but I don't get it... Thank's!

[toc] | [next] | [standalone]


#49069

Frompablobarhamalzas@gmail.com
Date2013-06-24 12:53 -0700
Message-ID<f0fc81d0-332f-4b1d-a82f-3b9c15597977@googlegroups.com>
In reply to#49068
Just before anyone says, the reason I bind to the Canvas instead of binding directly to the rectangle is because I plan to add more squares in the future.
Cheers.

[toc] | [prev] | [next] | [standalone]


#49071

FromPeter Otten <__peter__@web.de>
Date2013-06-24 22:05 +0200
Message-ID<mailman.3758.1372104355.3114.python-list@python.org>
In reply to#49068
pablobarhamalzas@gmail.com wrote:

> Hi there! I'm quite new to programming, even newer in python (this is
> actually the first thing I try on it), and every other topic I've seen on
> forums about my problem doesn't seem to help.
> 
> So, the following lines are intended to draw a white square (which it
> does), turn it to blue when you click on it, and back to white when you
> click on it again (and so on). Here's what I wrote (python 3 syntax):
> 
> 
> from tkinter import *
> 
> root = Tk()
> root.geometry("500x500")
> 
> w = Canvas(root, width=500, height=500)
> w.pack()
> 
> coords = (x1, y1, x2, y2) = (100, 100, 200, 200)
> 
> rect = w.create_rectangle(coords, fill="white")
> isWhite = True
>         
> def change(event):
>     if event.x > x1 and event.x < x2 and event.y > y1 and event.y < y2:
>         if isWhite:
>             w.itemconfig(rect, fill="blue")
>             isWhite = False
>         else:
>             w.itemconfig(rect, fill="white")
>             isWhite = True
>                               
> w.bind("<Button-1>", change)
>       
> root.mainloop()
> 
> 
> The problem occurs when clicking on the white square. The following error
> appears: "if isWhite:
> UnboundLocalError: local variable 'isWhite' referenced before assignment"
> 
> However, the isWhite variable is clearly defined at "True" a few lines
> before. Also, if I remove the lines that change isWhite to False if it's
> True and viceversa, the program doesn't throw any error, but obviously
> doesn't do what I want it to do (it only changes the square color once, as
> isWhite stays set to True).
> 
> What can the problem be? I'm sure it's something really simple, but I
> don't get it... Thank's!

Python statically determines the scope of a variable -- if you rebind a name 
it assumes that the variable is local:

>>> def f():
...     print is_white
...     is_white = 42
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'is_white' referenced before assignment

The fix is to tell Python that you want to access the global variable:

>>> def f():
...     global is_white
...     print is_white
...     is_white = 42
... 
>>> is_white = "whatever"
>>> f()
whatever
>>> f()
42

[toc] | [prev] | [next] | [standalone]


#49072

FromAntoon Pardon <antoon.pardon@rece.vub.ac.be>
Date2013-06-24 22:10 +0200
Message-ID<mailman.3759.1372104684.3114.python-list@python.org>
In reply to#49068
Op 24-06-13 21:47, pablobarhamalzas@gmail.com schreef:
> Hi there! I'm quite new to programming, even newer in python (this is actually the first thing I try on it), and every other topic I've seen on forums about my problem doesn't seem to help.
>
> So, the following lines are intended to draw a white square (which it does), turn it to blue when you click on it, and back to white when you click on it again (and so on). Here's what I wrote (python 3 syntax):
>
>
> from tkinter import *
>
> root = Tk()
> root.geometry("500x500")
>
> w = Canvas(root, width=500, height=500)
> w.pack()
>
> coords = (x1, y1, x2, y2) = (100, 100, 200, 200)
>
> rect = w.create_rectangle(coords, fill="white")
> isWhite = True
>
> def change(event):
>      if event.x>  x1 and event.x<  x2 and event.y>  y1 and event.y<  y2:
>          if isWhite:
>              w.itemconfig(rect, fill="blue")
>              isWhite = False
>          else:
>              w.itemconfig(rect, fill="white")
>              isWhite = True
>
> w.bind("<Button-1>", change)
>
> root.mainloop()
>
>
> The problem occurs when clicking on the white square. The following error appears:
> "if isWhite:
> UnboundLocalError: local variable 'isWhite' referenced before assignment"
>
> However, the isWhite variable is clearly defined at "True" a few lines before.

No it is not.

In Python, when you assign to a variable within a function, that
variable will be treated as a local variable. If you have a global
variable with the same name, that global variable will just for
the duration of the function become inaccessible.

The quick solution in this case is to include a global statement.
Something like

def change(event)
     global isWhite
     ...

-- 
Antoon Pardon

[toc] | [prev] | [next] | [standalone]


#49074

FromJohn Gordon <gordon@panix.com>
Date2013-06-24 20:12 +0000
Message-ID<kqa981$9uu$1@reader2.panix.com>
In reply to#49068
In <b3d3518a-f24a-4c32-a41a-b99145753528@googlegroups.com> pablobarhamalzas@gmail.com writes:

> isWhite = True
>         
> def change(event):
>     if event.x > x1 and event.x < x2 and event.y > y1 and event.y < y2:
>         if isWhite:
>             w.itemconfig(rect, fill="blue")
>             isWhite = False
>         else:
>             w.itemconfig(rect, fill="white")   
>             isWhite = True
>                               
> w.bind("<Button-1>", change)
>       
> root.mainloop()  

> The problem occurs when clicking on the white square. The following error
> appears:
> "if isWhite:
> UnboundLocalError: local variable 'isWhite' referenced before assignment"

> However, the isWhite variable is clearly defined at "True" a few lines
> before.

Since you're new to programming, this might be a bit tricky to explain,
but I'll do my best. :-)

The problem is that change() isn't being executed here; instead it's being
executed from within root.mainloop(), whenever the user presses button-1.

And within root.mainloop(), there is no variable called isWhite.

-- 
John Gordon                   A is for Amy, who fell down the stairs
gordon@panix.com              B is for Basil, assaulted by bears
                                -- Edward Gorey, "The Gashlycrumb Tinies"

[toc] | [prev] | [next] | [standalone]


#49078

FromJoshua Landau <joshua.landau.ws@gmail.com>
Date2013-06-24 21:33 +0100
Message-ID<mailman.3763.1372106041.3114.python-list@python.org>
In reply to#49074
On 24 June 2013 21:12, John Gordon <gordon@panix.com> wrote:
> Since you're new to programming, this might be a bit tricky to explain,
> but I'll do my best. :-)
>
> The problem is that change() isn't being executed here; instead it's being
> executed from within root.mainloop(), whenever the user presses button-1.
>
> And within root.mainloop(), there is no variable called isWhite.

Sorry, but I don't think you're right. Functions "carry" their
contexts around with them, so that shouldn't matter

(See Peter and Antoon's comments for explanation of what I think it is)

[toc] | [prev] | [next] | [standalone]


#49082

FromDave Angel <davea@davea.name>
Date2013-06-24 16:43 -0400
Message-ID<mailman.3767.1372106609.3114.python-list@python.org>
In reply to#49074
On 06/24/2013 04:12 PM, John Gordon wrote:
> In <b3d3518a-f24a-4c32-a41a-b99145753528@googlegroups.com> pablobarhamalzas@gmail.com writes:
>
>> isWhite = True
>>
>> def change(event):
>>      if event.x > x1 and event.x < x2 and event.y > y1 and event.y < y2:
>>          if isWhite:
>>              w.itemconfig(rect, fill="blue")
>>              isWhite = False
>>          else:
>>              w.itemconfig(rect, fill="white")
>>              isWhite = True
>>
>> w.bind("<Button-1>", change)
>>
>> root.mainloop()
>
>> The problem occurs when clicking on the white square. The following error
>> appears:
>> "if isWhite:
>> UnboundLocalError: local variable 'isWhite' referenced before assignment"
>
>> However, the isWhite variable is clearly defined at "True" a few lines
>> before.
>
> Since you're new to programming, this might be a bit tricky to explain,
> but I'll do my best. :-)
>
> The problem is that change() isn't being executed here; instead it's being
> executed from within root.mainloop(), whenever the user presses button-1.
>
> And within root.mainloop(), there is no variable called isWhite.
>

Actually that's irrelevant.  Whether or not there's one global with the 
same name, or twenty-three object attributes with the same name, the 
fact that there's a binding of the local makes that name a local.  The 
only way to avoid that is not to bind, or to use global or nonlocal 
declarations.


Pablo:  Global variables are generally frowned upon, unless they're 
constant.  If they're constant, use ALLCAPS to indicate that.  Since 
this is not, it would normally be an attribute of some object, in your 
case, possibly the object w.  And of course, w should have been an 
argument to the function as well, since you're operating on it.  But you 
may be stuck with that, because of tkinter's design.  Anyway, you can assign
     w.isWhite = True

and access
     if w.isWhite

with impunity, since w is not being bound inside the function.


When you need to pass extra arguments that the event model doesn't allow 
for, one approach is to use functools.partial().

And it's also possible that there's a method (in tkinter) on event that 
let's you find the object that it's acting upon, w.  In this case, you 
could avoid needing a global at all, which would be a big improvement. 
Especially when you decide to have multiple such boxes, and want each to 
be able to toggle colors independently.


-- 
DaveA

[toc] | [prev] | [next] | [standalone]


#49090

FromJohn Gordon <gordon@panix.com>
Date2013-06-24 21:41 +0000
Message-ID<kqaef1$l1s$1@reader2.panix.com>
In reply to#49082
In <mailman.3767.1372106609.3114.python-list@python.org> Dave Angel <davea@davea.name> writes:

> > The problem is that change() isn't being executed here; instead it's being
> > executed from within root.mainloop(), whenever the user presses button-1.
> >
> > And within root.mainloop(), there is no variable called isWhite.
> >

> Actually that's irrelevant.  Whether or not there's one global with the 
> same name, or twenty-three object attributes with the same name, the 
> fact that there's a binding of the local makes that name a local.  The 
> only way to avoid that is not to bind, or to use global or nonlocal 
> declarations.

Quite right.  I should have verified my answer before posting.  Thanks
for setting me straight.  :-)

-- 
John Gordon                   A is for Amy, who fell down the stairs
gordon@panix.com              B is for Basil, assaulted by bears
                                -- Edward Gorey, "The Gashlycrumb Tinies"

[toc] | [prev] | [next] | [standalone]


#49075

Frompablobarhamalzas@gmail.com
Date2013-06-24 13:19 -0700
Message-ID<3dc29fa9-cd6d-4150-a76f-d215d7a4192e@googlegroups.com>
In reply to#49068
Thank's to you all! 

Setting isWhite as global worked fine.
I'll probably be back soon with another silly question, see you then :)

[toc] | [prev] | [next] | [standalone]


#49080

FromJoshua Landau <joshua.landau.ws@gmail.com>
Date2013-06-24 21:33 +0100
Message-ID<mailman.3766.1372106494.3114.python-list@python.org>
In reply to#49075
On 24 June 2013 21:19,  <pablobarhamalzas@gmail.com> wrote:
> Thank's to you all!
>
> Setting isWhite as global worked fine.
> I'll probably be back soon with another silly question, see you then :)

By the way, it's normally bad to use globals like this. When you're
learning it's something you just do, though; it's fine for now.

[toc] | [prev] | [next] | [standalone]


#49077

FromJoshua Landau <joshua.landau.ws@gmail.com>
Date2013-06-24 21:19 +0100
Message-ID<mailman.3762.1372105516.3114.python-list@python.org>
In reply to#49068
Here's a little test to make sure you understand (this is one of the
most confusing parts of Python's closures in my opinion):

foo = "I'm foo!"

def working():
    print(foo)

def broken():
    print(foo)

    if False: # There's no way this could cause a problem!
        foo = "This will *never* happen"

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web