PYTHON 3.6 — IN-DEPT LOOK

Emeka Onu
8 min readFeb 2, 2017

Python 3.6 was released on 23 Dec 2016, and so many people have not yet started using or learning about the awesome features it has. I wrote this article to give a highlight of the new features I found the most interesting.

Python 3.6 was shipped with 16 Python Enhancement Proposals (PEP). If you don’t know what PEP is, A Python Enhancement Proposals (PEP) is a design document to propose a fundamental change of language such as syntax or really key APIs. The PEP provides a concise technical specification of the feature and a rationale for the feature.

PEP 468: Preserving Keyword Argument Order

If you have ever used **kwargs, you may have noticed that it’s just a dictionary and if you specify keyword arguments, they will come back in a random order because dictionaries are ordered.

Python 3.6 keeps the order of the arguments passed into the function call.

# Type the following in your command line>>> def PEP468(**kwargs):
... print(list(kwargs.keys()))
...
>>> PEP468(a=1, b=2, c=3, d=4)
['a', 'b', 'c', 'd']
>>>

a, b, c, and d is keyword arguments that will return a mapping whose iterable on keys and values and whatever you want to keep that order of a, b, c, and d.

PEP 487: Simpler customization of class creation

If you have tried to write a meta-class, you will agree with me that it’s not easy. It’s really complicated and there’s little subtleties to it.

# Type the following in your command line>>> class PEP487:
... def __init_subclass__(cls, whom, **kwargs):
... super().__init_subclass__(**kwargs)
... cls.hello=lambda: print(f"hello, {whom}")
...
>>> class Helloworld(PEP487, whom = "world"):
... pass
...
>>> Helloworld.hello()
hello, world
>>>

It is now possible to customize subclass creation without using a meta-class. The new _init_subclass class method will be called on the base class whenever a new subclass is created.

Another hook is __set_owner__, a hook in descriptors which gets called once the class the descriptor is part of is created.

PEP 495: Local Time Disambiguation

In most world locations, there have been and will be times when local clocks are moved back. In those times, intervals are introduced in which local clocks show the same time twice in the same day. In these situations, the information displayed on a local clock (or stored in a Python datetime instance) is insufficient to identify a particular moment in time.

# Type the following in your command line>>> dt = datetime.datetime(2017, 11, 1, 1, 30)
>>> pdt = dt.astimezone()
>>> pst = dt.replace(fold=1).astimezone()
>>>
>>> pdt.strftime('%Y-%m-%d %T %Z%z')
'2016-11-06 01:30:00 WAT+0100'
>>> pst.strftime('%Y-%m-%d %T %Z%z')
'2016-11-06 01:30:00 WAT+0100'

PEP 495 adds the new fold attribute to instances of datetime.datetime and datetime.time classes to differentiate between two moments in time for which local times are the same.

PEP 498: Formatted string literals

Also known as String interpolation, PEP498 basically added a concept called formatted strings or “f” strings. It’s totally obvious to notice from the example code below that there’s an ‘f’ prefixing the strings. These strings contain replacement field, which is expression delimited by curly braces {}. While other string literals always have a constant value, formatted strings are really expressions evaluated at run time.

# Type the following in your command line>>> name = "Emeka Onu" 
>>> print (f"My name is {name}")
My name is Emeka Onu
>>>

PEP 506/524 — Adding A Secrets Module To The Standard Library

In Python 3.5.2, os.urandom which is the best way to get a cryptographic secure random bits up blocked if there was not enough randomness in the operating system. This isn’t a problem if you are just using your desktop but if u are using a container that starts up instantaneously or you are using a little IoT device that doesn’t have a lot of input that could be a problem because your system would halt to crawl until enough input that randomly was coming to the system initialize the entropy for the last kernal to actually give you this cryptographically secure bits.

In python 3.6, os.urandom() blocks until there’s enough entropy in the system (a potential problem for containers)

The new os.getrandom() raises an exception if it would block

The main purpose of the new secrets module is to provide an obvious way to reliably generate cryptographically strong pseudo-random values suitable for managing secrets, such as account authentication, tokens, and similar.

  • Use the secrets module for anything security or crypto-related
  • Use random for modeling and simulation

PEP 509 — Add a private version to dict

Dictionaries now have a version ID that’s only accessible from C code — Meant as a monitor to detect when namespaces have mutated — Helpful for catching e.g global and built-in namespace lookup.

PEP 515 — Underscores in Numeric Literals

This basically gives you the ability to write long numbers with underscores where the comma normally would be. In other words, 1000000 can now be written as 1_000_000. this update came at the right time for me.

The ability to use underscores in numeric literals for improved readability. Anytime I put more than 20 zeros in a row I had to start counting zeros to make sure I get it right.

