This doesn't seem to be the case for me
-> % python test.py 2>&1 | python test.py
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
[snip]
python 24150 someone 0u CHR 136,0 0t0 3 /dev/pts/0
python 24150 someone 1w FIFO 0,8 0t0 72772851 pipe
python 24150 someone 2w FIFO 0,8 0t0 72772851 pipe
python 24150 someone 3w CHR 5,0 0t0 1049 /dev/tty
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
[snip]
python 24151 someone 0r FIFO 0,8 0t0 72772851 pipe
python 24151 someone 1u CHR 136,0 0t0 3 /dev/pts/0
python 24151 someone 2u CHR 136,0 0t0 3 /dev/pts/0
python 24151 someone 3w CHR 5,0 0t0 1049 /dev/tty
someone@arch-two [01:29:00] [~]
-> % echo $ZSH_VERSION
5.2
someone@arch-two [01:29:16] [~]
-> % cat test.py
import os
import sys
import subprocess
with open("/dev/tty", "w") as f:
subprocess.check_call(["lsof", "-p", str(os.getpid())], stdout=f)
Edit: Ok so I misread your comment and you were talking about >&2. Which is weird because it actually has different behaviour in bash vs zsh. Having said I can't think why you would want the bash behaviour, although the bash behaviour does seem more consistent.