This is my first foray into POSIX system programming, so I could be misunderstanding things quite badly.
Yep.
POSIX file descriptors are just numbers - they're not objects, so you can't override their methods. For example, 0, 1, and 2 are all [usually] valid file descriptors.
"the relevant system calls" are built in to the Linux kernel. The Linux kernel itself maintains a list that maps file descriptors to some internal kernel object (which does have methods!) but you can't insert a new file descriptor from Python. Code running in kernel space is very different from normal ("user mode") code.
Can I suggest you look at subprocess.PIPE, and either the stdout/stdin/stderr properties or the communicate() method on subprocess.Popen objects? This will let you start a subprocess, read the data that it outputs, and have full control of the data that gets sent to it. (I think this is what you're really trying to do...). If you're curious, then when you've played with this you can look at the subprocess.py source code to see how it works.
There's an example of subprocess.PIPE here.
Alternatively, if you actually want to implement a full filesystem in Python, look at FUSE, and it's Python bindings. FUSE includes a C module that runs in the kernel, and handles filesystem requests for a certain directory. It handles them by passing them to a userspace program, which could be written in Python. You can open those files from a separate Python program, to get a file descriptor to them. This is kind of complex, and probably not the best place for a beginner to start.