psychopy.org | Reference | Downloads | Github

For loop in a custom function does not work


#1

Hi everyone,

I made a custom function that automatically generates a trial list.

But it doesn’t assign values correctly.

I think the problem is related the for loop I used in my function, or I think it could be the arguments I passed in to the function that caused the problem.

Here is my code:

def make_trials(trial_info, num_trial, stop_ratio):
    '''
    Make trial list
    '''
    trials = []

    num_stop = int(num_trial * stop_ratio)

    for i in range(num_trial):
        trials.append(trial_info)
        if i < int(num_trial*stop_ratio):
            trials[i]['go_stop'] = 1
        else:
            trials[i]['go_stop'] = 0


    #trials = sample(trials, len(trials))

    return trials

trial_info is a dictionary containing trial information. So, if stop_ratio is 1/2 it should return a list that contains half of the element with ‘go_stop’ value of 1, and the remainder with ‘go_stop’ value of 0

test_trials = make_trials(test_info, 10, 1/2)

This returns a list in which all of its elements have ‘go_stop’ value of 1, or 0 sometimes.

How can I solve this problem?


#2

trial_info is a single object. Each time you append it to a list, what you are actually appending is just a new reference to that single object. So when you change the 'go_stop' value of any of those references, you change that value for all entries in the list simultaneously. That is why you see that all elements of your list have the same value in their dictionary, because all elements in your list actually point to the same dictionary.

e.g. try this:

b = trial_info
b['blah' = 'hello']
print(trial_info) # now has a 'blah' key

A variable name can only point to a single object, but there is no restriction on how many variable names can point to the same object.

So you need to explicitly add a copy of the dictionary to each list element:

trials.append(trial_info.copy())

#3

@Michael

So, do you mean the if-else lines updates trial_info itself, right?

I tried .copy() method, and it seemed worked but only in the same script.

When I import the make_trials module on other script the values of ‘go_stop’ key are all same.


#4

@Michael

More specifically,

from __future__ import unicode_literals, absolute_import, division
from random import sample

def make_trials(trial_info, num_trial, stop_ratio):
    '''
    Make trial list for SST
    '''
    trials = []

    for i in range(num_trial):
        trials.append(trial_info.copy())
        if i < int(num_trial*stop_ratio):
            trials[i]['go_stop'] = 1
        else:
            trials[i]['go_stop'] = 0

    trials = sample(trials, len(trials))

    return trials

test_info = {'id': 'test', 'go_stop': None}
test_trials = make_trials(test_info, 10, 1/2)

This is the whole script. When I run the script as a whole, everything’s fine. But when I run only the last line, same problem happens.

What can I do about this?


#5

Python can be lazy about importing. It may think it has already imported the module and so not do the whole process again (i.e. new changes in the module may not be reflected). There is some function to force a full reload of a module which is useful during active development, but I can’t recall it off the top of my head and think it might differ between Python 2 and 3.

But the issue will go away if you restart the Python interpreter: modules are always imported fully the first time in each session.