using "with" statement on class methods - python

I'm required to use "with" on a method of an object and not on the object itself.
Here is what I've already tried:
class LSTM:
...
def run(self):
def __enter__(self):
do something
return self
def __exit__(self, type, value, tb):
return self
An example of I want to use the function in main:
lstm = LSTM(...)
with lstm.run():
...
The error I get:
AttributeError: __enter__

The object returned by your method must be a context manager. Write your method as a generator and apply the contextlib.contextmanager decorator to automatically create the proper helper object:
from contextlib import contextmanager
class LSTM:
#contextmanager
def run(self):
# prepare
yield self
# clean up
The object created by the decorator uses anything before the yield as __enter__, and anything after it as __exit__. Whatever is provided by yield is available for use in the as clause of the with statement. If an error terminates the context, it is raised at yield.

When you write:
with soemthing:
Then the object soemthing needs to have those __enter__ & __exit__ methods.
So for:
with lstm.run():
The object returned by lstm.run() needs to have the __enter__ and __exit__ methods - the fact that lstm has those methods is of no consequence.
What you should do will depend on what you're trying to achieve. But this:
with lstm:
Will call the __enter__ & __exit__ methods you have defined.

The context expression lstm.run() of your with statement does not evaluate to a context manager but to None, since there is no return statement in your run method.
Cf. https://docs.python.org/3/reference/compound_stmts.html#the-with-statement

Related

Python context managers in sqlalchemy session class (please explain)

I am having a deeper look at the Session class inside the Sqlalchemy library in sqlalchemy.orm.session.py (link here) and i see this block inside the Session class at line 1170:
#util.contextmanager
def _maker_context_manager(self):
with self:
with self.begin():
yield self
I don't understand the syntax and what it does. Why is there a with self: at the start? Can we use with and any class? Can someone please explain this and how it is useful in the context of sqlalchemy session?
with self: points to __enter__ and __exit__ methods. These methods act as context managers. Here's an excerpt from Python's doc on context managers:
Python’s with statement supports the concept of a runtime context defined by a context manager. This is implemented using a pair of methods that allow user-defined classes to define a runtime context that is entered before the statement body is executed and exited when the statement ends:
contextmanager.__enter__(): Enter the runtime context and return either this object or another object related to the runtime context [...]
contextmanager.__exit__(exc_type, exc_val, exc_tb): Exit the runtime
context [...].
From Session (here):
def __enter__(self: _S) -> _S:
return self
def __exit__(self, type_: Any, value: Any, traceback: Any) -> None:
self.close()
Here, __enter__ returns the instance of the class. And __exit__ helps with graceful termination (closing the session when you exit the scope). Acting as internal context managers.

What is assigned to `variable`, in `with expression as variable`?

From Learning Python:
The basic format of the with statement looks like this, with an
optional part in square brackets here:
with expression [as variable]:
with-block
The expression here is assumed to return an object that supports the
context management protocol (more on this protocol in a moment).
This object may also return a value that will be assigned to the name variable if the optional as clause is present.
Note that the variable is not necessarily assigned the result of
the expression; the result of the expression is the object that
supports the context protocol, and the variable may be assigned
something else intended to be used inside the statement.
expression is evaluated to a context manager object.
What is assigned to variable? The quote only says that it is not a context manager object.
Does the assignment to variable call some method of a context manager class to produce the actual value assigned to variable?
Thanks.
Whatever is returned from __enter__. From the documentation on the __enter__ method of context managers:
contextmanager.__enter__()
Enter the runtime context and return either this object or another object related to the runtime context. The value returned by this method is bound to the identifier in the as clause of with statements using this context manager.
(Emphasis mine)
The result of calling __enter__ could very well be a context manager, nothing in the specification forbids this. It could of course be another object related to the runtime context, as the docs state.
Objects that return themselves from __enter__ can be used again and again as context managers. file objects, for example:
with open('test_file') as f1: # file.__enter__ returns self
with f1 as f2: # use it again, get __self__ back
print("Super context managing")
with f2 as f3, f1 as f4: # getting weird.
print("This can go on since f1.__enter__ returns f1")
print("f1.__exit__ has been called here, though :)")
print("f1 closed: {}".format(f1.closed))
Not that the previous made much sense but just to make the point clear.
Your object can function as a context manager if it provides both __enter__ and __exit__. The object returned by __enter__ is bound to the object you specify in the as part of the with statement:
In [1]: class Foo:
...: def __enter__(self):
...: return 'hello'
...: def __exit__(self, *args):
...: pass
...:
In [2]: with Foo() as a:
...: print(a)
...:
hello

