Constant variables in Python

The greatest optimization in terms of memory and speed is likely to come from the ability to make Python variables constant as needed.

This could allow not only avoiding allocating more memory than necessary, but also changing LOAD_GLOBAL calls to LOAD_CONST.

Example:

PI = 3.14

def calculate(radius):
    return radius * PI

dis.dis(calculate)

This displays:


5 LOAD_FAST 0 (radius)
LOAD_GLOBAL 0 (PI)
BINARY_OP 5 (*)
RETURN_VALUE

But if PI were made constant, PI could be replaced with its constant value, and then it would display:


5 LOAD_FAST 0 (radius)
LOAD_CONST 1 (3.14)
BINARY_OP 5 (*)
RETURN_VALUE

I’d like to hear your opinions.

This has been discussed before, and various problems have been raised, I’d suggest you search for previous threads in this forum to get an idea of what issues you need to address.

Beyond that, you haven’t provided enough of an actual proposal to really comment. Obviously, just making PI constant in the code you posted is not backward compatible, and so is a non-starter. If you weren’t aware of that, you should read up on Python’s backward compatibility policies - this is a good place to start.

So how would a variable be marked as constant? What does “constant” even mean - for example, can you have a constant list? If so how do you propose handling calls to mutating methods like append? And if you can’t have a constant list, what types are allowed to be used as constants? Can 3rd party types declare themselves as “possible constants”?

I suggest you do some background reading, and then flesh out your proposal if you think you still have something new to propose which hasn’t been discussed before.

5 Likes

Also a minor point: ‘constant’ and ‘variable’ are contradictory, so you’ll need to find a slightly better name for the idea :slight_smile:

4 Likes

Yeah, well, software’s full of that sort of thing :slight_smile: This is hardly the first time anyone’s used “constant variable” for this sort of thing. “Named constants” might be better, but it might be easier to name the proposal by what it concretelty does, eg “compile-time name resolution” or similar.

But if you search the web for “constant variable”, you’ll find plenty of other places where the term is used.

3 Likes

The typical workaround is to make the named constants (which are really global variables) default values to non-public parameters so they’re loaded as fast locals:

import dis

PI = 3.14

def calculate(radius, _PI=PI):
    return radius * _PI

dis.dis(calculate)

which outputs:

  5           RESUME                   0

  6           LOAD_FAST_LOAD_FAST      1 (radius, _PI)
              BINARY_OP                5 (*)
              RETURN_VALUE

which is just as fast as your suggested “constants”.

4 Likes

Thank you so much for your comment.

My main concern is whether, even after putting in all the effort this would entail, there would actually be a significant improvement.

But I’ll explain what I’ve been thinking:

My idea would be to add a new hint that is const (it would be clearer if it could be done like in other languages ​​and use const variable_name = ..., but I don’t think that’s possible in Python).

Something like this:

from typing import Const

PI: Const[float] = 3.14159

def calculate(radius):
    return 2 * PI * radius

This should be changed internally to

from typing import Const

PI: Const[float] = 3.14159

def calculate(radius):
    return 2 * 3.14159 * radius

In other words, declaring a variable as a constant should replace the places where it is used with the content of that variable.
This solution would save the interpreter from having to perform dictionary searches to check the value of the variable

The main doubts and problems I have encountered are the following:

  • What if a constant variable is inside a module that is imported at runtime?
    It would become constant at the moment the value was assigned.

  • What happens if the constant variable is the result of a function that changes its result on each call?
    It keeps the first value that has been assigned to it.

  • If a list is constant, are its elements constant?
    You would need to define whether you want the list to be constant, whether the list contains constant elements, or whether the constant list contains constant elements; that is, it could be:
    Const[list], list[Const], Const[list[Const]]

This proposal would simply mean that if a constant variable is being called anywhere, before execution, that variable would be replaced with its value.

Further research might explore optimizing the PyObjects of elements declared as const.

Perhaps adding a hint isn’t the best way to indicate a constant value; perhaps a constructor could be used, doing something like PI = Const(3.14).

You should not be using type hints, i.e. type annotations for something that actually affects the runtime as a core part of the language.

What happens if the constant variable is the result of a function that changes its result on each call?
It keeps the first value that has been assigned to it.

This is semantically confusing. If it’s a constant in the way you are describing it doesn’t have a moment at runtime where the assignment is executed - this is what it means for it to be compile time expanded. Complex compile time evaluation is a whole new rabbit hole with weird implications.


Your mental model of how python currently works is incomplete to the point where part of your proposal is nonsensical. I would suggest doing more research, as well as reading up on previous discussions and e.g. PEP 795 which is related.

1 Like

I think an actual named constant can be touted as one of the use cases of a syntactic macro (e.g. defining PI a macro of 3.14), which Mark Shannon wrote PEP 638 – Syntactic Macros | peps.python.org for.

2 Likes

MicroPython has a const() which is aimed at optimizing memory and speed.

It can be used only on a subset of types: float , int str, bytes, tuple.

This makes sense in the embedded space where each byte and opcode matters, but not sure if that applies in the same way to CPython.

1 Like