Local variable referenced before assignment? [duplicate]
Asked Answered
V

5

81

I am using the PyQt library to take a screenshot of a webpage, then reading through a CSV file of different URLs. I am keeping a variable feed that incremements everytime a URL is processed and therefore should increment to the number of URLs.

Here's code:

webpage = QWebPage()
fo = open("C:/Users/Romi/Desktop/result1.txt", "w")
feed = 0
def onLoadFinished(result):
    #fo.write( column1[feed])#, column2[feed], urls[feed])
   #feed = 0
   if not result:
        print "Request failed"
    fo.write(column1[feed])
    fo.write(',')
    fo.write(column2[feed])
    fo.write(',')
    #fo.write(urls[feed])
    fo.write(',')
    fo.write('404,image not created\n')
    feed = feed + 1
        sys.exit(1)
        save_page(webpage, outputs.pop(0))   # pop output name from list and save
   if urls:
        url = urls.pop(0)   # pop next url to fetch from list
        webpage.mainFrame().load(QUrl(url))
    fo.write(column1[feed])#,column2[feed],urls[feed],'200','image created','/n')
    fo.write(',')
    fo.write(column2[feed])
    fo.write(',')
    #fo.write(urls[feed])
    fo.write(',')
    fo.write('200,image created\n')
    feed = feed + 1
   else:
        app.quit()  # exit after last url

webpage.connect(webpage, SIGNAL("loadFinished(bool)"), onLoadFinished)
webpage.mainFrame().load(QUrl(urls.pop(0)))
#fo.close()
sys.exit(app.exec_())

It gives me the error:

local variable feed referenced before the assignment at fo.write(column1[feed])#,column2[feed],urls[feed],'200','image created','/n')

Any idea why?

Vasily answered 1/8, 2013 at 19:24 Comment(2)
Another probable cause: I'd imported the sys module twice.Grannie
@Nae definitely a duplicate, and also this version is clearly lower quality as it lacks an MRE.Jowers
V
129

When Python parses the body of a function definition and encounters an assignment such as

feed = ...

Python interprets feed as a local variable by default. If you do not wish for it to be a local variable, you must put

global feed

in the function definition. The global statement does not have to be at the beginning of the function definition, but that is where it is usually placed. Wherever it is placed, the global declaration makes feed a global variable everywhere in the function.

Without the global statement, since feed is taken to be a local variable, when Python executes

feed = feed + 1,

Python evaluates the right-hand side first and tries to look up the value of feed. The first time through it finds feed is undefined. Hence the error.

The shortest way to patch up the code is to add global feed to the beginning of onLoadFinished. The nicer way is to use a class:

class Page(object):
    def __init__(self):
        self.feed = 0
    def onLoadFinished(self, result):
        ...
        self.feed += 1

The problem with having functions which mutate global variables is that it makes it harder to grok your code. Functions are no longer isolated units. Their interaction extends to everything that affects or is affected by the global variable. Thus it makes larger programs harder to understand.

By avoiding mutating globals, in the long run your code will be easier to understand, test and maintain.

Virilism answered 1/8, 2013 at 19:31 Comment(7)
how come you don't need to do this for dict?Machado
@HoKy22: Are you asking why dct[key] = val does not raise a "local variable referenced before assignment" error? The reason is that this is not a bare name assignment. Instead, it causes Python to make the function call dct.__setitem__(key, val).Virilism
yes, that's what i am asking. for example, in a file, all the way in the top, you just put dict = {} and then in a method, you set dict['apple'] = 5 and in a different method you can print(dict['apple']). so this is because dict call __setitem__('apple', 5) when you do dict['apple'] = 5?Machado
When Python encounters dct['apple'] = 5 in a function, it does not see this as an assignment to dct. It translates it into the function call dct.__setitem__('apple', 5). (I'm trying to avoid calling the variable dict since that shadows the builtin of the same name). Assuming there is no assignment to dct, dct is not a local variable of the function. So according to the LEGB scoping rules Python will find dct in the global namespace since dct = {} was defined at the top of the script. Hence, no error is raised.Virilism
You might also find Ned Batchelder's essay on Facts and myths about Python names and values to be helpful. It will clarify the mental model you need when thinking about how Python treats bare variable names and their relationship to values.Virilism
You also don't need to do this for list.append(). As others said, anything that doesn't run assignment under the hood is fine.Australoid
The class thing is a convenient workaround, woah...Benedick
A
47

Put a global statement at the top of your function and you should be good:

def onLoadFinished(result):
    global feed
    ...

To demonstrate what I mean, look at this little test:

x = 0
def t():
    x += 1
t()

this blows up with your exact same error where as:

x = 0
def t():
    global x
    x += 1
t()

does not.

The reason for this is that, inside t, Python thinks that x is a local variable. Furthermore, unless you explicitly tell it that x is global, it will try to use a local variable named x in x += 1. But, since there is no x defined in the local scope of t, it throws an error.

Apathetic answered 1/8, 2013 at 19:30 Comment(0)
L
12

As the Python interpreter reads the definition of a function (or, I think, even a block of indented code), all variables that are assigned to inside the function are added to the locals for that function. If a local does not have a definition before an assignment, the Python interpreter does not know what to do, so it throws this error.

The solution here is to add

global feed

to your function (usually near the top) to indicate to the interpreter that the feed variable is not local to this function.

Laywoman answered 1/8, 2013 at 19:30 Comment(0)
C
2

in my case that exact same error was triggered by a typo ! I thought my my var name was

varAlpha

but in the code i had defined

varalpha

& got the error

UnboundLocalError: local variable 'varAlpha' referenced before assignment

when calling varAlpha

I hope it helps somebody one day searching for that error & wondering (as my search for that error led me here while being unrelated with the use of global or not global which was a head scratcher !)

Corm answered 23/6, 2021 at 15:49 Comment(0)
I
0

You can do like this for the function scope

def main()

  self.x = 0

  def incr():
    self.x += 1
  
  for i in range(5):
     incr()
  
  print(self.x)
Imprinting answered 26/10, 2021 at 22:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.