Skip to content

Commit a7119c0

Browse files
heikkitoivonencodex
andcommitted
Fix: Correct builtin complexity docs for CPython 3.14
Co-Authored-By: Codex <codex@openai.com>
1 parent 1ca1d70 commit a7119c0

10 files changed

Lines changed: 72 additions & 69 deletions

File tree

docs/builtins/aiter.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ asyncio.run(main())
3737
import asyncio
3838

3939
class AsyncIterable:
40-
async def __aiter__(self):
40+
def __aiter__(self):
4141
return AsyncIterator()
4242

4343
class AsyncIterator:

docs/builtins/all.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,17 @@ result = all(x > 2 for x in numbers) # False - stops at 1
4444

4545
```python
4646
# ✅ O(1) - stops immediately at first falsy
47-
result = all([False, expensive_function(), expensive_function()])
48-
# Doesn't call expensive_function()
47+
checks = [lambda: False, expensive_function, expensive_function]
48+
result = all(check() for check in checks)
49+
# expensive_function() is never called
4950

5051
# ❌ O(n) - evaluates all
5152
result = all([False] + [expensive_function() for _ in range(1000)])
5253
# Calls expensive_function() 1000 times
5354

54-
# ✅ O(1) - generator stops early
55-
result = all(x > 0 for x in range(1000000) if x < 0)
56-
# Stops immediately when x < 0 is False
55+
# ✅ O(k) - generator stops when predicate first fails
56+
result = all(x < 100 for x in range(1000000))
57+
# Stops after checking 100 items
5758
```
5859

5960
### Generator Efficiency

docs/builtins/anext.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ async def async_generator():
2626
async def main():
2727
async_iter = aiter(async_generator())
2828

29-
# Get next item - O(n)
29+
# Get next item - O(k) where k = async iterator work for this step
3030
value = await anext(async_iter)
3131
print(value) # 0
3232

@@ -52,7 +52,7 @@ async def main():
5252
print(await anext(async_iter)) # 1
5353
print(await anext(async_iter)) # 2
5454

55-
# Default when exhausted - O(n)
55+
# Default when exhausted - O(1) once iterator is exhausted
5656
value = await anext(async_iter, "END")
5757
print(value) # "END"
5858

@@ -73,7 +73,7 @@ async def main():
7373
print(await anext(async_iter)) # 1
7474

7575
try:
76-
# No default - raises StopAsyncIteration - O(n)
76+
# No default - raises StopAsyncIteration when exhausted (O(1) at exhaustion)
7777
print(await anext(async_iter))
7878
except StopAsyncIteration:
7979
print("Iterator exhausted")
@@ -95,7 +95,7 @@ async def main():
9595
# Create async iterator - O(1)
9696
iterator = aiter(fetch_items(["a", "b", "c"]))
9797

98-
# Get items manually - O(n) each
98+
# Get items manually - O(k) each where k depends on iterator body
9999
first = await anext(iterator)
100100
print(f"First: {first}") # First: a
101101

docs/builtins/any.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ result = any(x > 2 for x in numbers) # True - stops at 3 (index 2)
4545

