Unravelling literals

In this post of my syntactic sugar series, I want to tackle literals. Now some literals don't really require any code changes and are really just an alternative way to write the same thing, no call of code required (e.g. writing integers in hexadecimal format versus in base-10). Others, though, can be done away with entirely using some code transformations (e.g. strings). Regardless, this blog post is somewhat academic and doesn't really help simplify the semantics of Python.

Numeric literals

You can break the numeric literals in Python down into integers, floats, and complex numbers. For integers and floats, the various representations directly translate to a base representation, e.g. integers can be written in binary, octal, hex, and base-10 and they are all equivalent.

For floats, they are effectively integers that have been divided by some power-of-ten number. For example, 3.14 is the same as 314/100.

Complex numbers also have a slight translation. If you write 4+3j you can reconstruct it with complex(4, 3).  And if you leave off the real part of the complex number then it simply defaults to 0: 3j becomes complex(0, 3).

True and False

The boolean keywords/literals can be created by calling bool(), i.e. bool(1) for True and bool(0) for False. The one interesting thing to note is you can't fully replicate bool() in pure Python code as there eventually needs to be a break as to what represents true and false as you can't keep calling __bool__() which is expected to itself return True or False.

None

Did you know that all Python functions return None if you don't explicitly return something? This is because there's no concept of a "void" function like in typed languages such as C. As such, everything in Python has to return something, and that something happens to be None. Thanks to this, it's easy to replace None as a function call.

def _None():
    """Implictly return None."""
    pass
Function that returns None without using it

So now any place you would write None you can write _None().

Bytes

Bytes literals, thanks to relying on ASCII, translate to lists of integers passed to bytes(): b"ABC" becomes bytes([65, 66, 67]).

Strings

When you think about it, strings are just bytes with special meaning. And thanks to Unicode we have a shared understanding of what various bytes represent when it comes to strings. That means we can write strings out as those equivalent bytes and then decode them into strings. Using UTF-8 as an example, we can translate "ABC" into bytes([65, 66, 67]).decode("utf-8").