We're back after a server migration that caused effbot.org to fall over a bit harder than expected. Expect some glitches.

Why doesn't Python have a "with" statement like some other languages?

Starting with Python 2.5, Python does in fact have a with statement, which is used to control execution of code in a specific context:

with manager() as ctx:
    ... do something with ctx ...

Here, the object returned by the call to manager will be called before and after the with-body has been executed, no matter what happens inside the statement. This is primarily used for resource management. For example, the following statement guarantees that the file is closed after the file has been processed:

with open(filename) as f:
    process(f.read())

However, this article is about the kind of with-statement that’s available in Object Pascal and several other languages, where it is basically used to add the attributes of a given object to the current scope. In pseudo-Python, it could look something like:

with-object obj:
    attrib1 = value
    attrib2 = value

where attrib1 and attrib2 would then refer to object attributes, rather than a local variable with the same name.

Unfortunately, there’s no obvious way to implement such construct for Python, since it would be ambiguous.

Some languages, such as Object Pascal, Delphi, and C++, use static types. So it is possible to know, in an unambiguous way, what member is being assigned in a “with” clause. This is the main point — the compiler always knows the scope of every variable at compile time.

Python uses dynamic types. It is impossible to know in advance which attribute will be referenced at runtime. Member attributes may be added or removed from objects on the fly. This would make it impossible to know, from a simple reading, what attribute is being referenced — a local one, a global one, or a member attribute.

For instance, take the following incomplete snippet:

def foo(a):
   with-object a:
      print x

The snippet assumes that “a” must have a member attribute called “x”. However, there is nothing in Python that guarantees that. What should happen if “a” is, let us say, an integer? And if I have a global variable named “x”, will it end up being used inside the with block? As you see, the dynamic nature of Python makes such choices much harder.

The primary benefit of “with-object” and similar language features (reduction of code volume) can, however, easily be achieved in Python by assignment. Instead of:

function(args).dict[key][index].a = 21
function(args).dict[key][index].b = 42
function(args).dict[key][index].c = 63

write this:

ref = function(args).dict[key][index]
ref.a = 21
ref.b = 42
ref.c = 63

This also has the side-effect of increasing execution speed because name bindings are resolved at run-time in Python, and the second version only needs to perform the resolution once. If the referenced object does not have a, b and c attributes, of course, the end result is still a run-time AttributeError exception.

CATEGORY: general

CATEGORY: design