4646
```python
4747
# ✅ O(1) - stops immediately at first truthy
48-
result = any([True, expensive_function(), expensive_function()])
49-
# Doesn't call expensive_function()
48+
checks = [lambda: True, expensive_function, expensive_function]
49+
result = any(check() for check in checks)
50+
# expensive_function() is never called
5051

5152
# ❌ O(n) - evaluates all
5253
result = any([True] + [expensive_function() for _ in range(1000)])

docs/builtins/compile.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@ result2 = eval(code_obj, {"x": 5, "y": 10}) # 15
135135
## Compilation with Optimization
136136

137137
```python
138-
# O(n) - optimize flag (Python 2 feature, deprecated in 3)
139-
# Python 3 ignores optimize parameter
140-
141-
# In Python 3, compile is always well-optimized
142-
code = compile("x = 1", "<string>", "exec", optimize=-1)
138+
# O(n) - optimize is active in Python 3
139+
# optimize=-1 (inherit), 0 (no optimization), 1, or 2
140+
code_default = compile("x = 1", "<string>", "exec", optimize=-1)
141+
code_o1 = compile("assert x > 0", "<string>", "exec", optimize=1)
142+
code_o2 = compile("def f():\n 'doc'\n return 1", "<string>", "exec", optimize=2)
143143
```
144144

145145
## Error Handling

docs/builtins/dict.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ CPython uses a hash table with:
3434
- **Hash function**: SipHash13 for `str`/`bytes` (default since Python 3.11); other types use type-specific hashing
3535
- **Collision handling**: Open addressing with probing
3636
- **Growth factor**: ~2-4x when load factor exceeded
37-
- **Python 3.6+**: Insertion order preserved (compact dict design)
37+
- **Python 3.6 (CPython)**: Compact dict preserves insertion order as an implementation detail
3838

3939
### Hash Collision Impact
4040

@@ -50,7 +50,7 @@ value = d[500] # O(1)
5050
### Insertion Order Guarantee
5151

5252
```python
53-
# Python 3.6+ guarantees insertion order
53+
# Python 3.7+ guarantees insertion order (language guarantee)
5454
d = {}
5555
d['a'] = 1
5656
d['b'] = 2
@@ -62,7 +62,8 @@ d['c'] = 3
6262

6363
| Version | Change |
6464
|---------|--------|
65-
| Python 3.6+ | Insertion order preserved |
65+
| Python 3.6 | CPython compact dict preserves insertion order (implementation detail) |
66+
| Python 3.7+ | Insertion order guaranteed by language spec |
6667
| Python 3.9+ | Dict merge & update operators (`\|`, `\|=`) |
6768
| Python 3.10+ | Pattern matching with dicts |
6869
| Python 3.11+ | 23% smaller when all keys are Unicode strings |
@@ -91,7 +92,7 @@ Similar hash table implementation as CPython.
9192

9293
**Avoid**:
9394

94-
- Don't rely on order in Python < 3.6
95+
- Don't rely on insertion order in Python < 3.7 for portable behavior
9596
- Unhashable types as keys (lists, dicts, sets)
9697
- Extremely large dicts with poor hash functions
9798

docs/builtins/dict_func.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ The `dict()` function creates dictionaries from various sources.
77
| Case | Time | Space | Notes |
88
|------|------|-------|-------|
99
| Empty dict | O(1) | O(1) | dict() |
10-
| From keyword arguments | O(n) | O(n) | n = number of kwargs |
11-
| From list of pairs | O(n) | O(n) | n = number of pairs |
12-
| From another dict | O(n) | O(n) | n = dict size |
13-
| From iterable of pairs | O(n) | O(n) | n = number of pairs |
10+
| From keyword arguments | O(n) avg, O(n^2) worst | O(n) | n = number of kwargs |
11+
| From list of pairs | O(n) avg, O(n^2) worst | O(n) | n = number of pairs |
12+
| From another dict | O(n) avg, O(n^2) worst | O(n) | n = dict size |
13+
| From iterable of pairs | O(n) avg, O(n^2) worst | O(n) | n = number of pairs |
1414

1515
## Basic Usage
1616

@@ -84,14 +84,13 @@ d = dict(pairs) # O(4) - hash and store each pair
8484
### Collision Handling
8585

8686
```python
87-
# O(n) - collisions don't affect overall complexity
88-
# Even with hash collisions, dict() is still O(n)
87+
# Average case: O(n)
88+
# Pathological collisions can degrade construction toward O(n^2)
8989

90-
# worst case - all keys hash to same value
91-
# Still O(n) due to Python's implementation
90+
# Worst case: many keys map to the same hash/probe sequence
9291

9392
pairs = [(i, i*10) for i in range(1000)]
94-
d = dict(pairs) # O(1000) - even with collisions
93+
d = dict(pairs) # O(n) average
9594
```
9695

