What Is New in Python 3.6
Python 3.6 was released on December 22, 2016. The standout additions are formatted string literals (f-strings), the secrets module for cryptographically secure random data, underscores in numeric literals, variable annotations, and the async/await becoming reserved syntax. CPython's dictionary implementation was also completely reworked, delivering a compact and faster dict that preserves insertion order as an implementation detail.
| Category | Change | PEP / Reference |
|---|---|---|
| New Syntax | Formatted string literals -- f-strings | PEP 498 |
| New Syntax | Variable annotations (x: int = 5) |
PEP 526 |
| New Syntax | Underscores in numeric literals (1_000_000) |
PEP 515 |
| New Syntax | Asynchronous generators and comprehensions | PEP 525, PEP 530 |
| New Modules | secrets -- cryptographically secure random tokens |
PEP 506 |
| Standard Library | typing enhancements: ClassVar, NewType, TYPE_CHECKING |
PEP 526, PEP 544 |
| Performance | CPython dict reimplemented -- 20-25% memory reduction, insertion-order preserved | -- |
| Performance | Faster isinstance() checks for classes with __instancecheck__ |
-- |
| Interpreter | Customization of class creation without metaclass: __init_subclass__ |
PEP 487 |
| Interpreter | Descriptor protocol extended: __set_name__ |
PEP 487 |
| asyncio | New async/await reserved keywords; async generators and comprehensions |
PEP 525, PEP 530 |
| Deprecated | Misuse of StopIteration inside generators now raises RuntimeError |
PEP 479 |
What Are the Key Language Features in Python 3.6?
Formatted String Literals -- f-strings (PEP 498)
F-strings are string literals prefixed with f or F. Expressions inside {} are evaluated at runtime and formatted into the string. They are the fastest string formatting option in Python -- faster than % formatting, .format(), and str.join() in most benchmarks.
name = "Alice"
age = 30
print(f"Name: {name}, Age: {age}") # Name: Alice, Age: 30
print(f"Next year: {age + 1}") # Next year: 31
print(f"{'hello':>10}") # right-align with width 10
print(f"{3.14159:.2f}") # 3.14
F-strings also support nested expressions, lambda functions (wrapped in parens), and multi-line formatting. They replaced the need for "Hello %s" % name and "Hello {}".format(name) in virtually all new code.
Variable Annotations (PEP 526)
Variables can now be annotated at the module, class, and function level without assignment. Annotations are stored in __annotations__ on the enclosing scope but are not enforced at runtime.
count: int = 0
items: list[str] = []
class Config:
host: str
port: int = 8080
debug: bool
Underscores in Numeric Literals (PEP 515)
Underscores can be placed freely in numeric literals for readability. They are ignored by the parser -- only the value matters.
one_million = 1_000_000
hex_addr = 0xDEAD_BEEF
pi_approx = 3.141_592_653
binary_mask = 0b1111_0000
New Standard Library: The secrets Module (PEP 506)
The secrets module provides functions for generating cryptographically strong random data suitable for tokens, passwords, and similar security-sensitive use cases. It is backed by the OS random source and is explicitly not intended for simulations or games -- that is what random is for.
import secrets
token = secrets.token_hex(32) # 64-char hex string
url_token = secrets.token_urlsafe(16) # URL-safe base64 token
pin = secrets.randbelow(10 ** 6) # 0 to 999999
Before secrets, many developers incorrectly used random.random() or random.choice() for security tokens -- those are seeded with a predictable value and are not suitable for this purpose.
Async Generators and Comprehensions (PEP 525, PEP 530)
Python 3.6 extends async support to generators and comprehensions. An async def function can now contain yield, creating an async generator that works with async for. Async comprehensions allow await expressions inside list, set, dict, and generator expressions.
async def fetch_pages(urls):
async for url in urls:
page = await fetch(url)
yield page
# Async comprehension
results = [r async for r in fetch_pages(url_list)]
CPython Dictionary Overhaul
CPython 3.6 ships a completely reworked dict implementation by Inada Naoki (based on a design by Raymond Hettinger). The new structure uses a compact array plus a separate index table, reducing memory usage by 20-25% for typical dictionaries. As a side effect of the implementation, dictionaries maintain insertion order -- though this was documented only as a CPython implementation detail in 3.6 and became a language guarantee in 3.7.
FAQ
Are f-strings faster than .format() and why?
Yes, measurably. F-strings are compiled to bytecode that evaluates expressions directly, whereas .format() parses the template string at runtime. In micro-benchmarks, f-strings are roughly 2x faster than str.format() and about 30% faster than % formatting. For tight loops doing lots of string construction, this is meaningful.
Do variable annotations in Python 3.6 enforce types at runtime?
No. Annotations are purely metadata -- they are stored in __annotations__ but the interpreter does not check or enforce them. Type checkers like mypy and pyright read annotations statically. For runtime enforcement, you need a library like Pydantic or beartype.
Can I use async generators with asyncio.gather()?
No directly. asyncio.gather() takes awaitables (coroutines, tasks, futures), not async generators. To consume an async generator in parallel you'd iterate it explicitly and dispatch with asyncio.create_task(). The async for loop consumes it sequentially.
Is dict insertion order preserved in Python 3.6 for all implementations?
Only as a CPython implementation detail in 3.6. Other implementations (PyPy, Jython, MicroPython) were not required to follow it. The insertion-order guarantee became part of the Python language specification in 3.7, covering all conforming implementations.
What replaced the secrets module before Python 3.6?
The correct approach was os.urandom() for raw bytes, or constructing tokens manually using os.urandom() combined with base64.urlsafe_b64encode(). The secrets module wraps this correctly and provides a clean, auditable API so developers don't have to remember the right combination themselves.