Getting started
Introduction
Python 3 introduced breaking changes to improve consistency and remove legacy quirks. This cheatsheet covers the major syntax and behavior differences between Python 2.7 and Python 3.x.
Quick migration
# Check Python version
python --version
python3 --version
# Run 2to3 conversion tool
2to3 script.py
2to3 -w script.py # Write changes
# Run with Python 3
python3 script.py
Version check in code
# Python 2 & 3 compatible
import sys
if sys.version_info[0] < 3:
print("Python 2")
else:
print("Python 3")
# Python 3.6+
if sys.version_info >= (3, 6):
print("Modern Python 3")
Print statement vs function
Python 2 (statement)
# Print statement
print "Hello"
print "Hello", "World"
# Multiple arguments
print "x =", 42
# Without newline
print "Hello",
# To file
print >> sys.stderr, "Error"
Python 3 (function)
# Print function
print("Hello")
print("Hello", "World")
# Multiple arguments
print("x =", 42)
# Without newline
print("Hello", end="")
# To file
print("Error", file=sys.stderr)
Compatible approach
# Works in both 2 & 3
from __future__ import print_function
print("Hello")
print("x =", 42)
print("Hello", end="")
Integer division
Python 2
# Integer division by default
5 / 2 # 2
5.0 / 2 # 2.5
# Floor division
5 // 2 # 2
# True division (opt-in)
from __future__ import division
5 / 2 # 2.5
Python 3
# True division by default
5 / 2 # 2.5
5.0 / 2 # 2.5
# Floor division
5 // 2 # 2
-5 // 2 # -3
# Always returns float
10 / 5 # 2.0 (not 2)
Migration tip
# Python 2 & 3 compatible
from __future__ import division
# Use // for integer division
count = total // batch_size
# Use / for float division
average = sum / count
String and Unicode
Python 2
# str is bytes
s = "hello" # <type 'str'>
type(s) # bytes
# Unicode strings
u = u"hello" # <type 'unicode'>
u = unicode("hello")
# Mixing causes issues
"hello" + u"world" # Works
b"hello" + u"world" # TypeError
Python 3
# str is Unicode
s = "hello" # <class 'str'>
type(s) # str (Unicode)
# Bytes strings
b = b"hello" # <class 'bytes'>
b = bytes("hello", "utf-8")
# No mixing
"hello" + "world" # Works
b"hello" + "world" # TypeError
Encoding/decoding
# Python 2
text = u"hello"
data = text.encode("utf-8") # str
text = data.decode("utf-8") # unicode
# Python 3
text = "hello"
data = text.encode("utf-8") # bytes
text = data.decode("utf-8") # str
Range and xrange
Python 2
# range() returns list
range(5) # [0, 1, 2, 3, 4]
type(range(5)) # <type 'list'>
# xrange() returns iterator
xrange(5) # xrange(5)
list(xrange(5)) # [0, 1, 2, 3, 4]
# Memory efficient for loops
for i in xrange(1000000):
pass
Python 3
# range() returns iterator
range(5) # range(0, 5)
type(range(5)) # <class 'range'>
# Convert to list
list(range(5)) # [0, 1, 2, 3, 4]
# No xrange()
xrange(5) # NameError
# Memory efficient by default
for i in range(1000000):
pass
Compatible code
# Python 2 & 3
try:
range = xrange
except NameError:
pass # Python 3
for i in range(100):
print(i)
Dictionary methods
Python 2
d = {"a": 1, "b": 2}
# Returns lists
d.keys() # ['a', 'b']
d.values() # [1, 2]
d.items() # [('a', 1), ('b', 2)]
# Separate iterator methods
d.iterkeys() # <dictionary-keyiterator>
d.itervalues() # <dictionary-valueiterator>
d.iteritems() # <dictionary-itemiterator>
Python 3
d = {"a": 1, "b": 2}
# Returns view objects
d.keys() # dict_keys(['a', 'b'])
d.values() # dict_values([1, 2])
d.items() # dict_items([('a', 1), ('b', 2)])
# Convert to list
list(d.keys()) # ['a', 'b']
# No iter* methods
d.iterkeys() # AttributeError
View objects
# Python 3 views are dynamic
d = {"a": 1}
keys = d.keys()
d["b"] = 2
list(keys) # ['a', 'b']
# Views support set operations
d1 = {"a": 1, "b": 2}
d2 = {"b": 2, "c": 3}
d1.keys() & d2.keys() # {'b'}
Input functions
Python 2
# raw_input() returns string
name = raw_input("Name: ")
type(name) # <type 'str'>
# input() evaluates code
num = input("Number: ") # User types: 42
type(num) # <type 'int'>
# DANGEROUS - avoid input()
input("Enter: ") # User types: __import__('os').system('ls')
Python 3
# input() returns string
name = input("Name: ")
type(name) # <class 'str'>
# raw_input() doesn't exist
raw_input("Name: ") # NameError
# Convert manually
num = int(input("Number: "))
type(num) # <class 'int'>
Compatible approach
# Python 2 & 3
try:
input = raw_input
except NameError:
pass # Python 3
name = input("Name: ")
Exception syntax
Python 2
# Old syntax (comma)
try:
risky()
except ValueError, e:
print(e)
# Multiple exceptions
except (TypeError, KeyError), e:
print(e)
# New syntax also works
except ValueError as e:
print(e)
Python 3
# Only 'as' syntax
try:
risky()
except ValueError as e:
print(e)
# Multiple exceptions
except (TypeError, KeyError) as e:
print(e)
# Old syntax removed
except ValueError, e: # SyntaxError
print(e)
Raising exceptions
# Python 2
raise ValueError, "message"
raise ValueError("message")
# Python 3 (only)
raise ValueError("message")
# With traceback
raise ValueError("message") from original_exception
Import changes
Python 2
# Implicit relative imports
from . import module # Explicit (recommended)
import module # May import local module
# Absolute imports (opt-in)
from __future__ import absolute_import
import module # Always absolute
# Package structure
mypackage/
__init__.py
module.py
subpkg/
__init__.py
Python 3
# Only explicit relative imports
from . import module # Relative
from .. import module # Parent package
import module # Always absolute
# Implicit relative imports removed
import module # Never searches package
# Package structure (same)
mypackage/
__init__.py
module.py
subpkg/
__init__.py
Migration
# Python 2 compatible imports
from __future__ import absolute_import
# Explicit relative
from . import sibling
from .sibling import function
# Explicit absolute
import mypackage.module
Iterators
Python 2
# Returns lists
map(str, [1, 2, 3]) # ['1', '2', '3']
filter(None, [0, 1, 2]) # [1, 2]
zip([1, 2], [3, 4]) # [(1, 3), (2, 4)]
# Iterator versions
from itertools import imap, ifilter, izip
imap(str, [1, 2, 3]) # <itertools.imap>
# range also returns list
range(5) # [0, 1, 2, 3, 4]
Python 3
# Returns iterators
map(str, [1, 2, 3]) # <map object>
filter(None, [0, 1, 2]) # <filter object>
zip([1, 2], [3, 4]) # <zip object>
# Convert to list
list(map(str, [1, 2, 3])) # ['1', '2', '3']
# No imap, ifilter, izip
from itertools import imap # ImportError
# range returns iterator
range(5) # range(0, 5)
Iteration patterns
# Python 3 - more memory efficient
nums = map(lambda x: x * 2, range(1000000))
for n in nums: # Computed on demand
pass
# Force evaluation
result = list(map(str, [1, 2, 3]))
String formatting
Old style (%)
# Python 2 & 3
name = "World"
"Hello %s" % name
"x = %d, y = %d" % (1, 2)
# Named arguments
"%(name)s is %(age)d" % {"name": "Alice", "age": 30}
# Format codes
"%10s" % "right" # Right-aligned
"%-10s" % "left" # Left-aligned
"%.2f" % 3.14159 # Decimal places
.format() method
# Python 2.6+ & 3
"Hello {}".format("World")
"{} {}".format("Hello", "World")
# Positional
"{0} {1}".format("Hello", "World")
"{1} {0}".format("World", "Hello")
# Named
"{name} is {age}".format(name="Alice", age=30)
# Format specs
"{:>10}".format("right")
"{:.2f}".format(3.14159)
f-strings (3.6+)
# Python 3.6+
name = "World"
f"Hello {name}"
age = 30
f"Age: {age}"
# Expressions
f"2 + 2 = {2 + 2}"
# Format specs
value = 3.14159
f"Value: {value:.2f}"
# Debugging (3.8+)
f"{name=}, {age=}"
Class definitions
Python 2
# Old-style class
class OldStyle:
pass
# New-style class (recommended)
class NewStyle(object):
pass
# Differences
old = OldStyle()
new = NewStyle()
type(old) # <type 'instance'>
type(new) # <class '__main__.NewStyle'>
Python 3
# All classes are new-style
class MyClass:
pass
# Explicit object inheritance
class MyClass(object):
pass
# Both are equivalent
c = MyClass()
type(c) # <class '__main__.MyClass'>
super() changes
# Python 2
class Child(Parent):
def method(self):
super(Child, self).method()
# Python 3
class Child(Parent):
def method(self):
super().method() # Simpler!
Library renames
Standard library
# Python 2 → Python 3
ConfigParser → configparser
Queue → queue
SocketServer → socketserver
repr → reprlib
StringIO → io.StringIO
cStringIO → io.StringIO
cPickle → pickle
thread → _thread
dummy_thread → _dummy_thread
Tkinter → tkinter
tkMessageBox → tkinter.messagebox
urllib changes
# Python 2
import urllib
import urllib2
import urlparse
urllib.urlopen()
urllib2.urlopen()
urlparse.urlparse()
# Python 3
import urllib.request
import urllib.parse
import urllib.error
urllib.request.urlopen()
urllib.parse.urlparse()
six library
# Compatibility library
import six
# Python 2/3 compatible
six.text_type # unicode/str
six.string_types # basestring/(str,)
six.iteritems(d) # d.iteritems()/d.items()
six.moves.urllib # Unified urllib
Migration tools
2to3 tool
# Analyze changes
2to3 script.py
# Show differences
2to3 -d script.py
# Write changes
2to3 -w script.py
# Backup originals
2to3 -w -n script.py
# Process directory
2to3 -w mypackage/
Common fixes
# 2to3 transformations
print "x" → print("x")
except E, e: → except E as e:
d.keys() → list(d.keys())
xrange() → range()
raw_input() → input()
unicode() → str()
unichr() → chr()
future imports
# Python 2 compatibility
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# Enable Python 3 behavior in Python 2
print("Hello") # Function
5 / 2 # 2.5
s = "unicode" # Unicode by default
Additional differences
Comparison operators
# Python 2
1 < "2" # True (arbitrary)
None < 0 # True
# Python 3
1 < "2" # TypeError
None < 0 # TypeError
# Must compare same types
Next method
# Python 2
it = iter([1, 2, 3])
it.next() # 1
# Python 3
it = iter([1, 2, 3])
next(it) # 1
it.__next__() # 2
Metaclasses
# Python 2
class Meta(type):
pass
class MyClass(object):
__metaclass__ = Meta
# Python 3
class Meta(type):
pass
class MyClass(metaclass=Meta):
pass
Gotchas
Dictionary iteration
# Python 2 - modifying dict while iterating
for key in d.keys(): # Creates list copy
if condition:
del d[key] # Safe
# Python 3 - views are dynamic
for key in d.keys(): # View object
if condition:
del d[key] # RuntimeError
# Fix
for key in list(d.keys()):
del d[key]
Integer types
# Python 2
type(1) # <type 'int'>
type(1L) # <type 'long'>
sys.maxint # 9223372036854775807
# Python 3
type(1) # <class 'int'>
type(1L) # SyntaxError
sys.maxsize # 9223372036854775807
# No separate long type
File I/O
# Python 2
open("file.txt") # Text mode, bytes
open("file.txt", "rb") # Binary mode
# Python 3
open("file.txt") # Text mode, unicode
open("file.txt", "rb") # Binary mode, bytes
# Specify encoding
open("file.txt", encoding="utf-8")
Also see
- Python 3 Official Docs - Official documentation
- What's New in Python 3 - Comprehensive changes list
- Python 2to3 Guide - Automated conversion tool
- six library - Python 2/3 compatibility library
- python-future - Easy Python 2/3 compatibility
- Porting Python 2 to 3 - Official migration guide
- GitHub Issue #597 - Original request