0

I have 2 functions contained in 2 separate files.

The functions are similar and use the same names for local variables within them. The argument, 'eachtickerlist', that is passed into each function is also the same. eachtickerlist is a list of lists that look like this

[[ticker,price,year,month,day,seconds from epoch],[ticker,price,year.........

For some reason, the Local variable within each function 'amlist' is holding onto the data from function 1 when it is executing function 2 even though it is re-initialized in the second function and even though it is a local variable. I know this because when I print out 'amlist' at the end of function 2 in order to test it, the strings 'undefined', 'up', or 'down' are listed 2 times. If I don't call function 1 in the main program, this does not happen which proves that what is happening in function 1 is affecting function 2...which I totally don't understand.

In my main program I call each function as shown below:

eachtickerlist=sorted(eachtickerlist,key=operator.itemgetter(6)) #This is the argument to be passed     

upsvsdownsresult=upsvsdowns.upsvsdowns(eachtickerlist) #This sends the argument to the first function and stores the return value for use later

swingsresult=swings.swings(eachtickerlist) #This sends the same argument to the second function and stores the return value for use later

Here is function 1:

def upsvsdowns(eachtickerlist):

amlist=[]
for thing in eachtickerlist:
    if (thing[5]=='8'):
        amlist.append(thing)


    else:
        pass


try:
    amlist[0].append('undefined')
    length=len(amlist)


    for x in range(1,length):
        price=float(amlist[x][1])
        yesterdaysprice=float(amlist[(x-1)][1])

        if ((price-yesterdaysprice)>0):
            amlist[x].append('up')

        else:
            amlist[x].append('down')

    upcount=0
    totalcount=0
    for y in amlist:
        if (y[7]=='up'):
            upcount=upcount+1
            totalcount=totalcount+1

        else:
            totalcount=totalcount+1

    percentage=round(float(upcount)/float(totalcount),3)
    returnlist=[amlist[0][0],str(percentage)]

    return (returnlist)

except (IndexError):
    returnlist=[eachtickerlist[0][0],'No Data']
    return (return list)

Here is function 2:

def swings(eachtickerlist):

amlist=[]
for thing in eachtickerlist:
    if (thing[5]=='8'):
        amlist.append(thing)


    else:
        pass


try:
    amlist[0].append('undefined')
    length=len(amlist)


    for x in range(1,length):
        price=float(amlist[x][1])
        yesterdaysprice=float(amlist[(x-1)][1])

        if ((price-yesterdaysprice)>0):
            amlist[x].append('up')

        else:
            amlist[x].append('down')

    upcount=0
    downcount=0
    ups=[]
    downs=[]

    print amlist

    for y in amlist:
        if (y[7]=='up'):

            if (downcount!=0):
                downs.append(downcount)

            else:
                pass

            downcount=0
            upcount=upcount+1


        elif (y[7]=='down'):

            if (upcount!=0):
                ups.append(upcount)
            else:
                pass
            upcount=0
            downcount=downcount+1

        if (upcount!=0):
            ups.append(upcount)
        elif (downcount!=0):
            downs.append(downcount)
        else:
            pass
    #print ups
    #print downs


    try:
        averageup=round(sum(ups)/float(len(ups)),3)

    except(ZeroDivisionError):
        averageup=round(0.0,3)

    try:
        averagedown=round(sum(downs)/float(len(downs)),3)

    except(ZeroDivisionError):
        averagedown=round(0.0,3)


    returnlist=[amlist[0][0],str(averageup),str(averagedown)]

    return (returnlist)

except (IndexError):
    returnlist=[eachtickerlist[0][0],'No Data']
    return (return list)

Here is the output from the print statement in the second function. Notice the 2 undefined's, up's and down's in each list.

['AAIT', '35.09', '2014', '7', '28', '8', '2409480.0', 'undefined', 'undefined'], ['AAIT', '35.21', '2014', '7', '29', '8', '2494662.0', 'up', 'up'], ['AAIT', '40', '2014', '7', '29', '8', '2494662.5', 'up', 'up'], ['AAIT', '42.3', '2014', '7', '29', '8', '2494663.0', 'up', 'up']]

any help would be appreciated.

-Brandon

BPoy
  • 159
  • 2
  • 11
  • You say you call the methods with `upsvsdowns.upsvsdowns` but the declarations say they are global functions. Are these methods members of a class or some other data structure? – Andrew Johnson Aug 01 '14 at 02:54
  • Welcome to Stack Overflow! It is wonderful that you have include the actual code that is failing (and not some pseudo-code or inaccurate description.) There are two things that would make your question better (and more likely to elicit quality answers). First, reduce your program to the smallest actual program that still demonstrates the error. Copy-paste that short, complete program into your question. Second, pay careful attention to your indentation. Pasting your program into SO seems to have broken its indentation. Again, welcome! – Robᵩ Aug 01 '14 at 03:02
  • In the main program I import upsvsdowns.py and swings.py which are files that only hold the respective function. I'm not sure if that answers your question? – BPoy Aug 01 '14 at 05:56
  • I will upload less code next time. And sorry about the indentation. – BPoy Aug 01 '14 at 05:57

1 Answers1

2

No, local variables are not shared between functions. Local variables exist entirely inside one function. But unlike other programming languages, variables are not objects. They are merely names by which objects are known. The objects, on the other hand, can persist long after the called function returns.

Let's consider a simpler program:

def func1(arg):
    arg.append('Hello from Func1!')
    print arg

def func2(arg):
    arg.append(2)
    print arg


main_list = [9,8,7]

func1(main_list)
func2(main_list)

The output of this program is:

[9, 8, 7, 'Hello from Func1!']
[9, 8, 7, 'Hello from Func1!', 2]

As you can see, the local variable arg in func2 has the message from func1 in it! But that one was only added to the local variable in func1. So, does this mean that the name arg in one context is related somehow to the name arg in the other context?

No.

In Python, unlike C or C++, variables are not passed by value to subroutines. Rather, parameters in callee are bound to the same object that existed in the caller. This is sort of like pass-by-reference in other programming languages.

Consider this:

def func(i):
   print id(i)

a="Some string"
print id(a)
func(a)

In this example, we print out the id() of an object. Every object has an id(), and no two objects ever have the same id at the same time.

The output of this example on my PC is:

140601888512880
140601888512880

As you can see, a in the caller and i in the callee are the same object. One is not a copy of the other, they are precisely the same object.

What does this all have to do with your program?

In your program, you modify the passed-in list. Since no copy was made, you are actually modifying the original list. When that original list is passed to the next function, it receives your modified list.

Here is how you modify the passed-in list object in upsvsdowns:

amlist.append(thing) # This doesn't modify the original object, but it does
                     # set up the modification. After this line completes,
                     # amlist[i] and eachtickerlist[j] are both references
                     # to the same object (for some value of i and j).

amlist[0].append('undefined') # This statement modifies the object referenced by
                              # amlist[0]. Since that object is also referenced by
                              # eachtickerlist[j] (for some j), you have now
                              # stored the word 'undefined' "inside" the object
                              # referenced by eachtickerlist. Since that object
                              # is also referenced by the caller's eachtickerlist,
                              # the change is permanent and globally visible

For a clearer explanation, see the docs, particularly §3.1.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • So what you are saying is I should do a clean copy of the passed in list and then use the copied list for all my work within the function? I will give it a try. Thanks for the help. – BPoy Aug 01 '14 at 06:03
  • I just tried copying to a clean list at the top of the function with the same result. I am still unclear how to break the connection with the passed in list. – BPoy Aug 01 '14 at 06:19
  • I got it to work using deepcopy() which was discussed here http://stackoverflow.com/questions/2322068/python-passing-list-as-argument – BPoy Aug 01 '14 at 06:34
  • @BPoy - as you have found, it depends how you do your copy. "a=b" does not do a copy - it simply creates another name. for a list "a=b[:]" will do a copy of the contents of b into a (so you can change a without changing b - although that is only for non-nested lists. – Tony Suffolk 66 Aug 01 '14 at 06:48
  • @BPoy - No, I am not saying that. I offered no advice nor prescription. I simply tried to explain what you were seeing. – Robᵩ Aug 01 '14 at 12:45