Haskell has an equivalent of with for files, it's called withFile. This:
with open("file1", "w") as f:
    with open("file2", "r") as g:
        k = g.readline()
        f.write(k)
is equivalent to:
withFile "file1" WriteMode $ \f ->
  withFile "file2" ReadMode $ \g ->
    do k <- hGetLine g
       hPutStr f k
Now, withFile might look like something monadic. Its type is:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
right side looks like (a -> m b) -> m b.
Another similarity: In Python you can skip as, and in Haskell you can use >> instead of >>= (or, a do block without <- arrow).
So I'll answer this question: is withFile monadic?
You could think that it can be written like this:
do f <- withFile "file1" WriteMode
   g <- withFile "file2" ReadMode
   k <- hGetLine g
   hPutStr f k
But this doesn't type check. And it cannot.
It's because in Haskell the IO monad is sequential: if you write
do x <- a
   y <- b
   c
after a is executed, b is executed and then c. There is no "backtrack"
to clean a at the end or something like that. withFile, on the other hand,
has to close the handle after the block is executed.
There is another monad, called continuation monad, which allows to do such
things. However, you have now two monads, IO and continuations, and using effects of two monads at once requires using monad transformers.
import System.IO
import Control.Monad.Cont
k :: ContT r IO ()
k = do f <- ContT $ withFile "file1" WriteMode 
       g <- ContT $ withFile "file2" ReadMode 
       lift $ hGetLine g >>= hPutStr f
main = runContT k return
That's ugly. So the answer is: somewhat, but that requires dealing with a lot of  subtleties that make the issue rather opaque.
Python's with can simulate only a limited bit of what monads can do - add entering and finalization code. I don't think you can simulate e.g.
do x <- [2,3,4]
   y <- [0,1]
   return (x+y)
using with (it might be possible with some dirty hacks). Instead, use for:
for x in [2,3,4]:
    for y in [0,1]:
        print x+y
And there's a Haskell function for this - forM:
forM [2,3,4] $ \x ->
  forM [0,1] $ \y ->
    print (x+y)
I recommed reading about yield which bears more resemblance to monads than with:
http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html
A related question: if we have monads, do we need exceptions?
Basically no, instead of a function that throws A or returns B you can make a function that returns Either A B. The monad for Either A will then behave just like exceptions - if one line of code will return an error, the whole block will.
However, that would mean that division would have type Integer -> Integer -> Either Error Integer and so on, to catch division by zero. You would have to detect errors (explicitly pattern match or use bind) in any code that uses division or has even slightest possibility of going wrong. Haskell uses exceptions to avoid doing this.