psychopy.org | Reference | Downloads | Github

Mutability of psychopy.visual.text.TextStim classes

I thought Python classes were mutable, and wrote the following code to check whether it is true for the visual.text.TextStim class. My code :

win = visual.Window(...)

stim_H = visual.TextStim(win=win, text=u'H' ....)
stim_S = visual.TextStim(win=win, text=u'S' ....)

running_stim = stim_H
running_stim = stim_S

print stim_S == stim_H

which returns False. Could you clarify why ?
I checked this is because I wanted to avoid things code like the one below when drawing stimuli in my experiment:

if condition == X:
    stim_H.draw()   
elif  condition == Y:
    stim.S.draw()  
etc...

which usually takes time when lots of statements have to be checked. Instead, I wanted to do these computations in the intertrial interval, and have something like:

if condition == X:
    running_stim = stim_H   
elif  condition == Y:
    running_stim = stim_S  
etc...

and then just draw running_stim in the critical section. Everything works great (timing is improved), except that I was expecting PsychoPy classes to be mutable…

Hi,

there seems to be some confusion here as to what mutability is and how to test for it:

# below you are creating two separate objects, each with a different name 
# pointing to it:
stim_H = visual.TextStim(win=win, text=u'H' ....)
stim_S = visual.TextStim(win=win, text=u'S' ....)

# now you also point a different name to the first object:
running_stim = stim_H
# i.e. it is still a single text stimulus object, but just has two 
# different names pointing toward it.

# now immediately you redirect the name running_stim to point to 
# the other stimulus:
running_stim = stim_S

# the current situation is that you have two TextStim objects, the first 
# pointed at by one name (stim_H) and the second by two names 
# (stim_S and running_stim)

# Nothing has changed just because you have introduced a third name 
# into the mix, there are still two different objects, containing 
# different content, and so this comparison will always return False:
print stim_S == stim_H

There is nothing PsychoPy-specific here: this is just the way that names refer to objects in Python. There is actually no mutation going on here at all. You can assign different additional names to objects, that doesn’t change them in any way (i.e. doesn’t mutate them).

So I’m not sure what the issue is. The code you wanted to apply (i.e. point the name running_stim to one of several stimuli, and then calling running_stim.draw()) should indeed work without any problem, and should be the expected behaviour in Python. And I think that is what you hope to happen?

PS when testing for identity of objects in Python, you probably want to use is rather than == e.g. try this:

a = [1, 2, 3]
b = [1, 2, 3]

a == b # True  (they contain the same values)
a is b # False (they aren't the same object)

b = a  # assign both names to point to the same object
a is b # True (there is now only one object, with two names)

You can explore this using id(a) and id(b) to get the memory address of each object. Initially they are different (showing that they can’t be same object). After doing a = b, id(a) and id(b) return the same number, indicating that each name is pointing to the same object (meanwhile, the object originally pointed to by the name b, which now has no pointers to it, has in fact silently been erased from existence).

Perhaps the conceptual leap to make here is that names of objects aren’t the objects to which they refer. Think of an object as a real thing. You can stick as many post-it notes on such an object as you want (where each posit-it note is equivalent to a name). The names don’t change the object, and in fact you can peel off any of the notes at any time and stick them on a different object (which is what you do in your conditional drawing code). There is nothing special about which name you use to refer to an object, and in fact objects don’t even know their own name(s). The only restriction is that to continue existing, a Python object needs to have at least one name pointing to it (otherwise it is just floating unreachable in the vacuum of space).

Complications arise when you are dealing with that small number of Python classes (like strings and integers) that actually are immutable:

a = 'hello'
b = a

a == b # True
a is b # True - they point to the same object

a = a + 'bye' # a now points to a newly-created object, 
# as the original is immutable

a is b # False: b still points to the original 'hello' object

1 Like

Hi Mike,
Many thanks for the thorough comments (very much appreciated) and sorry for the mutability stuff, I was confused. What I am trying to do is to avoid putting lots of conditional statements (depending on experimental conditions) when drawing stimuli in time-critical parts of my experiments (the example I gave in my previous post is minimalist, but complex experiments may sometimes require many conditional statements to determine the stimulus to be drawn for the current trial) . So I was thinking of doing these statements in the intertrial interval, and simply refer to the to-be drawn stimuli by a ‘running_stim’ variable that I would draw afterwards. What do you think of this idea ?

Yes, that seems fine, and should work exactly as you propose above. i.e. Based on a number of decisions, you assign a single name (running_stim) to point to any one of a number of possible stimuli, and then use that name later to control drawing, positioning, etc.

running_stim can then be pointed to any other stimulus as required. The original names also remain in effect for each stimulus. It is a perfectly sensible approach. :+1: