What's new in Python 3.6

by Sabine Maennel

Timeline

Python 3.6

  • was released on December 23, 2016
  • work on it began in May 2015

Resources

How to find out what's in it?

Complete Resources

  • Thomas Nyberg, User Group NYC, "Let's Review the dict Module: Part II: Compact Dicts (The Modern Implementation)"Slides)
  • Martin Geisler: "Asynchronous I/O" slides, "The Dictionary Type" slides, "Type Hints in Python 3" slides
  • David Beazley: Python3 Metaprogramming

Gossip

Brett Cannon: "quite the release"

  • 16 Python Enhancement Proposals (PEP)
  • there has not been as many PEPs in a single release since Python 3.0!

Raymond Hettinger "3.6 is the best Python ever"

  • "I have never said this before about a Python version"
  • "You really want it!"
  • "It is the first Python 3 worthy of the name, go get it."

The Details

Convenient new stuff:

String interpolation with fstrings

PEP498

Until 3.5:

two ways of string interpolation

  • string substitution with %
  • str.format
In [46]:
name = "Sabine"
In [47]:
print("My name is %s." % name) 
My name is Sabine.
In [48]:
print("My name is {}.".format(name))
My name is Sabine.

New in 3.6: fstrings

  • the f stands for "format"
In [49]:
name = "Sabine"
print(f"My name is {name}.")
My name is Sabine.
  • nesting is possible
In [50]:
import decimal
width = 10
precision = 4
value = decimal.Decimal("12.34567")
f"result: {value:{width}.{precision}}"  # nested fields
Out[50]:
'result:      12.35'
  • Can do everything format can do

Advantages

  • Faster substitution!
  • Easier to read!
  • "It's just better!"
  • "people, when they start to use this tend to be very, very ... happy!" (Brett Cannon)

Numeric literals

PEP515

Until Python 3.5 long number were hard to write

  • For 100000000 you had to count your digits
  • in real language you use the dot 100.000.000

Now in Python 3.6 you can use underscore

  • 100_000_000
In [52]:
hundred_million = 100_000_000
hundred_million
Out[52]:
100000000

Important new stuff

Adding a secrets module

PEP506, PEP524

until Python 3.5

  • os.urandom was used to generate secret keys

From the Flask documentation

In [53]:
Image(filename="images/flask.png")
Out[53]:
In [54]:
import os
os.urandom(24)
Out[54]:
b'\xc9\x19C7\xb3\x8e.\xb7f\xd42\xa6\x85t\x86\xe0,\xc16\x19\xfb\x99\xe0n'
  • it blocked at times where the operating system did not have enough randomness

Python 3.6

  • the secrets module was added
In [55]:
import secrets
secrets.token_bytes(32)
Out[55]:
b'\x16\x1f\xe8H,e\x8ar*\xb1\x8d\x7f\x00U\xc5E\x844\xa0\xc8F\x08\x16\xc49\xcd5\x11\x191\r\xd3'

Always use the secrets module for cryptographical secure identifiers!

The random module is not meant for secrets

  • predictable randomness: use random for modelling and simulation
In [6]:
# always produces the same sequence 9, 0, 6
import random
random.seed(10)
x = random.randint(0, 9)
y = random.randint(0, 9)
z = random.randint(0, 9)
print(x, y, z)
9 0 6

Completing things that have been started

Pathlib everywere

up to Python 3.3

  • os module existed for path handling
  • paths were provided as strings
In [7]:
import os
path = '/usr/lib'
type(path)
Out[7]:
str
In [8]:
os.path.dirname(path)
Out[8]:
'/usr'

In python 3.4: pathlib was added to the standard library

  • pathlib library considers paths as objects
  • path object does not subclass str
  • os could not work with path objects

since Python 3.6

  • you can use path objects everywhere, even in os
In [13]:
Image(filename="images/path.png")
Out[13]:
In [10]:
import pathlib
pathobject = pathlib.Path('/usr/lib')
type(pathobject)
Out[10]:
pathlib.PosixPath
In [11]:
os.path.dirname(pathobject)
Out[11]:
'/usr'

Asyncio spread

PEP525, PEP530

Python 3.5 async/await keywords were added

In [14]:
Image(filename="images/asyncio.png")
Out[14]:
  • they were added as provisional key words

In Python 3.6

  • async and await remain provisonal keywords (they will turn into reserved key words with Python 3.7)
  • support for use in generators and list comprehension is added

generator example

In [16]:
async def ticker(delay, to):
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

async def run():
    starttime = datetime.datetime.now()
    print(f"Start at second {starttime.second}")
    async for i in ticker(5, 10):
        outputtime = datetime.datetime.now()
        print(f"output: {i} at second: {outputtime.second}")
In [17]:
import asyncio
import datetime
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(run())
finally:
    loop.close()
Start at second 53
output: 0 at second: 53
output: 1 at second: 58
output: 2 at second: 3
output: 3 at second: 8
output: 4 at second: 13
output: 5 at second: 18
output: 6 at second: 23
output: 7 at second: 28
output: 8 at second: 33
output: 9 at second: 38

Type annotations expanded

Since Python 3.5:

  • Type annotations for functions are possible
In [15]:
Image(filename="images/Type_hints.png")
Out[15]:
In [18]:
def add(x: int, y:int):
    return x + y

add(3,4)
Out[18]:
7
  • annotations can be seen in annotations
In [19]:
add.__annotations__
Out[19]:
{'x': int, 'y': int}
  • no automatic type checking happens

New in Python 3.6:

Type annotations are now possible everywhere!

  • typing module is no longer provisional
In [20]:
from typing import List, Dict
primes: List[int] = []

captain: str  # Note: no initial value!

class Starship:
    stats: Dict[str, int] = {}
        
s = Starship()
s.__annotations__
Out[20]:
{'stats': typing.Dict[str, int]}

Annotations are now also possible for

  • local variables
  • for class and instance variables

Tools that will use the new syntax

  • mypy, pytype

CPython Improvements

New dict implementation

What it means

  • This is something big!
  • because: dictionaries are everywhere in Python
  • The new dictionary is ordered, but we are asked to not rely on that it is ordered yet

Except in two cases..

with keyword arguments of functions

PEP468

  • now they come out exactly in the order, that they have been put in:
In [21]:
def orderpreserved(**kwargs):
     print(list(kwargs.keys()))
    
orderpreserved(c=1,b=2,a=3)    
['c', 'b', 'a']

With class attributes

PEP520

In [22]:
class OrderPreserved:
    c = 1
    b = 2
    def a(self):
        pass
    
list(OrderPreserved.__dict__.keys())    
Out[22]:
['__module__', 'c', 'b', 'a', '__dict__', '__weakref__', '__doc__']

Everywhere else: (Brett Cannon)

  • "We are making no garanties that dictionaries will be ordered"
  • "If they happen to be ordered consider it pure luck"

But: (Raymond Hettinger)

  • from databases to hashtables and now back to databases
Version DictSize Dict Ordering
Python 2.7 280,280,280 Scrambled (predictable)
Python 3.5 196,196,196 Randomized (unpredictable): key-sharing dictionary
Python 3.6 112,112,112 Ordered

now dictionaries are

  • small
  • ordered

Dictionaries in Python 3.5

In [23]:
Image(filename="images/pydict.png")
Out[23]: