1. 26

  2. 11

    Like many languages, Python has a “null value”, which it calls None. By default, any Python variable can have the value None at any moment in time

    FWIW any variable can have any value at any moment in time. None isn’t special in this regard.

    1. 12

      In theory yes. But in practice None has a common meaning and is typically used to indicate a specific state. There’s a reason we have NullPointerException and not ValueIs8Exception in java for example.

      The big brain play is to acknowledge that and address issues we run into most often.

      1. 1

        There’s a reason we have NullPointerException and not ValueIs8Exception in java for example.

        I am yet to be considered that either have a reason to exist, othrn than people abusonc primitive types by accounting for then being null. Which in my opinion is an anti-patern.

        1. 2

          Java has tried to fix this with the @Nonnull annotations. Without that, every object pointer types T are all implicitly a union types of T | null. There are good reasons for union types to exist, and since 0 is not a valid pointer bit pattern on most platforms, it is useful for encoding the union type of {pointer to dynamic thing} | {singleton not-a-thing}, also known as an option type on {pointer to dynamic thing}.

          The only mistake (Tony Hoare’s billion dollar mistake) was making this implicit.

          1. 4

            The only mistake (Tony Hoare’s billion dollar mistake) was making this implicit.

            That is a gigantic understatement. That is the very core of the language, so imprinted in most C like language programmers that they don’t even realize/understand that it is just non sense.

            We see this in this very post. In the very first example. What is the point of manually handle input outside of the domain of the function? Why manually force an exception? An error will occur further down the code, as it should!

      2. 4

        any variable can have any value at any moment in time

        That sounds like the wikipedia definition of dynamic typing – only meaningful to those who already understand what dynamic typing means, and anyone who interprets that statement in the terminology of static typing will see no difference from static typing.

        Less misunderstandable: Any variable can have any type at any moment in time

      3. 10

        I suppose it’s worth pointing out that Optional is simply syntactic sugar for Union[NoneType, T].

        1. 6

          Or with the new syntax simply None | T

        2. 7

          I feel like most of the code reviews I’ve been doing recently include me saying “Instead of return null values here, we can make this an Optional so users of this method know they have to handle the empty case”. Sometimes my coworkers grumble but the amount of NPEs we’ve had in production has definitely gone down. Extra boilerplate up front saves time in the future!

          1. 4

            The good news is that “instead of returning a null value, make it Optional” is one and the same in Python: If the value can be None, then its type is already effectively Optional and should be marked as such (or equivalently, Variant[None, …]). I don’t think there exists anything to disagree about.

            As for reviewing code when tradeoffs exist (such as in strongly typed languages, where Optional actually does something – it adds a machine word to the size of the type), I’m glad you say “can” and present it as a tip with a certain reason. In code review, “considerate” is more helpful than “holder of popular opinion”!

          2. 4

            I cannot stress enough how important type hinting is to large-scale python applications. If you use it properly you can have high guarantees that your code will not have issues like this.

            Source: building a quantum computer OS/control system in python.

            1. 2

              The use of asserts in production code this way goes against the recommendations of static analysis/lint tools such bandit. So i would be careful and just throw an exception instead.

              This plugin test checks for the use of the Python assert keyword. It was discovered that some projects used assert to enforce interface constraints. However, assert is removed with compiling to optimised byte code (python -o producing *.pyo files). This caused various protections to be removed. Consider raising a semantically meaningful error or AssertionError instead.

              1. 1

                A limited alternative to Optional for languages that don’t support it is to always use empty strings and empty collections in preference to null. Any code that iterates over strings or collections will also be cleaner, because it doesn’t need to special-case null.

                1. 14

                  The issue is when an empty string is a valid return value that carries distinct information from null.

                  1. 9
                  2. 5

                    Python treating 0 as falsey causes so many problems… Treating empty string as null… No way that could go wrong.

                    1. 1

                      How and when does that goes wrong?

                      1. 1

                        Both Oracle DB and Django do it! It causes problems very often in both cases

                        1. 1

                          edit: misread comment; ignore