9796
### Keyword Arguments

docs/builtins/globals.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The `globals()` and `locals()` functions return dictionary representations of th
77
| Operation | Time | Space | Notes |
88
|-----------|------|-------|-------|
99
| `globals()` | O(1) | O(1) | Returns reference to existing global dict |
10-
| `locals()` | O(m) | O(m) | Creates snapshot copy; m = number of local vars |
10+
| `locals()` | O(1) or O(m) | O(1) or O(m) | Module/class scope is O(1); optimized function scopes materialize locals mapping |
1111
| Accessing dict value | O(1) avg | O(1) | Dict key lookup; O(n) worst case with collisions |
1212

1313
## Understanding Namespaces
@@ -54,7 +54,7 @@ x = 10
5454
y = 20
5555
z = 30
5656

57-
all_vars = globals() # O(n)
57+
all_vars = globals() # O(1)
5858

5959
# Filter variables (not functions/modules) - O(n)
6060
my_vars = {k: v for k, v in all_vars.items()
@@ -113,7 +113,7 @@ caller()
113113
### Module Level
114114

115115
```python
116-
# At module level, locals() == globals() - O(n)
116+
# At module level, locals() == globals() - O(1)
117117
X = 100
118118

119119
print(locals() is globals()) # True at module level
@@ -249,7 +249,7 @@ print(task())
249249
### Configuration Registry
250250

251251
```python
252-
# Use globals() as simple registry - O(n)
252+
# Use globals() as simple registry - O(1) per lookup/update
253253
CONFIG = {}
254254

255255
def register_config(name, value):
@@ -286,13 +286,16 @@ for i in range(1000):
286286
### Large Namespaces
287287

288288
```python
289-
# Many variables = expensive globals() call - O(n)
290-
# In interactive session with many variables
289+
# globals() remains O(1) regardless of namespace size
290+
# Iterating over the returned dict is O(n)
291291
for i in range(10000):
292292
globals()[f'var_{i}'] = i # O(1) each
293293

294-
# Now globals() is O(10000)
295-
g = globals() # Slower!
294+
# Still O(1): returns the same global dict object
295+
g = globals()
296+
297+
# Iteration scales with number of entries - O(n)
298+
names = [k for k in g if k.startswith("var_")]
296299

297300
# Prefer dict for custom data
298301
my_data = {}
@@ -347,13 +350,14 @@ print(obj.x) # 10
347350
## Debugging with Frame Inspection
348351

349352
```python
350-
import sys
353+
import inspect
351354

352355
def get_caller_info():
353356
"""Get information about calling function"""
354-
frame = sys.exc_info()[2] # O(1)
357+
current = inspect.currentframe() # O(1)
358+
frame = current.f_back if current is not None else None # O(1)
355359
if frame is None:
356-
frame = sys.gettrace().f_back # O(1)
360+
return None
357361

358362
# Access caller's locals - O(m)
359363
locals_dict = frame.f_locals
@@ -373,7 +377,7 @@ def example():
373377

374378
- **Python 2.x**: Same behavior
375379
- **Python 3.x**: Same behavior
376-
- **All versions**: O(1) for globals() (returns reference), O(m) for locals() (creates copy)
380+
- **All versions**: `globals()` is O(1); `locals()` semantics are scope- and implementation-dependent
377381

378382
## Related Functions
379383

@@ -386,14 +390,12 @@ def example():
386390
**Do**:
387391

388392
- Use `globals()` for metaprogramming when needed
389-
- Cache globals() if calling multiple times
390393
- Use inspect module for reflection
391394
- Be explicit about variable scope
392395

393396
**Avoid**:
394397

395398
- Relying on locals() to set variables in functions
396-
- Calling globals() in loops (cache it)
397399
- Modifying globals() at module level (confusing)
398400
- Using globals()/locals() instead of proper parameters
399401
- Assuming locals() state persists after function returns

0 commit comments

Comments
 (0)