Skip to content

Commit 8cb4dc4

Browse files
authored
RST
1 parent e4c42a0 commit 8cb4dc4

1 file changed

Lines changed: 103 additions & 140 deletions

File tree

peps/pep-0812.rst

Lines changed: 103 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,188 +1,151 @@
1-
PEP: 812 Title: Immutable variables with const keyword Author: Michael Voznesensky <mvoz@google.com>
1+
PEP: 812
2+
Title: Immutable variables with const keyword
3+
Author: Michael Voznesensky <mvoz@google.com>
4+
Status: Draft
25
Type: Standards Track
6+
Content-Type: text/x-rst
7+
Created: 31-Oct-2025
38

4-
# Abstract
9+
Abstract
10+
========
511

6-
Today, python variables are wonderfully mutable - this is a super power of the language. However, in larger codebases, or more complex implementations, there is often a need to mark a variable as immutable. This is useful in two major ways, the first of which is general to programming, and not specific to python - assurances of objects staying identical for their lifetimes is a powerful hint to both the programmer, and the compiler. Potential compiler opitmizations aside, `const` hints ignal to programmers that the original author of the code intended this object not to change, which, like c++'s `const` corectness ideas, promotes safety and readability. Potential general programming benefits aside, a far more specific python "Gotcha", is mutable defaults.
12+
Today, python variables are wonderfully mutable - this is a super power of the language. However, in larger codebases, or more complex implementations, there is often a need to mark a variable as immutable. This is useful in two major ways, the first of which is general to programming, and not specific to python - assurances of objects staying identical for their lifetimes is a powerful hint to both the programmer, and the compiler. Potential compiler opitmizations aside, ``const`` hints ignal to programmers that the original author of the code intended this object not to change, which, like c++'s ``const`` corectness ideas, promotes safety and readability. Potential general programming benefits aside, a far more specific python "Gotcha", is mutable defaults.
713

8-
This PEP proposes a `const` keyword that can be inserted in function arguments, defaults, and in scopes that declares an object as immutable.
14+
This PEP proposes a ``const`` keyword that can be inserted in function arguments, defaults, and in scopes that declares an object as immutable.
915

10-
# Proposal
16+
Proposal
17+
========
1118

12-
A `const` keyword that applies to functions, class attributes, and variables.
19+
A ``const`` keyword that applies to functions, class attributes, and variables.
1320

14-
# Motivation
21+
Motivation
22+
==========
1523

16-
To elaborate on the cases above, consider the following code:
24+
To elaborate on the cases above, consider the following code::
25+
26+
def add_item_to_cart(item, cart=[]):
27+
"""
28+
Adds an item to a user's cart.
29+
If no cart is provided, starts a new one.
30+
"""
31+
cart.append(item)
32+
return cart
1733

18-
```
19-
def add_item_to_cart(item, cart=[]):
20-
"""
21-
Adds an item to a user's cart.
22-
If no cart is provided, starts a new one.
23-
"""
24-
cart.append(item)
25-
return cart
26-
```
2734
cart is evaluated *once* when the function is defined - this means that a second caller appending to the cart is going to see the first item, and so forth, - a common mistake.
2835

29-
Or
36+
Or::
37+
38+
def analyze_latest_scores(current_scores):
39+
original_order = current_scores
40+
current_scores.sort(reverse=True)
41+
return {
42+
"top_score": current_scores[0],
43+
"first_entry": original_order[0]
44+
}
3045

31-
```
32-
def analyze_latest_scores(current_scores):
33-
original_order = current_scores
34-
current_scores.sort(reverse=True)
35-
return {
36-
"top_score": current_scores[0],
37-
"first_entry": original_order[0] # Bug: This will be the top score, not the first entry
38-
}
39-
```
40-
It looks like we are saving a snapshot of the data as it came in... but .sort() modifies the list *in-place*. Because 'original_order' is just a reference to 'current_scores', the returned "first_entry" field is will be the top score, not the first entry!
46+
It looks like we are saving a snapshot of the data as it came in... but .sort() modifies the list *in-place*. Because 'original_order' is just a reference to 'current_scores', the returned "first_entry" field is will be the top score, not the first entry!
4147

