Nick Coghlan added another interesting note in response to my last post about creating side-effecting assignments in python. Here’s Nick:
Let’s look at that gist:
1 2 3 4 5 6 7 8 9 10 11 12 13
This is really fun stuff, and nicely illustrates a core feature of python – that namespaces are basically just dictionaries.
So what’s going on here? In this case, I think it’s easiest to work backwards from the end.
Last we have
exec code in Madness(). The
exec keyword in python 21 executes a string as code.
1 2 3
exec optionally takes a context in which to execute the code. A dictionary is a perfectly legal context to use:
All the code is shown here –
b is not defined elsewhere in the REPL session.
If a dictionary is provided, it’s presumed to contain both the global and the local variables. This dictionary is the only context in which the code will be executed (it won’t check the scope from which the
exec statement was made).
1 2 3 4 5 6
So without knowing anything about the
Madness() object, we’d expect it to be a dictionary-like thing.
And sure enough, it is:
__setitem__ is the standard way to override setting a key:value pair in a dictionary. It works just like you’d expect:
1 2 3 4
We’re intercepting any attempt to write to the Madness dictionary-like object.
1 2 3 4
If the value of the key:value pair that we’re setting is an instance of the
type type – that is, if the object in question is a class – then grab that class’s name and set it to the key. (This step will blow up if the key in question isn’t a legal name in the first place.) Then use the parent class (
__setitem__ to actually write to the Madness object.
We can execute the code string one line at a time to better see what’s happening.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
m.keys() to examine the state of the namespace instead of just printing
m because the reference to
__builtins__ pukes a bunch of tiresome definitions and copyright statements into the REPL.
exec adds a reference to
__builtins__ on the execution of any statement.)
We’re now quite close to the original Ruby behavior – with a deeper understanding of python namespaces!
All of this works in python 3, too, with slightly different syntax because
execis a function, not a keyword. Nick’s gist includes the python 3 version too.↩