What Is New in Python 3.0
Python 3.0 -- also known as "Python 3000" or "Py3K" -- was released on December 3, 2008. It is the first intentionally backwards-incompatible Python release. The primary goal was to clean up accumulated design decisions that had accumulated over 17 years of the Python 2.x line: unifying string and unicode types, fixing integer division, cleaning up the standard library, and removing redundant syntax. Python 3.0 code does not run on Python 2 interpreters.
| Category | Change | Notes |
|---|---|---|
| Strings | All strings are Unicode (str); raw bytes use bytes |
Removed the py2 str/unicode split |
print is now a function, not a statement |
PEP 3105 | |
| Integer Division | 3 / 2 returns 1.5 (not 1); use // for floor division |
PEP 238 |
| Exceptions | raise Exception("msg") only; raise Exception, "msg" removed |
PEP 3109 |
| Exceptions | except Exception as e syntax; except Exception, e removed |
PEP 3110 |
| Views | dict.keys(), dict.values(), dict.items() return views, not lists |
-- |
| Builtins | range() returns an iterator; zip(), map(), filter() are lazy |
Removed xrange(), izip() |
| Syntax | Extended iterable unpacking: first, *rest = iterable |
PEP 3132 |
| Syntax | Keyword-only arguments via * in function signatures |
PEP 3102 |
| Syntax | Function annotations (def f(x: int) -> str) |
PEP 3107 |
| Syntax | Metaclasses via class Foo(metaclass=Meta) |
PEP 3115 |
| Removed | execfile(), reduce() built-in, apply(), has_key(), backtick repr |
-- |
The Biggest Breaking Changes from Python 2
Strings Are Always Unicode
In Python 2, str was bytes and unicode was text. In Python 3, str is always Unicode text, and bytes is raw binary data. The two types do not implicitly coerce -- you must explicitly encode/decode at I/O boundaries.
# Python 3
text = "Hello" # str -- Unicode
raw = b"Hello" # bytes -- binary
encoded = text.encode("utf-8") # str -> bytes
decoded = raw.decode("utf-8") # bytes -> str
# Mixing str and bytes raises TypeError
text + raw # TypeError!
Integer Division
The / operator now always returns a float: 7 / 2 == 3.5. Use // for floor (integer) division: 7 // 2 == 3. This was one of the most common Python 2 bugs -- silent truncation when dividing two integers.
print() Is a Function
print "hello" is a SyntaxError in Python 3. The function form print("hello") allows keyword arguments like end, sep, and file.
print("a", "b", "c", sep="-") # a-b-c
print("loading...", end="")
print("done", file=sys.stderr)
Extended Iterable Unpacking (PEP 3132)
The starred assignment expression captures remaining items in a list.
first, *rest = [1, 2, 3, 4, 5]
# first = 1, rest = [2, 3, 4, 5]
head, *middle, last = range(10)
# head = 0, middle = [1..8], last = 9
Lazy Built-ins and Views
range(), zip(), map(), and filter() now all return lazy iterators rather than lists. dict.keys(), .values(), and .items() return view objects that reflect the current state of the dict. This is more memory-efficient for large collections but requires wrapping in list() when you need random access or multiple iterations.
FAQ
Why was Python 3 intentionally backward-incompatible?
Python's creator Guido van Rossum made a deliberate decision to fix long-standing design flaws -- primarily the string/unicode split, integer division behavior, and accumulated redundancies -- even at the cost of compatibility. The goal was a cleaner language that could evolve without carrying 2.x baggage forward.
Is Python 2 and Python 3 code ever compatible?
Some simple code works in both, but non-trivial programs cannot be byte-for-byte identical. Tools like 2to3 (automated migration) and six (compatibility library) helped during the transition period. As of January 1, 2020, Python 2.7 reached end-of-life and should not be used.
What happened to xrange() in Python 3?
Python 3's range() is what Python 2's xrange() was -- a lazy sequence. There is no separate xrange(). Remove all xrange() calls and replace with range() when migrating.
Where did reduce() go?
reduce() was removed from builtins and moved to functools.reduce(). The rationale was that explicit loops are clearer than reduce() for most cases, and the function is niche enough not to warrant top-level exposure.
Why does dict.keys() return a view instead of a list in Python 3?
Views are more efficient -- they don't copy the entire key set. They also reflect updates to the dict in real time. For iteration, views work directly in for loops. Wrap with list(d.keys()) when you need a snapshot or random access.