4248
And, aside from these edge cases of mutability, just general readability and safety added to python.
4349

44-
# What does `const` mean?
50+
What does ``const`` mean?
51+
=========================
52+
53+
There are two tiers of ``const``-ness - this proposal pushes for the strictest version of it.
54+
55+
Less restrictive - ``const`` only forbids rebinding
56+
---------------------------------------------------
4557

46-
There are two tiers of `const`-ness - this proposal pushes for the strictest version of it.
58+
In this variant of ``const``, we limit it to mean rebinding. It is closer spiritually to "final" in certain other languages.
4759

48-
## Less restrictive - `const` only forbids rebinding
60+
.. code-block:: python
4961
50-
In this variant of `const`, we limit it to mean rebinding. It is closer spiritually to "final" in certain other languages.
62+
const x = []
63+
x = {} # Fails, no rebinding allowed, raises
64+
65+
However::
66+
67+
const x = []
68+
x.append(1) # Sound, allowed, as the name `x` stays the same type and object, it merely got mutated
5169

52-
```
53-
`const` x = []
54-
x = {} # Fails, no rebinding allowed, raises
55-
```
56-
However:
57-
```
58-
`const` x = []
59-
x.append(1) # Sound, allowed, as the name `x` stays the same type and object, it merely got mutated
60-
```
6170
In this case, theres not much to do with function arguments, except catch shadowing as an exception.
62-
In this case, the mutable default problem presented above is not resolved.
63-
64-
## More restrictive - `const` forbids direct mutation
65-
66-
```
67-
`const` x = []
68-
x = {} # Fails, no rebinding allowed, raises
69-
```
70-
And:
71-
```
72-
`const` x = []
73-
x.append(1) # Fails, modifying the object declared as `const`, illegal
74-
```
75-
And
76-
```
77-
class MyWidget:
78-
x: int
79-
80-
def update(self, x):
81-
self.x = x
82-
83-
m = MyWidget()
84-
m.update(1) # 1, sound
85-
m.update(2) # 2, sound
86-
`const` n = MyWidget()
87-
n.update(1) # Fails, updating a `const`
88-
```
89-
Variables marked as `const` cannot be updated, and raise upon updated
90-
91-
# Usage
92-
93-
There are three primary uses of the `const` keyword proposed here:
94-
95-
- On function arguments
96-
- On attributes and fields classes
97-
- On variables
98-
99-
## On function arguments
100-
101-
An argument marked as `const`, be it an arg or a kwarg, functions exactly as if you were to define a local variable at the top of the function as `const`. It cannot be modified, and the object it refers to cannot be updated or written to in any way. It can only be passed to functions that also expect it as "`const`" - that is, you cannot erase `const`ness once it is applied. It can be copied out to a non `const` variable, and that is the proposed analogue of `const`_cast here, the only way to un-`const` something is via a copy.
71+
In this case, the mutable default problem presented above is not resolved.
10272

103-
Shadowing a name becomes an exception.
73+
More restrictive - ``const`` forbids direct mutation
74+
----------------------------------------------------
10475

105-
```
106-
def foo(`const` bar, baz):
107-
bar = 3 # Fails, raises on shadowing
108-
return bar * baz
109-
```
76+
.. code-block:: python
11077
111-
```
112-
def boo(bat, bat):
113-
...
78+
const x = []
79+
x = {} # Fails, no rebinding allowed, raises
11480
115-
def foo(`const` bar, baz):
116-
boo(bar, bar) # Fails, raises on passing bar to boo's bat, which is not `const`
117-
...
118-
```
81+
And::
11982

120-
## On attributes and fields in classes
83+
const x = []
84+
x.append(1) # Fails, modifying the object declared as const, illegal
12185

122-
This makes the attribute only writable at __init__ time - or assignable with a default. It is illegal to modify a `const` variable after.
86+
And::
12387

124-
```
125-
class MyWidget:
126-
`const` x: int
127-
128-
def update(self, x):
129-
self.x = x # Fails, always raises, x is `const`
88+
class MyWidget:
89+
x: int
13090

131-
```
91+
def update(self, x):
92+
self.x = x
13293

