Latest in branch 2.1
2.1.3
Released 09 Apr 2002
(24 years ago)
SoftwarePython
Version2.1
Status
End of life
Initial release2.1
16 Apr 2001
(25 years ago)
Latest release2.1.3
09 Apr 2002
(24 years ago)
End of life09 Apr 2002
(Ended 24 years, 2 months ago)
Source codehttps://github.com/python/cpython/tree/v2.1.3
Documentationhttps://docs.python.org/release/2.1.3/
Downloadhttps://www.python.org/downloads/release/python-213/
Python 2.1 ReleasesView full list

What Is New in Python 2.1

Python 2.1 introduced nested scopes (lexical scoping resolved via the LEGB rule), the __future__ import mechanism, weak references, and the first steps toward better class introspection. Nested scopes are the change that made closures work in Python as developers expect.

Category Change PEP / Reference
Language Nested scopes -- closures and lexical scoping (LEGB) PEP 227
Language __future__ import mechanism PEP 236
Standard Library weakref -- weak references to objects PEP 205
Standard Library Rich comparison methods: __lt__, __le__, __gt__, __ge__, __eq__, __ne__ PEP 207
Standard Library warnings module for deprecation warnings PEP 230
Standard Library Functions as objects with __doc__ and __dict__ --

Key Features in Python 2.1

Nested Scopes and Closures (PEP 227)

Before Python 2.1, a function could only see its own local variables and global variables -- not variables from enclosing functions. Python 2.1 introduced lexical scoping (implemented as the LEGB rule: Local, Enclosing, Global, Built-in), making closures work correctly.

def make_adder(n):
    def adder(x):
        return x + n  # 'n' from enclosing scope -- works in 2.1+
    return adder

add5 = make_adder(5)
print(add5(10))  # 15

In Python 2.0 and earlier, the reference to n in adder would fail or require a workaround like def adder(x, n=n) to capture the value via a default argument. Nested scopes eliminated this entire class of hacks.

The __future__ Import Mechanism (PEP 236)

from __future__ import feature allows new language features to be enabled per module before they become the default. This was the mechanism used throughout the Python 2.x series to let code opt into Python 3 behaviors selectively.

from __future__ import division      # True division in 2.2+
from __future__ import print_function # print() as function
from __future__ import unicode_literals
from __future__ import annotations   # Postponed in 3.7+

Weak References (PEP 205)

Weak references allow an object to be referenced without preventing garbage collection. This is useful for caches and observer patterns where you do not want the cache to keep objects alive.

import weakref

class BigObject:
    pass

obj = BigObject()
ref = weakref.ref(obj)

print(ref())   # The object
del obj        # object eligible for GC
print(ref())   # None -- object was collected

FAQ

What is the LEGB rule and why does it matter?
LEGB stands for Local, Enclosing, Global, Built-in -- the order in which Python searches for a name. A name is first looked up in the local scope, then in enclosing function scopes (from inner to outer), then module globals, then built-in names. Understanding LEGB explains why closures work, why a global declaration is needed to reassign a module-level variable from a function, and why nonlocal (added in Python 3) was needed.

When was nonlocal added to handle closure assignments?
Python 3.0. In Python 2.x, you could read variables from enclosing scopes but not assign to them without a global reference. Developers worked around this with mutable containers (e.g., counter = [0] and counter[0] += 1). Python 3's nonlocal statement allows direct assignment to enclosing scope variables.

What are weak references used for in practice?
Two main patterns: (1) caches where you want entries to expire naturally when not otherwise referenced (use weakref.WeakValueDictionary), and (2) observer/event systems where subscribers should not be kept alive just because they subscribed. Without weak references, a cache holding the only remaining reference to an object prevents its garbage collection -- a subtle leak.

Why does Python 2.1 require from __future__ import nested_scopes to enable LEGB?
In Python 2.1, nested scopes were opt-in via from __future__ import nested_scopes because they changed bytecode and could break code that relied on the old behavior (though such code was rare). In Python 2.2, nested scopes became the default and the future import was no longer needed.

What are rich comparisons and why were they added?
Before Python 2.1, comparison operators (<, >=, etc.) all routed through __cmp__ which returned -1, 0, or 1. Rich comparisons allow each operator to have its own method and return any value -- typically True/False but also NotImplemented. This enables NumPy arrays to return element-wise comparison arrays from < rather than a single boolean.

Releases In Branch 2.1

VersionRelease date
2.1.309 Apr 2002
(24 years ago)
2.1.215 Jan 2002
(24 years ago)
2.1.2c110 Jan 2002
(24 years ago)
2.1.120 Jul 2001
(24 years ago)
2.1.1c113 Jul 2001
(24 years ago)
2.116 Apr 2001
(25 years ago)
2.1c216 Apr 2001
(25 years ago)
2.1c113 Apr 2001
(25 years ago)
2.1b223 Mar 2001
(25 years ago)
2.1b102 Mar 2001
(25 years ago)
2.1a202 Feb 2001
(25 years ago)
2.1a123 Jan 2001
(25 years ago)