All the cool Python 3 features that'll make you switch today!

import io
from contextlib import redirect_stdout

# Easy redirect sys.stdout to another file or file-like object

f = io.StringIO()
with redirect_stdout(f):
    import this
f.getvalue()

# You could easily redirect stdout to a file:

with open('import_this.txt', 'w') as f:
    with redirect_stdout(f):
        import this

# You could also make a custom StringIO with a complex behavior

class CaptureStdOutput(io.StringIO):
    """Captures IO stream and emit a signal."""

    def write(self, text):
        """Do something awesome instead of writing to std."""
        pass

d = CaptureStdOutput()
with redirect_stdout(d):
    import this
d.getvalue()

# In python2 you should overwrite sys.stdout
# and pull it back after redirecting the output
import sys
stdout = sys.stdout
sys.stdout = open('import_this.txt', 'w')
import this
sys.stdout = stdout
# Dictionaries maintain their order and use 20 - 25% less memory

my_dict = {'one': 1, 'two': 2, 'three': 3, 'four': 4,
           'five': 5, 'six': 6, 'seven': 7, 'eight': 8,
           'nine': 9, 'ten': 10}

print(my_dict)
# {'one': 1, 'two': 2, 'three': 3, 'four': 4,
# 'five': 5, 'six': 6, 'seven': 7, 'eight': 8,
# 'nine': 9, 'ten': 10}

from sys import getsizeof
print(getsizeof(my_dict)) # 368

# in python2 the order is not preserved and the memory footprint is greater
# https://repl.it/KruM/0

print(my_dict)
# {'four': 4, 'seven': 7, 'five': 5, 'three': 3,
# 'ten': 10, 'eight': 8, 'nine': 9, 'six': 6,
# 'two': 2, 'one': 1}

print(getsizeof(my_dict)) # 664
# Given these three dictionaries, create one combined dict:

get = {'referrer': 'https://talkpython.fm', 'sort': 'newest'}
post = {'first': 'Tom', 'last': 'Jones', 'sort': 'popular'}
routes = {'action': 'snippets'}

all_inputs = {**get, **post, **routes}

import json
output = json.dumps(all_inputs, indent=True)
print(f'The merged dictionary is {output}.')

# Prints:
# The merged dictionary is {
#  "referrer": "https://talkpython.fm",
#  "sort": "popular",
#  "first": "Tom",
#  "last": "Jones",
#  "action": "snippets"
# }.
from contextlib import ExitStack

with ExitStack() as stack:
  files = [stack.enter_context(open(fname)) for fname in ['/etc/hosts', 'nosuchfile']]
  # All opened files will automatically be closed at the end of
  # the with statement, even if attempts to open files later
  # in the list raise an exception
# super() no longer requires passing the class name and self

class A:
    def __init__(self, arg):
        self.arg = arg

class B(A):
    def __init__(self):
        super().__init__("hello")
        # in python 2 this would be:
        #super(B, self).__init__("hello)

b = B()
print(b.arg)  # hello
# underscores can be used to make numbers easier to read in code
big_number = 100_000_000_000

print(big_number)  # 100000000000
# easy string formatting
# no more '%(name)s' % {'name': name}
name = 'Alice'
print(f'My name is {name}.')  # prints "My name is Alice"
# add type annotations to variables, arguments and functions
# you can later access those programmatically

# you can also use mypy to check for type errors
# http://mypy-lang.org/

def my_add(a: int, b: int) -> int:
    return a + b

print(my_add(40, 2))  # 42
print(my_add.__annotations__)  # {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
# easy way to use iterators in your generator

def weird_range():
  yield from range(5)
  yield from range(5)
  
for i in weird_range():
  print(i)

# in python 2 you had to manually iterate over the iterator

def python2_weird_range():
  for i in range(5):
    yield i
  for i in range(5):
    yield i
# easily split lists or iterators into variables

a, b, *rest = range(10)
print(a)     # prints 0
print(b)     # prints 1
print(rest)  # prints [2, 3, 4, 5, 6, 7, 8, 9]

# or even

a, *rest, b = range(10)
print(rest)  # prints [1, 2, 3, 4, 5, 6, 7, 8]

# in python 2 it's a lot more verbose

l = list(range(10))
a = l[0]
rest = l[1:-1]
b = l[-1]
from contextlib import suppress

d = {1: 'one', 2: 'two'}
with suppress(KeyError):
  print(d[3])

### is way easier to read than:

try:
  print(d[3])
except KeyError:
  pass
# useful helpers for generating random and *secure* data of all types

import secrets

print(secrets.token_bytes(16))
print(secrets.token_hex(16))
print(secrets.token_urlsafe(16))
print(secrets.randbelow(10))
# easy path manipulation library
# no more os.path.join() and os.path.is* everywhere

from pathlib import Path

directory = Path("/etc")
filepath = directory / "test_file.txt"

if not filepath.exists():
    Path("/tmp/some/folder/somewhere").mkdir(parents=True)
# built-in Last Recently Used cache decorator
# useful for caching expensive function most-used results

from functools import lru_cache

counter = 0

@lru_cache(maxsize=2)
def get_something(i):
    global counter
    counter += 1
    return counter

print(get_something(0))  # 1
print(get_something(0))  # 1
print(get_something(1))  # 2
print(get_something(0))  # 1
print(get_something(2))  # 3
print(get_something(3))  # 4
print(get_something(4))  # 5
print(get_something(0))  # 6
# avoid accidental passing of keyword arguments

a, b, *rest = range(10)
def f(a, b, *args, option=True):
  print(option)
  
# in python 2 this would have passed option=False, but not in python 3
f(a, b, False)
# in both python 2 and 3 this explictly passes option=False
f(a, b, option=False)
# allow only keyword arguments after a bare `*`

def f(*, a, b):
  print(a + b)

f(1, 3)  # raises: "f() takes 0 positional arguments"
f(a=1, b=3)
# built-in ipaddress module that helps you easily deal with IP addresses
# check type, compare, check against subnets and more with both IPv4 and IPv6

from ipaddress import ip_address, ip_network

ip4 = ip_address('192.168.0.1')
print('packed', ip4.packed)
print('is private', ip4.is_private)

ip6 = ip_address('2001:db8::')
print('exploded', ip6.exploded)
print('is multicast', ip6.is_multicast)

net = ip_network('192.168.0.0/24')
print(f'{ip4} in {net} =', ip4 in net)
print(f'{ip6} in {net} =', ip6 in net)
# enumerations are fully supported
# you longer need to create your own conversion and comparison methods

from enum import Enum

class Color(Enum):
    red = 1
    green = 2
    blue = 3

print(Color.red.value)      # 1
print(Color(2))             # Color.green

print(Color.red == 1)       # False
print(Color.red.value == 1) # True

Got more? Submit a pull request.

Created by Amir Szekely. Inspired by Aaron Meurer's list.