Welcome!

Sign in with your CogSci, Facebook, Google, or Twitter account.

Or register to create a new account.

We'll use your information only for signing in to this forum.

Supported by

[open] Global/local variables and lists

JákupJákup Posts: 47
edited December 2012 in OpenSesame

Greetings.

I'm a bit confused about the local/global variable stuff. At the moment, I'm coding three inline_scripts that create random lists, draw from one of them, and use the drawn item for another task, respectively. They are all part of the same trial sequence. In the first script, variables and lists are defined and set globally, and are then used in the two latter scripts.

Here's the first script:

import random
global list1, list2, random

list = [] # create empty list

# append five 1s and 2s to the list:
for i in range(5): 
    list.append(1)
    list.append(2)
    
# define shuffle list function, which takes a list as its argument:
def shuffleList(list): 
    L = list[:] # copy the input list
    random.shuffle(L) # and shuffle the copy
    return L # return shuffled copy

# create 2 shuffled lists:
list1 = shuffleList(list) 
list2 = shuffleList(list)

My questions are:

1) Am I correct in assuming, that I only need exp.set or exp.get if I wish to use these variables and lists in other items, outside this sequence?

2) How come I need to declare random global in order to use it in my shuffleList function?

Best regards,

Jákup

Comments

  • WouterWouter Posts: 48
    edited 8:10PM

    Hi Jákup,

    Your questions go a lot deeper than you probably thought:

    Am I correct in assuming, that I only need exp.set or exp.get if I wish to use these variables and lists in other items, outside this sequence?

    'normally', yes: for communication between multiple items (or the 'run' and 'prepare' phases for that matter) can best be done through exp.get() and exp.set(). However, in this thread Lotje mentioned how Opensesame treats list variables: as strings (they're not ints or floats). This is somewhat problematic if you wish to convert them back to their original types. E.g. In 0.26, I tested setting:


    prepare: exp.set('mylist',[1,2,3,4]) run: x = exp.get('mylist')

    which crashed because OS attempted to evaluate '[1,2,3,4]' as one bracket-enclosed variable.

    The solution would be to circumvent the get and set methods, by simply creating your variable in the exp object directly, e.g.


    prepare: exp.mylist = [1,2,3,4] run: x = exp.mylist

    Some words of warning though:
    • variables set as such will not be logged by the logger

    • make sure your variable names do not overwrite critical OS fields: for example don't say exp.width = 15 because this overwrites OS's variable for the screen width.

    As for your second question;

    How come I need to declare random global in order to use it in my shuffleList function?

    Internally, your script is not run as a python program. Rather, it is represented as one string, and then the functions inline_script.prepare() and inline_script.run() compile and execute this string by calling python's compile() and exec() functions.

    This works -almost- the same, except for some limitations. Because you're running things from within a function (prepare() or run()), modules and variables are defined with only a local scope, i.e. defined within that function. If you now define another function within this function (i.e. your shuffleList function), python would usually transport these local variables and modules into the inner function.
    However, with exec() this doesn't happen (here's a thread to get you started on why ). Local variables and modules are not passed onto the inner function, and therefore random is not defined in this function.

    You can see this in a python terminal, using the locals() and globals() functions:


    def comp_and_run():
    exec('import random\ndef fun():\n print locals();\n print random.choice([5,6])\n\nfun()') comp_and_run();

    yields:

    {} # i.e. locals() is empty in the inner function! and then an error: Traceback (most recent call last):
    File "bla.py", line 10, in <module>
    comp_and_run();
    File "bla.py", line 7, in comp_and_run
    exec('import random\nprint locals(); print globals()\ndef fun():\n print locals();print globals();\n print random.choice([5,6])\n\nfun()')
    File "<string>", line 7, in <module>
    File "<string>", line 5, in fun
    NameError: global name 'random' is not defined

    Whereas when running the same program as 'regular code'


    def comp_and_run():
    import random
    def fun():
    print locals();
    print random.choice([5,6])
    fun() comp_and_run()

    runs fine, yielding:

    {'random': (...and then some more...)}
    ** # i.e. the locals have been copied into fun() ** and then it runs fine: 6

    ...because as you can see, the 'locals' are transported into fun.

    it's pretty technical, but I hope this clarifies matters a bit!

    As for your program; if you don't want to type (shuffle) the whole time, you can always define a for-loop, right?


    import random list1 = [1,2] * 5 # a python 'trick', easier than appending with a loop
    list2 = list1[:] # deep copy of list1 for l in [list1, list2]:
    random.shuffle(l) and setting them in exp: exp.my_list1 = list1
    exp.my_list2 = list2

    Hope this helps,

    Wouter

  • JákupJákup Posts: 47
    edited December 2012

    Dear Wouter.

    Thank you very much for your excellent explanation! This really cleared things up for me. You are right in saying, it ran deeper than I had imagined! I actually thought long about whether or not I should even post, as the questions seemed almost too simple. :)

    And thanks for the 5x multiplication trick instead of appending. Another great example of how elegant Python can be :)

    Best regards,

    Jákup

  • Try this detailed tutorial on...python list

Sign In or Register to comment.