‘Pythonic’ is a matter of judgment and interpretation. And you won’t find much judgmentalism here about ‘correct’ programming style. Do whatever works for you.
A lot of the PsychoPy coder demos and examples are perhaps not very ‘Pythonic’, in that they are simply a list of instructions in order: create this object, change this attribute, draw it to the screen, collect this response, etc. This is probably a deliberate approach to make sense to experimenters learning to program rather than to programmers learning to make experiments. i.e. the code simply describes the list of operations in the experiment. People coming from a programming background might want their code to look more beautiful, to minimise repetition by using functions and iterators, and encapsulate functions and state in custom objects, etc, and are free to do so.
Basically, adopt whatever style works for you. But the one thing that trumps everything in the PsychoPy world is performance. You want stimuli to appear on time, with the desired attributes, and for responses to be collected with fidelity. Those are the issues you’ll get best feedback from in this forum.
So in answer to that specific aspect of your question, yes it is (usually) perfectly fine to have just one TextStim
for the entire experiment. In fact, it is usually desirable to minimise the creation of stimuli (which is time-expensive), and wherever possible just update the attributes of existing ones.Meanwhile, purely for clarity in the code, some people might want to create separate text stimuli named, say, instruction_text
and feedback_text
. It can make the experiment code more readable, and doesn’t consume any meaningful extra amount of memory. But you could certainly have a generic_text
stimulus, and alter its contents as needed.
But note that altering text stimulus content is a slower process than updating most other stimuli. So when performance is critical (e.g. showing different text stimuli on alternating screen refreshes), then it would definitely be better to do this:
# set the content of two stimuli just once:
text_1.text = 'hello'
text_2.text = 'goodbye'
while True: # so can very quickly alternate stimuli
text_1.draw()
win.flip()
text_2.draw()
win.flip()
rather than this:
# needlessly update content of a single stimulus repetitively:
while True:
text_1.text = 'hello'
text_1.draw()
win.flip()
text_1.text = 'goodbye'
text_1.draw()
win.flip()
i.e. if you update enough text stimuli, eventually you might find that the code takes longer than one screen refresh to execute (and has caused memory leaks in underlying frameworks before). But if you are just going to present instruction text for the duration of a trial, then updating it is fine. (Note that in your function example above, you are re-settting the text content on every single refresh, even though that content is not changing. PsychoPy is clever enough to test for that, and only update text content if the string has actually altered, but you should be wary of needless updating).
Lastly, my only other comment on your proposed structure is that embedding the win.flip()
within small functions can become limiting. Think more about the structure of your code. Will you also be collecting responses? Then that code needs to be within the function too. But it would also need to be within the other functions, so you might be getting a lot of duplication. i.e. the stimuli might change from one portion to the next, but response collection might not. So it might be best to have your functions just draw stimuli (without flipping the window), and then do other tasks outside those functions.
Before thinking about specific functions, think first of the wider cyclic nature of the experiment. Generally you are passing through phases of an experiment, and then looping over trials within one or phases, and lastly looping over screen refreshes within portions of those trials. How you might divide that process up into functions, or embed functions within those loops, is up to you. But the ExperimentHandler
and TrialHandler
classes do a lot of the housework for providing a structure for your experiment, saving data, and so on, and it can be good to start with those and try to incorporate them within whatever coding approach you like.