In my post on unravelling the
global statement, I mentioned how after my PyCascades 2023 talk some people came up to me about a couple of pieces of Python syntax that I had not managed to unravel. Beyond
global, people thought I should be able to get rid of the
del statement. Turns out that a bit of extra work I did in my
global statement post lets me unravel
del as well!
global statement post I talk about the (roughly) 3 namespaces that Python has:
In order to support
del, I need to figure out how delete names from the local and global namespaces (you will trigger a
NameError if you try and delete something in the built-in namespace).
Deleting from the local namespace
To delete a name in the local namespace, you need to make sure that if someone tries to use that same name later on, it causes an
UnboundLocalError to be raised. Since there's no way to directly manipulate the local namespace, we can assign a marker to tell us that a local name has been "deleted" (this also allow for garbage collection as expected). So
del A can become
_DELETED = object(); A = _DELETED. But we also have to detect if
A is used after deletion to raise
UnboundLocalError as appropriate.
You might be wondering why are we checking for our
_DELETED marker when we can look at some code and know that
del A was called, effectively making the
A name (seem) useless going forward? Well, think about what happens if
A was deleted in an
if block? That would mean
A could be deleted, but not necessarily deleted unconditionally.
Because local names can be used in expressions, we do have to unravel uses of potentially deleted names just like assignment expressions had use to make new names appear appropriately. It's a bit tedious, but it does mean Python's rules around evaluation is upheld while still allowing for
del to operate appropriately.
Deleting from the global namespace
global unravelling post, I talked about how to identify when a name was global or built-in compared to a local name. That's important here because deleting a global name is like deleting a key from a dictionary thanks to the
globals().__delitem__("A"). And if a name happens to not to be in that global dictionary, then
NameError should be raised since you can't directly delete something from the built-in namespace.