133-
## On variables
94+
m = MyWidget()
95+
m.update(1) # 1, sound
96+
m.update(2) # 2, sound
97+
const n = MyWidget()
98+
n.update(1) # Fails, updating a const
13499

135-
Mostly covered above, but either a local or global can be declared `const`, and enforces renaming and update semantics described above.
100+
Variables marked as ``const`` cannot be updated, and raise upon updated
136101

137-
Can only be passed functions where the argument is marked `const`.
102+
Usage
103+
=====
138104

139-
# Compiler benefits
105+
There are three primary uses of the ``const`` keyword proposed here:
140106

141-
## Globals
107+
* On function arguments
108+
* On attributes and fields classes
109+
* On variables
142110

143-
If the compiler knows a global is const, it can bake its value directly into the bytecode of functions that use it, rather than emitting a LOAD_GLOBAL instruction.
144-
```
145-
DEBUG = False
146-
def foo():
147-
if DEBUG:
148-
...
149-
if DEBUG:
150-
...
151-
```
152-
Looks like:
153-
```
154-
Disassembly of <code object foo at 0x561367afd590, file "example.py", line 2>:
155-
2 RESUME 0
111+
On function arguments
112+
---------------------
113+
114+
An argument marked as ``const``, be it an arg or a kwarg, functions exactly as if you were to define a local variable at the top of the function as ``const``. It cannot be modified, and the object it refers to cannot be updated or written to in any way. It can only be passed to functions that also expect it as "``const``" - that is, you cannot erase ``const``ness once it is applied. It can be copied out to a non ``const`` variable, and that is the proposed analogue of ``const_cast`` here, the only way to un-``const`` something is via a copy.
156115

157-
3 LOAD_GLOBAL 0 (DEBUG)
158-
TO_BOOL
159-
POP_JUMP_IF_FALSE 1 (to L1)
116+
Shadowing a name becomes an exception.
160117

161-
4 NOP
118+
.. code-block:: python
162119
163-
5 L1: LOAD_GLOBAL 0 (DEBUG)
164-
TO_BOOL
165-
POP_JUMP_IF_FALSE 1 (to L2)
120+
def foo(const bar, baz):
121+
bar = 3 # Fails, raises on shadowing
122+
return bar * baz
166123
167-
6 RETURN_CONST 0 (None)
124+
.. code-block:: python
168125
169-
5 L2: RETURN_CONST 0 (None)
170-
```
171-
Today, when, you could store the value once and skip the LOAD_GLOBALS, as well as the control flow (in this case). Further static analysis features could then kick in to mark the dead branch as dead.
126+
def boo(bat, bat):
127+
...
172128
173-
## Class safety / MRO optimization
129+
def foo(const bar, baz):
130+
boo(bar, bar) # Fails, raises on passing bar to boo's bat, which is not `const`
131+
...
174132
175-
If a class method is marked const, the compiler knows it will never be overridden by a subclass or shadowed by an instance attribute.
176-
When you call my_obj.const_method(), the compiler doesn't need to check the instance dictionary or walk the MRO. It can compile a direct call to that exact function object.
133+
On attributes and fields in classes
134+
-----------------------------------
177135

178-
## Guarding Jits
136+
This makes the attribute only writable at __init__ time - or assignable with a default. It is illegal to modify a ``const`` variable after.
179137

180-
JITs that rely on guards (Cpython jit, torchdynamo, etc) could emit less guards
138+
.. code-block:: python
181139
182-
# Non compiler benefits
140+
class MyWidget:
141+
const x: int
183142
184-
Cleaner, more readable code.
143+
def update(self, x):
144+
self.x = x # Fails, always raises, x is const
185145
186-
Stronger invariants at a language level.
146+
On variables
147+
------------
187148

149+
Mostly covered above, but either a local or global can be declared ``const``, and enforces renaming and update semantics described above.
188150

151+
Can only be passed functions where the argument is marked ``const``.

0 commit comments

Comments
 (0)