I dislike the argument about context managers being unusable if you have a class that needs to re-use some context across many methods.
There are a lot of ways to do so comfortably. A trick I really like is to use `contextlib.contextmanager`, and have a pure constructor and a separate static factory method that injects appropriate contexts. This also makes the code more loosely coupled in general :-)
from contextlib import contextmanager
class Session:
@classmethod
@contextmanager
def create(cls):
with db.connect(...) as connection:
yield cls(connection)
def __init__(self, connection: db.Connection):
self._connection = connection
def login(self, name, password):
self._connection.query(...)
with Session.create() as session:
...
class Session:
def __init__(self):
self._connection = db.connect(...)
def __enter__(self):
self._connection.__enter__(self)
return self
def __exit__(self, *args):
return self._connection.__exit__(self)
def login(self, name, password):
self._connection.query(...)
with Session() as session:
...
It might need a bit more knowledge about context managers [1], but it feels less magic and more explicit to me – i.e. more pythonic :). But it also contains the assumption that db.connection already returns the connection. If that is not true, a bit more housekeeping is needed:
it's subjective of course, but I find context manager decorators very pythonic, I still remember the 'whoa! that's beautiful' moment when I 1st learned about them.
But most importantly, generators as enclosures are used everywhere today with the whole async-await paradigm.
I absolutely agree – it's the specific usage in the class that I don't like. Having a factory instead of using the constructor, with that IMHO not that obvious interaction with the context manage. A factory that is actually a context manager just seems non-trivial, and given that there is more straightforward solution available, I would avoid it.
There are a lot of ways to do so comfortably. A trick I really like is to use `contextlib.contextmanager`, and have a pure constructor and a separate static factory method that injects appropriate contexts. This also makes the code more loosely coupled in general :-)