Can I make a class with an `__exit__` method but not an `__enter__` method?

I have a class that needs to run a TensorFlow session for each instance of the class, as long as that instance exists.
TensorFlow sessions use context managers, but I don't want to force anyone who uses my class to put my class into a context manager.
Is there any way to auto-close the session once the instance is no longer in use without using a context manager?
Can I just put in an __exit__ method and not an __enter__ method and start the session without the context manager and just close the session in the exit?
Is there any way to auto-close the session once the instance is no longer in use without using a context manager?
Not really, how would an object figure out when it’s no longer being used? If there was a safe way to do this, there wouldn’t be a need for context managers in the first place.
So you have to use context managers and the with statement to get this kind of feedback. But just because you have to use context managers, that does not mean that you actually need to have some separate “thing” you open. You can return anything in the __enter__ method, including the current object.
So the simplest context manager implementation that closes itself when the context is closed looks like this:
class MyClass:
def __enter__ (self):
return self
def __exit__ (self, *exc):
self.close()
def close (self):
# actually close the object
In fact, this pattern is so common, that there is a built-in recipe for this context manager: contextlib.closing. Using that, you do not actually need to modify your class at all, you can just wrap it in a closing() call and have it call close when the context is exited:
with closing(my_object):
my_object.do_something()
# my_object.close() is automatically called
You must define an __enter__ method, but you can just define it as:
def __enter__(self):
return self
and have the session defined in the init. Then, define __exit__ like so:
def __exit__(self, *exc):
self.close()
Then, define a close method that closes whatever resources were opened in __init__. (In my case, it's a TensorFlow session.)
This way, if the user decides to use the context manager, it will close it for them, and if they don't, they'll have to close it on their own.

What's the Pythonic way to write an auto-closing class?

I'm a noob with Python, but I've written an auto-close function like this..
#contextmanager
def AutoClose(obj):
try:
yield obj
finally:
obj.Close()
I have three classes that have a Close() method that this function can be used with. Is this the most Pythonic solution? Should I be doing something in the classes themselves instead?
Most pythonic solution is to define methods __enter__ and __exit__ methods in your class:
class Foo(object):
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.fd = open(self.filename)
def __exit__(self, exc_type, exc_value, traceback):
self.fd.close()
And using:
with Foo('/path/to/file') as foo:
# do something with foo
Methods __enter__ and __exit__ will be implicitly called when entering and leaving blocks with. Also note that __exit__ allows you to catch exception which raised inside the block with.
Function contextlib.closing is typically used for those classes that do not explicitly define the methods __enter__ and __exit__ (but have a method close). If you define your own classes, much better way is to define these methods.
What you're doing looks totally fine and Pythonic. Although, the contextlib standard library already has something similar, but you'll have to rename your Close methods to close.
import contextlib
with contextlib.closing(thing):
print thing
I would recommend using this instead. After all, the recommended naming convention for Python methods is all_lowercase_with_underscores.

Object becomes None when using a context manager

Why doesn`t this work:
class X:
var1 = 1
def __enter__(self): pass
def __exit__(self, type, value, traceback): pass
with X() as z:
print z.var1
I get:
print z.var1
AttributeError: 'NoneType' object has no attribute 'var1'
Change the definition of X to
class X(object):
var1 = 1
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
pass
with assigns the return value of the __enter__() method to the name after as. Your __enter__() returned None, which was assigned to z.
I also changed the class to a new-style class (which is not critical to make it work).
See the docs for context managers:
__enter__( ) Enter the runtime context and return either this object or
another object related to the runtime
context. The value returned by this
method is bound to the identifier in
the as clause of with statements using
this context manager. An example of a
context manager that returns itself is
a file object. File objects return
themselves from __enter__() to allow
open() to be used as the context
expression in a with statement.
An example of a context manager that
returns a related object is the one
returned by
decimal.Context.get_manager(). These
managers set the active decimal
context to a copy of the original
decimal context and then return the
copy. This allows changes to be made
to the current decimal context in the
body of the with statement without
affecting code outside the with
statement.
Your __enter__ method doesn't return anything, which is the same as returning None.
The function you've defined between 'with' and 'as' must have and only have one return value. 'with' will pass the value to its built-in method __enter__().
A class-type object in Python will not return any value if not defined when you called it.
Similarly, if you called a class-type object with its method that returns nothing, it will also throw out exception.
You can't write like this:
with open('file.txt').readlines() as lines:
This generated two return values, and it's even not allowed to pass to one variable in Python.
But this is good to use:
with open('file.txt') as f:
lines = f.readlines()

Categories

Resources