durbachit
durbachit

Reputation: 4886

"UnboundLocalError: local variable referenced before assignment" when calling a function

For plotting, I am defining colours based on a condition (the conditions are some values in certain columns of a pandas dataframe). Now I am not sure, if I make a mistake when defining the function. The function is as follows:

def getColour(C, threshold):
    neg = 0 - threshold
    half = threshold/2
    if C <= (neg - half):
        clr = '#2b83ba'
    if ((neg - half) < C <= neg):
        clr = '#abdda4'
    if ((threshold + half) > C >= threshold):
        clr = '#fdae61'
    if (C > (threshold + half)):    
        clr = '#d7191c'
    return clr

And this is how I implement it: I iterate through rows of a dataframe, then find columns fulfilling a condition, use the indices from these columns to get parameters from a list, apply another function that generates the results (this function works, the script is tested and worked fine when I set a fixed color for plotting) and then plot the results with different colour.

for index, row in Sparse.iterrows():
    lim = row[row.notnull()] 
    ci = [row.index.get_loc(x) for x in lim.index]
    params = np.array(myList)[ci]

    for i, w in enumerate(params):
        w = w.tolist()
        print w, w[2]
        print ci[i]
        colour = getColour(ci[i], threshold)
        x, y = myFunction(w)
        plt.plot(x,y, color=colour,linestyle='-',linewidth=1.5)

But this throws an error UnboundLocalError: local variable 'clr' referenced before assignment on the line colour = getColour(ci[i], threshold).

I have read other posts dealing with this error, but I can't see what my problem is.

Upvotes: 0

Views: 2489

Answers (2)

questionto42
questionto42

Reputation: 9562

Even with an "if ... else" statement, it should fail. I leave the many ifs even though that is bad practice just to show that it is not about adding an else or not. In a try statement, an except will not change it either, not speaking of finally. What you need is to draw the declaration of a variable before any if block (or before any try block or other cascading code blocks).

Means:

def getColour(C, threshold):
    neg = 0 - threshold
    half = threshold/2
    clr = None
    if C <= (neg - half):
        clr = '#2b83ba'
    if ((neg - half) < C <= neg):
        clr = '#abdda4'
    if ((threshold + half) > C >= threshold):
        clr = '#fdae61'
    if (C > (threshold + half)):    
        clr = '#d7191c'
    return clr

Another way is to return the variable only if it is in the dictionary that the globals() function returns.

def getColour(C, threshold):
    neg = 0 - threshold
    half = threshold/2
    if C <= (neg - half):
        clr = '#2b83ba'
    if ((neg - half) < C <= neg):
        clr = '#abdda4'
    if ((threshold + half) > C >= threshold):
        clr = '#fdae61'
    if (C > (threshold + half)):    
        clr = '#d7191c'
    if 'clr' in `globals()`:
        return clr
    else:
        return None

This is not needed since the function will return None by default:

    else:
        return None

It may make the code better readable with this check against globals(), though.

Upvotes: 0

Ludovic Guerra
Ludovic Guerra

Reputation: 350

In general, it is a good idea to consolidate the logic of your condition. Do your clr will have all those value ? No so you can use some elif :

def getColour(C, threshold):
    neg = 0 - threshold
    half = threshold/2
    if C <= (neg - half):
        clr = '#2b83ba'
    elif ((neg - half) < C <= neg):
        clr = '#abdda4'
    elif ((threshold + half) > C >= threshold):
        clr = '#fdae61'
    elif (C > (threshold + half)):    
        clr = '#d7191c'
    return clr

Then do your condition is looped ? Do you have all cases in your if ? If yes, it is a good idea to throw an error in an else. If no, then that's mean you just forgot one case:

def getColour(C, threshold):
    neg = 0 - threshold
    half = threshold/2
    if C <= (neg - half):
        clr = '#2b83ba'
    elif ((neg - half) < C <= neg):
        clr = '#abdda4'
    elif ((threshold + half) > C >= threshold):
        clr = '#fdae61'
    elif (C > (threshold + half)):    
        clr = '#d7191c'
    else:
        raise ValueError('Value expected for clr')
    return clr

Edit : To answer to your comment, I think you misunderstand what i meant. In Python, it is better to throw an error if you have something unexpected. So either:

  • You give a default color value like "White" and you use it normally
  • You return None and then the rest of your code should check for None value before reading it (and then maybe throwing error)
  • You directly throw an error

PEP20: Errors should never pass silently.

Upvotes: 2

Related Questions