Lists, dicts and the Python 2-only APIs
The methods that vanished in Python 3, what they actually did, and the mechanical replacement for each one.
Dictionary methods
| Python 2 | Python 3 | Note |
|---|---|---|
d.has_key(k) | k in d | Removed entirely |
d.iteritems() | d.items() | Python 3 items() is a view, not a list |
d.itervalues() | d.values() | Same change |
d.iterkeys() | d.keys() | Same change |
d.items() | list(d.items()) | In 2.7 it already returned a list |
d.viewitems() | d.items() | The 2.7 bridge to 3 semantics |
# Python 2
d = {"a": 1, "b": 2}
if d.has_key("a"):
for k, v in d.iteritems():
print k, v
keys = d.keys() # a real list you can sort and mutate# Python 3 equivalent
d = {"a": 1, "b": 2}
if "a" in d:
for k, v in d.items():
print(k, v)
keys = list(d.keys()) # copy if you intend to modify during iterationThe performance reason for iteritems disappeared: in Python 3 items() is already lazy and does not build a list. The one real behaviour change is that mutating a dict while iterating a view raises RuntimeError.
Builtins and sorting
| Python 2 | Python 3 |
|---|---|
xrange(n) | range(n) |
range(n) | a list in 2; a lazy range object in 3 |
raw_input() | input() |
input() | evaluated the text as code in 2, removed |
cmp(a, b) | use (a > b) - (a < b) |
reduce | moved to functools.reduce |
unichr / unicode | chr / str |
basestring | str |
# Python 2: the cmp= sort argument
def by_length(a, b):
return cmp(len(a), len(b))
names = ["charlie", "bob", "alice"]
names.sort(cmp=by_length)
# Python 3: key functions, or functools.cmp_to_key when you really need a comparator
names = sorted(["charlie", "bob", "alice"], key=len)
from functools import cmp_to_key
names = sorted(["charlie", "bob", "alice"], key=cmp_to_key(by_length))💡
input() in Python 2 called eval on whatever the user typed, so reading a value was equivalent to executing arbitrary code. Every project that still calls bare input() deserves a review before anything else.Behaviour changes that surprise people
# 1. sort stability and cmp removal: use key= everywhere
rows = [("a", 2), ("b", 1)]
rows.sort(key=lambda r: r[1])
# 2. dict ordering
# In 2.7 a dict has no guaranteed order. In 3.7+ insertion order is part of the language.
# Do not write code that depends on either, unless you have checked the version.
# 3. map and filter returned lists in 2, iterators in 3
squares = list(map(lambda x: x * x, xrange(5)))
# 4. zip returned a list in 2
pairs = list(zip("abc", xrange(3)))- In 2.7,
map,filterandzipall return lists, so wrapping them inlist()is harmless and makes the intent explicit. - Comparison across types was allowed in 2 (so
1 < "a"had an arbitrary but consistent answer) and raisesTypeErrorin 3. dictandsetiteration order changed between implementations and, in 3.3, was randomised per process for strings. Never rely on it.
FAQ
Is d.keys() a list or a view in Python 2.7?
A list. The view behaviour arrived in Python 3, and
viewkeys() existed in 2.7 as an opt-in preview. Calling list() around it is the portable habit.How do I replace cmp= without changing behaviour?
Prefer
key=, which is faster and clearer. When the comparison genuinely needs two values, wrap the comparator with functools.cmp_to_key so the same logic works under both versions.Related
The print statement and integer division Migrating to Python 3
Last refreshed 2026-09-18.