Let’s take a look at some simple examples:

# Type the following in your command line>>> 1_000_000 
1000000
>>> 0x_FF_FF_FF_FF
4294967295

The Python documentation and the PEP also mention that you can use the underscores after any base specifier. Here are a couple of examples taken from the PEP and the documentation:

# Type the following in your command line>>> flags = 0b_0011_1111_0100_1110 
>>> flags
16206
>>> 0x_FF_FF_FF_FF
4294967295
>>>

There are some notes about the underscore that need to be mentioned:

  • You can only use one consecutive underscore and it has to be between digits and after any base specifier
  • Leading and trailing underscores are not allowed

You can learn more about this change in…

PEP 519: Adding a file system path protocol

You can’t in anyway escape from dealing with paths, whether in CLI tools or in web applications.

File system paths have historically been represented as str or bytes objects. This has led to people who write code which operate on file system paths to assume that such objects are only one of those two types (an int representing a file descriptor does not count as that is not a file path). Unfortunately that assumption prevents alternative object representations of file system paths like pathlib from working with pre-existing code, including Python’s standard library.

Here are some examples of how the new interface allows for pathlib.Path to be used more easily and transparently with pre-existing code:

>>> import pathlib 
>>> with open(pathlib.Path("README")) as f:
... contents = f.read()
...
>>> import os.path
>>> os.path.splitext(pathlib.Path("some_file.txt")) ('some_file', '.txt')
>>> os.path.join("/a/b", pathlib.Path("c")) '/a/b/c'
>>> import os
>>> os.fspath(pathlib.Path("some_file.txt")) 'some_file.txt'

PEP 520 — Preserving class attribute definition order

Attributes in a class definition body have a natural ordering: the same order in which the names appear in the source. This order is now preserved in the new class’s dict attribute.

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

PEP 523 — Adding a frame evaluation API to CPython

While Python provides extensive support to customize how code executes, one place it has not done so is in the evaluation of frame objects. If you wanted some way to intercept frame evaluation in Python there really wasn’t any way without directly manipulating function pointers for defined functions.

In PEP 523 there is now a CAPI that allows for specifying an alternative frame evaluation function.

PEP 525/530 — Asynchronous generators and comprehensions

IF you have been using python 3.5, you’ve probably discovered a async and await. That is fantastic but asynch and await can’t be used everywhere. That’s frustrating!

Time to get rid of the frustration! Async and await keywords can now be used as neccessary in generators.

PEP 528/529: Windows default encoding

  • REPL now uses UTF-8
  • Bytes-based paths are now accepted on windows

PEP 526 — Syntax for variable annotations

The basic premise of this PEP is take the idea of Type Hinting (PEP 484) to its next logical step, which is basically adding option type definitions to Python variables, including class variables and instance variables. Please note that adding these annotations or definitions does not suddenly make Python a statically typed language. The interpreter still doesn’t care what type the variable is. However, a Python IDE or other utility like pylint could have an annotation checker added to them that could highlight when you use a variable that you have annotated as one type and then used incorrectly by changing its type mid-function.

Example of how this works:

# annotate.pyname: str = 'Emeka'

What we have here is a Python file that we have named annotate.py. In it, we have created a variable, name, and annotated it to indicate it is a string. This is done by adding a colon after the variable name and then specifying what type it should be. You don’t have to assign anything to the variable if you don’t want to. The following is just as valid:

# annotate.py 
name: str

When you annotate a variable, it will get added to the module’s or class’s annotations attribute. Let’s try importing the first version of our annotate module and access that attribute:

>>> import annotate 
>>> annotate.__annotations__
{'name': <class 'str'>}
>>> annotate.name
'Emeka'

As you can see, the annotations attribute returns a Python dict with the variable name as the key and the type as the value. Let’s add a couple of other annotations to our module and see how the annotations attribute updates.

# annotatetwo.pyname: str = 'Emeka'   
ages: list = [12, 20, 32]
class Car:
variable: dict

In this code, we add an annotated list variable and a class with an annotated class variable. Now let’s import our new version of the annotate module and check out its annotations attribute:

>>> import annotate 
>>> annotate.__annotations__
{'name': <class 'str'>, 'ages': <class 'list'>}
>>> annotate.Car.__annotations__
{'variable': <class 'dict'>}
>>> car = annotate.Car()
>>> car.__annotations__
{'variable': <class 'dict'>}

This time around, we see that the annotations dictionary contains two items. You will note that the module level annotations attribute does not contain the annotated class variable. To get access to that, we need to access the Car class directly or create a Car instance and access the attribute that way.

I am convinced that Python 3.6 is an interesting release. There are many more interesting additions and improvements that are worth checking out. You can learn more about them by reading the official “What’s New In Python 3.6” announcement.

--

--