0

I was pulling my hair trying to fix a bug on some two dimensional list manipulations. The following script simply initializes a few empty lists, then does basic calculations and returns the output. The problem occurs when I call the main function, here is the code:

dataList=[]
outputList1=[]
outputList2=[]
fileData=["a","b","c"]

def initialize(*args):
    global dataList

    dataList=[[]for j in range(len(fileData))] #initialize the array with empty lists
    for x in range(len(fileData)):
        for y in range(12):
            dataList[x].append(y) #populates the list with data

    for x in args:
        for y in range(len(fileData)):
            x[1][2].append([]) #initializes the output lists with empty lists

def functionAdd(number, inputList, outputList):#basic data manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList[y].append(inputList[y][x]+number)

def functionSubstract(number, inputList, outputList):#other basic manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList.append(inputList[y][x]-number)

def mainFunction(*args):#unpacks the function names to be executed, as well as their arguments
    for x in args:
        x[0](*x[1])

f1=[functionAdd,(2,dataList,outputList1)]#variable of a function to be executed
f2=[functionSubstract,(3.5,dataList,outputList2)]#variable of a function to be executed

initialize(f1,f2)#this part works fine
mainFunction(f1,f2)#this part does't work
##mainFunction([functionAdd,(2,dataList,outputList1)],[functionSubstract,(3.5,dataList,outputList2)])
print(outputList1)

The initialization goes without issues with (initialize(f1,f2)), it unpacks the arguments and creates empty sub-lists in the corresponding list to make a 2-dimensional array.

However, when I try to call the "mainFunction" the same way, the print statement displays: [[], [], []], so basically no calculation has been done to it.

But, when I un-comment the last line and instead call the mainFunction in this way:

mainFunction([functionAdd,(2,dataList,outputList1)],[functionSubstract,(3.5,dataList,outputList2)])

then I get the correct output, and the print statement displays:

[[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]

which is the correct result. So I finally got the function to work, but I still don't understand what's wrong with the previous syntax of calling it

mainFunction(f1,f2)

because in both cases I call the function with the exact same arguments...

Can anyone care to explain? Thanks,

1
  • You're reassigning dataList in your initialise function. It's no longer the same object you've passed to f1 & f2. Remove the dataList= from the initialise function and insert dataList.append([]) after for x in range(len(fileData)): and it should do what you want. Commented Jul 5, 2021 at 1:26

1 Answer 1

2

In the initialize function, you are assigning a new value (a list) to the variable dataList, while the variable dataList in:

f1=[functionAdd,(2,dataList,outputList1)]
f2=[functionSubstract,(3.5,dataList,outputList2)]

is the previously assigned variable dataList.

You can check this with id(), which shows the unique ID of an object in python:

dataList=[]
outputList1=[]
outputList2=[]
fileData=["a","b","c"]

print(f'Initial dataList, ID: {id(dataList)}')

def initialize(*args):
    global dataList

    dataList = [[] for j in range(len(fileData))]  # initialize the array with empty lists
    for x in range(len(fileData)):
        for y in range(12):
            dataList[x].append(y) #populates the list with data

    for x in args:
        for y in range(len(fileData)):
            x[1][2].append([]) #initializes the output lists with empty lists

    print(f'dataList in f1 & f2, ID: {id(args[1][1][1])}')
    print(f'dataList after reassigning dataList, ID: {id(dataList)}')


def functionAdd(number, inputList, outputList):#basic data manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList[y].append(inputList[y][x]+number)

def functionSubstract(number, inputList, outputList):#other basic manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList.append(inputList[y][x]-number)

def mainFunction(*args):#unpacks the function names to be executed, as well as their arguments
    for x in args:
        x[0](*x[1])

f1=[functionAdd,[2,dataList,outputList1]]#variable of a function to be executed
f2=[functionSubstract,[3.5,dataList,outputList2]]#variable of a function to be executed

initialize(f1,f2)#this part works fine
mainFunction(f1,f2)#this part does't work
##mainFunction([functionAdd,(2,dataList,outputList1)],[functionSubstract,(3.5,dataList,outputList2)])
print(outputList1)

Output:

>>>Initial dataList, ID: 2168655372096
>>>dataList in f1 & f2, ID: 2168655372096
>>>dataList after reassigning dataList, ID: 2168657467648
>>>[[], [], []]

You can see now that after the statement dataList = [[] for j in range(len(fileData))], the dataList's ID has changed. You are actually doing the calculations with the new variable dataList instead of the old variable dataList in f1&f2. This is why you don't get the correct output when calling mainFunction(f1,f2).

Calling mainFunction([functionAdd,(2,dataList,outputList1)],[functionSubstract,(3.5,dataList,outputList2)]) does give you the correct output because you now pass in the new variable dataList, which had the calculations.

You should do operations with the same variable dataList, instead of reassigning the variable dataList.

Example:

dataList=[]
outputList1=[]
outputList2=[]
fileData=["a","b","c"]

print(f'Initial dataList, ID: {id(dataList)}')

def initialize(*args):
    global dataList

    for i in range(len(fileData)): #initialize the array with empty lists
        dataList.append([])

    for x in range(len(fileData)):
        for y in range(12):
            dataList[x].append(y) #populates the list with data

    for x in args:
        for y in range(len(fileData)):
            x[1][2].append([]) #initializes the output lists with empty lists

    print(f'dataList in f1 & f2, ID: {id(args[1][1][1])}')
    print(f'dataList after operations with dataList, ID: {id(dataList)}')


def functionAdd(number, inputList, outputList):#basic data manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList[y].append(inputList[y][x]+number)

def functionSubstract(number, inputList, outputList):#other basic manipulation
    for y in range(len(inputList)):
        for x in range(len(inputList[y])):
            outputList.append(inputList[y][x]-number)

def mainFunction(*args):#unpacks the function names to be executed, as well as their arguments
    for x in args:
        x[0](*x[1])

f1=[functionAdd,[2,dataList,outputList1]]#variable of a function to be executed
f2=[functionSubstract,[3.5,dataList,outputList2]]#variable of a function to be executed

initialize(f1,f2)#this part works fine
mainFunction(f1,f2)#this part does't work
##mainFunction([functionAdd,(2,dataList,outputList1)],[functionSubstract,(3.5,dataList,outputList2)])
print(outputList1)

Output:

>>>Initial dataList, ID: 2560807861056
>>>dataList in f1 & f2, ID: 2560807861056
>>>dataList after operations with dataList, ID: 2560807861056
>>>[[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]
Sign up to request clarification or add additional context in comments.

1 Comment

Nice, indeed your solution fixed it. Since I was calling "global dataList", I thought that the list comprehension syntax would still point to that object, but yeah doing it with the append method is better. Thanks,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.