What is New in Python 3
Key Python 3 Features and Differences from Python 2
Section titled “Key Python 3 Features and Differences from Python 2”Python 3 introduced several significant changes and improvements compared to Python 2 (which reached end-of-life in 2020). Understanding these differences helps clarify why certain features in Python 3 work the way they do and provides historical context.
The print Function
Section titled “The print Function”One of the most visible changes. In Python 3, print is a built-in function and requires parentheses () around its arguments. In Python 2, print was a statement and parentheses were optional.
# Python 3 (Correct)print("Hello, World!")
# Python 2 style (SyntaxError in Python 3)# print "Hello, World"The print() function also has optional keyword arguments like sep (separator between arguments) and end (character(s) appended at the end, defaults to newline \n).
# Python 3: Control separator and end characterprint("apple", "banana", "cherry", sep=", ")print("This line stays on the same", end=" ")print("line.")
# Output:# apple, banana, cherry# This line stays on the same line.In Python 2, suppressing the newline often involved a trailing comma, which is not valid syntax in Python 3 for this purpose.
Integer Division
Section titled “Integer Division”In Python 3, the division operator / always performs floating-point division, even for integers. This is often more intuitive.
# Python 3result = 3 / 2print(result) # Output: 1.5If you need integer division (discarding the remainder), use the floor division operator //.
# Python 3: Floor divisionresult_floor = 3 // 2print(result_floor) # Output: 1
result_neg = -3 // 2print(result_neg) # Output: -2 (rounds down towards negative infinity)In Python 2, / performed integer division if both operands were integers (3 / 2 resulted in 1), and floating-point division only if at least one operand was a float (3.0 / 2 resulted in 1.5). This difference was a common source of bugs.
Unicode Strings by Default
Section titled “Unicode Strings by Default”Python 3 uses Unicode (specifically UTF-8 by default in most environments) for all strings (str type). This provides much better support for international characters and emojis directly.
# Python 3my_string = "你好世界 €"print(my_string)print(type(my_string)) # Output: <class 'str'>For representing sequences of raw bytes, Python 3 uses the bytes type. You can create bytes literals using a b prefix.
# Python 3my_bytes = b"\x48\x65\x6c\x6c\x6f" # Represents ASCII "Hello"print(my_bytes)print(type(my_bytes)) # Output: <class 'bytes'>
# Encoding a string to bytesencoded_string = my_string.encode('utf-8')print(encoded_string)print(type(encoded_string)) # Output: <class 'bytes'>
# Decoding bytes to a stringdecoded_string = encoded_string.decode('utf-8')print(decoded_string) # Output: 你好世界 €In Python 2, the default str type handled ASCII, and a separate unicode type (often created with a u prefix like u"你好") was needed for Unicode text. Handling the distinction between bytes and text was more complex and error-prone in Python 2.
range() Instead of xrange()
Section titled “range() Instead of xrange()”In Python 3, the range() function returns an immutable sequence type (a ‘range object’) that generates numbers on demand as you iterate over it. This is memory-efficient for large ranges.
# Python 3my_range = range(1, 10, 2) # Represents 1, 3, 5, 7, 9print(my_range) # Output: range(1, 10, 2)print(type(my_range)) # Output: <class 'range'>
for i in my_range: print(i, end=" ") # Output: 1 3 5 7 9
# Range objects support slicing (Python 3.2+)print("\n", my_range[1:3]) # Output: range(3, 7, 2)In Python 2, range() created a full list of numbers in memory, which could be inefficient. Python 2 had a separate xrange() function that behaved like Python 3’s range() (generating numbers lazily). In Python 3, xrange() was removed, and range() adopted its efficient behavior.
Exception Handling Syntax
Section titled “Exception Handling Syntax”Python 3 requires using the as keyword to assign the caught exception object to a variable in an except block.
# Python 3 (Correct)try: result = 10 / 0except ZeroDivisionError as e: print(f"Caught an exception: {e}") print(f"Exception type: {type(e)}")
# Python 2 style (SyntaxError in Python 3)# except ZeroDivisionError, e:Furthermore, raising exceptions requires enclosing the exception arguments in parentheses.
# Python 3 (Correct)raise ValueError("Invalid input provided")
# Python 2 style (SyntaxError in Python 3)# raise ValueError, "Invalid input provided"input() Behavior
Section titled “input() Behavior”In Python 3, the input() function always reads user input as a string. If you need to treat the input as a number, you must explicitly convert it.
# Python 3user_name = input("Enter your name: ")print(f"Hello, {user_name}! Type: {type(user_name)}")
user_age_str = input("Enter your age: ")try: user_age_int = int(user_age_str) print(f"Your age is {user_age_int}. Type: {type(user_age_int)}")except ValueError: print("Invalid age entered.")In Python 2, there were two functions: raw_input() (which behaved like Python 3’s input(), always returning a string) and input() (which tried to evaluate the input as Python code, posing a security risk and often causing confusion). Python 3 removed raw_input() and made input() behave safely.
Iterators and next()
Section titled “Iterators and next()”To get the next item from an iterator (like those created by iter() or generator functions), you use the built-in next() function in Python 3.
# Python 3my_list = [10, 20, 30]my_iterator = iter(my_list)
item1 = next(my_iterator)print(item1) # Output: 10
item2 = next(my_iterator)print(item2) # Output: 20
# Calling the .next() method directly raises an error# item3 = my_iterator.next() # AttributeError in Python 3In Python 2, iterators had a .next() method which was called directly. Python 3 standardized on using the next() function.
Other Notable Changes
Section titled “Other Notable Changes”- Dictionary Views: Methods like
.keys(),.values(), and.items()return memory-efficient ‘view’ objects in Python 3, not lists as in Python 2. You can convert them to lists if needed:list(my_dict.keys()). - Metaclass Syntax: The way metaclasses are specified changed.
- Standard Library Reorganization: Some modules were renamed or moved (e.g.,
urllib2became part ofurllib). - Function Annotations: Python 3 introduced syntax for type hints (typing module).
The __future__ Module (Historical Context)
Section titled “The __future__ Module (Historical Context)”The __future__ module was used in Python 2 to selectively enable features from Python 3, easing the transition. For example, from __future__ import division would make / perform float division in Python 2.
While historically important for migration, these imports have no effect in Python 3 and are generally not needed in modern Python 3 code.
The 2to3 Utility (Historical Context)
Section titled “The 2to3 Utility (Historical Context)”Python included a command-line tool called 2to3 that could automatically convert most Python 2 code to Python 3 syntax. It applied a series of fixers to handle changes like the print statement becoming a function, renaming modules, and updating exception syntax.
While useful during the major migration period, it’s less relevant now as most active development occurs directly in Python 3. However, understanding its existence provides context for the transition challenges faced years ago.
# Example of a Python 2 code snippet:def greet(name): print "Hello,", name
# Running '2to3' on this snippet would suggest changing it to:def greet(name): print("Hello,", name)