Up until recently, if I needed to launch a child process from a Python program, I would use the system() function in the os module, because it was the easiest one for me to remember how to use. This is despite the fact that system() is rarely what I actually wanted.
For starters, os.system() runs the command in a shell, so unless you are actually using the shell redirection operators or argument expansion, the function is just launching a shell in order to run the program you really wanted to start in the first place. Second, the shell is going to interpret your arguments the way shells do — splitting arguments on spaces, expanding wildcards, etc. If one of the arguments is a filename with spaces and shell special characters, you have to escape it very carefully (and in a system-specific way) to prevent the shell from messing with it. Naturally, I often just wouldn’t bother and thus my commands would fail whenever a strange filename showed up.
Before the introduction of the subprocess module in python 2.4, the alternatives were either the popen-style calls, or the spawn* family of functions, or possibly the ‘commands’ module. There are two implementations of the popen calls, one in the os module and one in the popen2 module, with different calling conventions. The spawn functions are simple wrappers around the C library calls of the same name, and thus feel very different from a typical python function. None of the alternatives are particularly easy to remember, a problem compounded by the sheer number of them and their different calling conventions.
The subprocess module has pretty much supplanted all of them with a straightforward and very pythonic interface. It’s easy to remember, the defaults are what you would expect, and all the arguments and methods have intuitive names. To everyone who contributed to it: well done.