diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst index a139fab3aa2f..c65c330edbef 100644 --- a/mypyc/doc/differences_from_python.rst +++ b/mypyc/doc/differences_from_python.rst @@ -237,28 +237,56 @@ nested function calls, typically due to out-of-control recursion. Final values ------------ -Compiled code replaces a reference to an attribute declared ``Final`` with -the value of the attribute computed at compile time. This is an example of -:ref:`early binding `. Example:: +Mypy treats variables and attributes defined as ``Final`` specially. +For instance attributes of native classes, mypyc prevents reassignment +of these attributes. + +Mypyc replaces references to a variable or attribute declared ``Final`` +with the value of the attribute computed at compile time, when it can +be determined during compilation. Example:: MAX: Final = 100 def limit_to_max(x: int) -> int: - if x > MAX: - return MAX - return x + if x > MAX: + return MAX + return x The two references to ``MAX`` don't involve any module namespace lookups, and are equivalent to this code:: def limit_to_max(x: int) -> int: - if x > 100: - return 100 - return x + if x > 100: + return 100 + return x When run as interpreted, the first example will execute slower due to -the extra namespace lookups. In interpreted code final attributes can -also be modified. +the extra namespace lookups. + +For a final class attribute or global variable whose value can't be +determined during compilation, mypyc defines a hidden native variable +that stores the value when the initialization assignment is evaluated. +Redefining the attribute using ``setattr`` or direct namespace access +has no effect for mypyc-compiled code (in the same compilation unit). + +Final global variables and class attributes are faster to read in +compiled code, since they don't have to be looked up from a namespace +dictionary. Thus using a ``Final`` object that holds mutable state in an +attribute tends to be faster than a plain global variable in compiled code:: + + class Counter: + def __init__(self) -> None: + self.value = 0 + + x: Final = Counter() + y = 0 + + def inc() -> None: + # Faster: no Python namespace lookup + x.value += 1 + # Slower: access through globals() namespace dictionary + global y + y += 1 .. _free-threading: