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