[Tutor] Python Variables Changing in Other Functions

Rachel-Mikel ArceJaeger arcejaeger at gmail.com
Wed May 25 06:14:22 CEST 2011


Hello,

I am having trouble with determining when python is passing by reference and by value and how to fix it to do what I want:

I am writing a program that will take in a list of book titles and will allow many people to rank them in terms of popularity and will export the results to Excel. I'll include the whole code below, but the function I'm having trouble with is rankRandom(). I want it to take in a list of titles, randomize the list (so that I can be certain the order isn't influencing the results of the rankings), get a person's rankings, and then resort the order of the rankings to match the original order of title list (that way I can match up different people's rankings to the correct title).

The issue is this: random.shuffle() mutates the list in place, rather than creating a new copy. This is fine, but rather than modifying just the local copy of my titles, it is modifying it in the other functions, too. For instance, rankRandom() is called by main(), which passes it listOfTitles. When rankRandom() returns, listOfTitles has been changed to the randomized version of titles.

To fix this, I tried copying the original title list and then assigning it to the mutated version right before the rankRandom() function returns. The local version of titles in rankRandom() does indeed regain its original value, but listOfTitles in main() is still being assigned to the randomized version, and not to the original version. This boggles me, since it seems like shuffle is mutating the titles as if it were a global variable, but assignment is treating it only as a local. 

What exactly is going on here, and how do I avoid this problem?

Many thank!
Rachel

----------------------------

import xlwt as excel
import random
import copy

def getTitleList():
    """ Makes a list of all the lines in a file """
    
    filename = raw_input("Name and Extension of File: ")
    
    myFile = open( filename )
    
    titles = []
    title = "none"
    
    while title != "":
        
        title = myFile.readline()
    
        if title not in ["\n",""]:
            titles.append(title)
        
    return titles
    
def rank( titles ):
    """ Gets a user-input ranking for each line of text. 
        Returns those rankings 
    """
    
    ranks = []
    
    for t in titles:
        
        rank = raw_input(t + " ")
        ranks.append(rank)
            
    return ranks
    
def rankRandom( titles ):
    """ Takes a list of titles, puts them in random order, gets ranks, and then
        returns the ranks to their original order (so that the rankings always  
        match the correct titles).
    """
    
    finalRanks = [0]*len(titles)
    
    origTitles = copy.copy(titles)
    #print "Orign: ", origTitles
    
    random.shuffle(titles)	# Shuffle works in-place
    ranks = rank(titles)
    
    i = 0
    for t in titles:
        
        finalRanks[ origTitles.index(t) ] = ranks[i]
        i += 1
        
    titles = origTitles # Must restore, since python passes by reference, not 
                        # value, and the original structure was changed by 
                        # shuffle
    #print "t: ", titles
        
    return finalRanks
    
def writeToExcel(titles, allRanks):
    
    # Open new workbook
    mydoc = excel.Workbook()
    
    # Add a worksheet
    mysheet = mydoc.add_sheet("Ranks")
    
    # Write headers
    header_font = excel.Font() # Make a font object
    header_font.bold = True
    header_font.underline = True
    
    # Header font needs to be style, actually
    header_style = excel.XFStyle(); header_style.font = header_font
    
    # Write Headers: write( row, col, data, style )
    row = 0
    col = 0
    for t in titles:
        # Write data. Indexing is zero based, row then column
        mysheet.write(row, col, t, header_style)
        col += 1
        
    # Write Data
    row += 1
    for ranks in allRanks:
        col = 0
        for r in ranks:
            mysheet.write(row, col, r)
            col += 1
        row += 1
          
    # Save file. You don't have to close it like you do with a file object
    mydoc.save("r.xls")

def main():
    
    listOfTitles = getTitleList()

    allRanks = []
    
    done = raw_input("Done?: ")
    
    while done != "y":
        allRanks.append( rankRandom( listOfTitles ) )
        #print listOfTitles
        done = raw_input("Done?: ")
        
    writeToExcel(listOfTitles, allRanks )
 

if __name__ == "__main__" : main()



More information about the Tutor mailing list