1

I'm trying to figure out how to use a Python 3 function (using nonlocal variables) within more than one other function without defining it all over again. Here's a very simplified example of what I mean:

def inner(airplane):
    nonlocal var
    if airplane == "Alpha":
        var = a
    elif airplane == "Beta":
        var = b
def outer1(airplane):
    inner(airplane)
    do stuff with var
def outer2(airplane)
    inner(airplane)
    do other stuff with var

outer1("FirstAirplane")
outer2("SecondAirplane")

I'm getting an error (SyntaxError: No binding for nonlocal 'var' found) but I suspect I'm doing this very wrongly. I don't ever intend to run inner() on its own. How do I reuse inner() the right way? I can't just define it inside outer1() and then reuse it in outer2(), can I?

Okay, by uh...popular demand...here's the relevant section of my code...

def planeandoffset(airplane):
    if airplane == "Zulu":
        linename = "\\[\\INOV"
        charoffset = 14
    elif airplane == "Lima":
        linename = "\\[\\ILIM"
        charoffset = 10
    elif airplane == "Mike":
        linename = "\\[\\IMIK"
        charoffset = 10
    else:
        print("There is no airplane by that name.")
    latstart = charoffset
    latend = 7 + charoffset
    lonstart = 9 + charoffset
    lonend = 17 + charoffset
    return airplane, linename, latstart, latend, lonstart, lonend

def latlongen(workingline, latstart, latend, lonstart, lonend):
# Determine Latitude and Longitude in decimal format
    latraw = workingline[latstart:latend]
    if latraw[0:1] == "S":
        pm = "-"
    else:
        pm = ""
    hours = float(latraw[3:5] + "." + latraw[5:])
    decimal = hours/60
    latitude = float(latraw[1:3]) + decimal
    latitude = float(pm + str(latitude))
    lonraw = workingline[lonstart:lonend]
    if lonraw[0:1] == "W":
        pm = "-"
    else:
        pm = ""
    hours = float(lonraw[4:6] + "." + lonraw[6:])
    decimal = hours/60
    longitude = float(lonraw[1:4]) + decimal
    longitude = float(pm + str(longitude))
    return latitude, longitude
def kmlplanegen(airplane):
    planeandoffset(airplane)
    global afffilename, iconurl, kmlwrite
    affread = open(afffilename)
    while True:
        line = affread.readline()
        # Choose appropriate line
        if line.startswith(linename):
            workingline = line
        elif len(line) == 0: # Zero length indicates EOF (Always)
            break
        else:
            pass
    try:
        latlongen(workingline, latstart, latend, lonstart, lonend)
    # Generate kml for Airplane
        print('''   <Placemark>
            <Style>
                <IconStyle>
                    <Icon>
                        <href>{0}</href>
                    </Icon>
                </IconStyle>
            </Style>
            <name>{1}</name>
            <description>Latitude: {2} Longitude: {3}</description>
            <Point>
                <coordinates>{3},{2},0</coordinates>
            </Point>
        </Placemark>'''.format(iconurl,airplane,latitude,longitude), file=kmlwrite)
    except Exception:
        exit(1, "There was an error. This message is kind of worthless. Stopping Program")

def kmlpathgen(airplane):
    planeandoffset(airplane)
    global afffilename, kmlwrite
    # Generate kml for Airplane Path
    print('''   <Style id="yellowLineGreenPoly">
        <LineStyle>
            <color>7f00ffff</color>
            <width>4</width>
        </LineStyle>
        <PolyStyle>
            <color>7f00ff00</color>
        </PolyStyle>
    </Style>
    <Placemark>
        <name>{0} Path</name>
        <description>Transparent green wall with yellow outlines</description>
        <styleUrl>#yellowLineGreenPoly</styleUrl>
        <LineString>
            <extrude>1</extrude>
            <tessellate>1</tessellate>
            <altitudeMode>relativeToGround</altitudeMode>
            <coordinates>'''.format(airplane), file=kmlwrite)
    try:
        affread = open(afffilename)
        while True:
            line = affread.readline()
            if len(line) == 0: # Zero length indicates EOF (Always)
                break
            elif line.startswith(linename):
                workingline = line
                latlongen(workingline, latstart, latend, lonstart, lonend)
                print("                 {0},{1},0".format(longitude,latitude), file=kmlwrite)
            else:
                pass
    except NameError:
        pass
    finally:
        print('''           </coordinates>
            </LineString>
        </Placemark>''', file=kmlwrite)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Russianspi
  • 23
  • 6

1 Answers1

3

Are you just learning to programme?

Forget about nonlocal anything. The correct way to do this is to return var with return var, and then assign the result of that in the calling context:

var = inner(airplane)

Edit:

Where you are simply calling:

planeandoffset(airplane)

You are not doing anything with the values returned. To access them do:

airplane, linename, latstart, latend, lonstart, lonend = planeandoffset(airplane)

I'm not going to analyse what you're doing with those globals, but whatever it is, stop. Either pass those as parameters between functions, or put them, together with your functions, in a class, and access them as members of the class.

Either way, I suggest that you follow a tutorial to learn a more usual style of programming that doesn't use globals.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • 1
    Unfortunately, no, I'm not just learning, but I'm trying to relearn well. I've written some very functional, VERY ugly code. It's a nightmare to maintain, and I've decided that I need to learn to write code well, instead of just code that does its job in the most quickly cobbled together way possible. – Russianspi Dec 13 '11 at 21:08
  • +1. Just forget about global and nonlocal. Use return. Make it simple. – Bite code Dec 14 '11 at 13:23
  • Yes, that seems to make sense. Do I just need to refer to my variable as "var" or do I need something like "inner.var". Its still not working quite right. "Outer" says its looking for a global variable var and can't find it. – Russianspi Dec 14 '11 at 22:56
  • @Russianspi: I'd need to see your code to comment on exactly what is wrong, but no, there is no special syntax, because the point is that `var` in the one context is a completely different variable from var in the other context. – Marcin Dec 14 '11 at 23:57