Overview

Python 3.0, released on December 3, 2008, is a landmark version of the language. Nicknamed "Python 3000" or "Py3k", this version deliberately breaks backward compatibility with the 2.x branch to fix design flaws that had accumulated since the language's inception.

The goal was ambitious: clean up the language by removing redundancies, unifying types, and modernizing the syntax. This bold decision led to a long transition period during which both branches coexisted, but it laid the foundation for the modern Python we use today.

Major Features

print() becomes a function

The most iconic change in Python 3.0 is the transformation of print from a statement into a function. This choice allows print() to be used like any other function: it can be passed as an argument, replaced, and most importantly, it supports keyword parameters like sep, end, and file.

python
# Python 2: print was a statement
# print "Hello", "World"

# Python 3: print is a function
print("Hello", "World")
# Hello World

# The sep parameter controls the separator
coordinates = [48.8566, 2.3522]
print("Latitude", coordinates[0], sep=" : ")
# Latitude : 48.8566

# The end parameter prevents the newline
for fruit in ["apple", "pear", "banana"]:
    print(fruit, end=" | ")
# apple | pear | banana | 

# The file parameter redirects output
with open("journal.log", "w") as f:
    print("Starting processing", file=f)
    print("Processing complete", file=f)

Unicode strings by default

In Python 3.0, all strings are now Unicode (type str). The bytes type explicitly represents binary data. This clear separation between text and binary data eliminates an entire category of encoding-related bugs.

python
# Strings are natively Unicode
message = "Caf\u00e9 cr\u00e8me and cr\u00eape au chocolat"
print(message)  # Caf\u00e9 cr\u00e8me and cr\u00eape au chocolat
print(len(message))  # 31 (each character counts as 1)

# Clear distinction between text and bytes
text = "R\u00e9sum\u00e9"
raw_bytes = text.encode("utf-8")
print(type(text))       # <class 'str'>
print(type(raw_bytes))  # <class 'bytes'>
print(raw_bytes)        # b'R\\xc3\\xa9sum\\xc3\\xa9'

# Reverse conversion
recovered_text = raw_bytes.decode("utf-8")
print(recovered_text)  # R\u00e9sum\u00e9

# You can no longer mix text and bytes
# text + raw_bytes  # TypeError!

Integer division

In Python 2, dividing two integers returned an integer (floor division). In Python 3.0, the / operator always performs true division, while // performs floor division. This change eliminates a frequent source of errors, especially in scientific computations.

python
# Python 2: 7 / 2 gave 3
# Python 3: 7 / 2 gives 3.5

# True division (always a float)
result = 7 / 2
print(result)  # 3.5

# Floor division (truncation)
integer_result = 7 // 2
print(integer_result)  # 3

# Practical example: computing an average
grades = [14, 17, 12, 15, 18]
average = sum(grades) / len(grades)
print(f"Average: {average}")  # Average: 15.2

# Practical example: splitting into groups
students = 37
groups = 5
per_group = students // groups
remaining = students % groups
print(f"{per_group} per group, {remaining} remaining")
# 7 per group, 2 remaining

New exception syntax

Python 3.0 replaces the except Exception, e syntax with except Exception as e. This new form is more readable and consistent with the rest of the language. It also allows catching multiple exception types without ambiguity.

python
# Python 2: except ValueError, e:
# Python 3: except ValueError as e:

def convert_to_integer(value):
    """Convert a value to integer with error handling."""
    try:
        return int(value)
    except ValueError as error:
        print(f"Cannot convert: {error}")
        return None

# Catching multiple exception types
def read_config(path):
    """Read a configuration file with robust error handling."""
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError as e:
        print(f"File not found: {e.filename}")
    except PermissionError as e:
        print(f"Permission denied: {e}")
    except OSError as e:
        print(f"System error: {e}")
    return None

print(convert_to_integer("42"))    # 42
print(convert_to_integer("abc"))   # Cannot convert: ...
                                    # None

Minor Improvements

  • dict.keys(), dict.values(), and dict.items() now return views instead of lists.
  • map() and filter() return iterators instead of lists.
  • range() replaces xrange() and returns an efficient sequence object.
  • raw_input() is removed: input() replaces it.
  • Comparisons between incompatible types raise an exception instead of returning an arbitrary result.
  • Octal literals use the 0o prefix (e.g., 0o777 instead of 0777).

Deprecations and Removals

Python 3.0 removes many obsolete elements:

  • Old-style classes are gone: all classes now inherit from object.
  • The long and int types are merged into a single int type.
  • The exec keyword becomes a function.
  • Backticks (`x`) for representation are removed in favor of repr(x).
  • The string module loses its deprecated functions (string.join, string.find, etc.).
  • The raise Exception, "message" statement is replaced by raise Exception("message").

Sources