Overview

Python 3.4, released on March 16, 2014, is a pivotal version that lays the foundations for asynchronous programming in Python. The asyncio module enters the standard library, paving the way for an entire ecosystem of asynchronous frameworks.

This version also brings enum for defining clean enumerations, pathlib for object-oriented filesystem path manipulation, the statistics module, and most importantly, the integration of pip via ensurepip.

Major Features

asyncio (PEP 3156)

The asyncio module provides an event loop and coroutines for writing single-threaded concurrent code. In Python 3.4, coroutines are defined with the @asyncio.coroutine decorator and the yield from keyword. This approach will be simplified in Python 3.5 with async/await.

python
import asyncio

# Coroutine that simulates fetching data
@asyncio.coroutine
def fetch_data(source, delay):
    """Simulate a network request with a variable delay."""
    print(f"Starting request to {source}...")
    yield from asyncio.sleep(delay)
    data = {"source": source, "results": delay * 100}
    print(f"Response received from {source}.")
    return data

# Coroutine that orchestrates multiple requests in parallel
@asyncio.coroutine
def aggregate_results():
    """Launch multiple requests simultaneously and aggregate results."""
    tasks = [
        fetch_data("users_api", 2),
        fetch_data("products_api", 1),
        fetch_data("orders_api", 3),
    ]
    # gather launches all tasks in parallel
    results = yield from asyncio.gather(*tasks)
    total = sum(r["results"] for r in results)
    print(f"Aggregated total: {total}")
    return results

# Running the event loop
loop = asyncio.get_event_loop()
results = loop.run_until_complete(aggregate_results())
loop.close()

# All 3 requests execute in ~3s instead of 6s sequentially
for r in results:
    print(f"  {r['source']} -> {r['results']} records")

enum (PEP 435)

The enum module allows defining enumerated types, i.e., sets of named constants. IntEnum creates enumerations compatible with integers, which is useful for interacting with existing code or numeric protocols.

python
from enum import Enum, IntEnum

# State machine for an order system
class OrderStatus(Enum):
    DRAFT = "draft"
    CONFIRMED = "confirmed"
    PREPARING = "preparing"
    SHIPPED = "shipped"
    DELIVERED = "delivered"
    CANCELLED = "cancelled"

# Allowed transitions between states
TRANSITIONS = {
    OrderStatus.DRAFT: [OrderStatus.CONFIRMED, OrderStatus.CANCELLED],
    OrderStatus.CONFIRMED: [OrderStatus.PREPARING, OrderStatus.CANCELLED],
    OrderStatus.PREPARING: [OrderStatus.SHIPPED],
    OrderStatus.SHIPPED: [OrderStatus.DELIVERED],
    OrderStatus.DELIVERED: [],
    OrderStatus.CANCELLED: [],
}

class Order:
    def __init__(self, number):
        self.number = number
        self.status = OrderStatus.DRAFT

    def transition(self, new_status):
        """Change the order status if the transition is valid."""
        if new_status in TRANSITIONS[self.status]:
            old = self.status
            self.status = new_status
            print(f"Order {self.number}: {old.value} -> {new_status.value}")
        else:
            print(f"Forbidden transition: {self.status.value} -> {new_status.value}")

order = Order("ORD-2014-001")
order.transition(OrderStatus.CONFIRMED)   # OK
order.transition(OrderStatus.PREPARING)   # OK
order.transition(OrderStatus.CANCELLED)   # Forbidden transition

# IntEnum for HTTP status codes
class HTTPCode(IntEnum):
    OK = 200
    CREATED = 201
    NOT_FOUND = 404
    SERVER_ERROR = 500

# Can be used directly as an integer
print(HTTPCode.NOT_FOUND == 404)  # True
print(HTTPCode.OK + 1)            # 201

pathlib (PEP 428)

The pathlib module provides an object-oriented approach to filesystem path manipulation. The / operator allows combining paths intuitively, advantageously replacing calls to os.path.join().

python
from pathlib import Path

# Comparison with os.path
import os.path

# Old way (os.path)
old_path = os.path.join(os.path.expanduser("~"), "projects", "my_app")
exists = os.path.isdir(old_path)

# New way (pathlib)
path = Path.home() / "projects" / "my_app"
exists = path.is_dir()

# Navigating a project tree
root = Path("/var/www/my_site")
config = root / "config" / "settings.toml"
logs = root / "logs"

print(config.name)      # settings.toml
print(config.stem)      # settings
print(config.suffix)    # .toml
print(config.parent)    # /var/www/my_site/config

# List all Python files in a project
project = Path(".")  # current directory
py_files = sorted(project.rglob("*.py"))
for f in py_files[:5]:
    size = f.stat().st_size
    print(f"  {f} ({size} bytes)")

# Simplified reading and writing
output_file = Path("result.txt")
output_file.write_text("Processing completed successfully.\n", encoding="utf-8")
content = output_file.read_text(encoding="utf-8")
print(content)  # Processing completed successfully.

# Creating nested directories
folder = Path("output") / "reports" / "2024"
folder.mkdir(parents=True, exist_ok=True)

statistics module

The new statistics module provides functions for computing basic descriptive statistics: mean, median, standard deviation, variance, and more. It is a welcome addition for quick analyses without external dependencies.

python
import statistics

# Analyzing API response times (in milliseconds)
response_times = [45, 52, 48, 120, 47, 51, 49, 200, 46, 53, 50, 48]

print(f"Mean        : {statistics.mean(response_times):.1f} ms")
print(f"Median      : {statistics.median(response_times):.1f} ms")
print(f"Std dev     : {statistics.stdev(response_times):.1f} ms")
print(f"Variance    : {statistics.variance(response_times):.1f}")

# The median is more resistant to outliers than the mean
# (120 ms and 200 ms pull the mean upward)

# Analyzing the grade distribution of an exam
grades = [8, 10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 16, 18]

print(f"\nExam grades:")
print(f"Mean        : {statistics.mean(grades):.1f}")
print(f"Median      : {statistics.median(grades):.1f}")
print(f"Mode        : {statistics.mode(grades)}")
print(f"Low median  : {statistics.median_low(grades)}")
print(f"High median : {statistics.median_high(grades)}")

Bundled pip (ensurepip)

The ensurepip module ensures that pip is available in every Python 3.4+ installation. Previously, you had to manually download and run an installation script. This change greatly simplifies deployment and onboarding with Python.

python
# Verify that pip is available
# python -m ensurepip --upgrade

# Standard pip usage
# python -m pip install requests
# python -m pip install --upgrade pip
# python -m pip list

# Deployment script that ensures pip is present
import subprocess
import sys

def ensure_pip():
    """Check and install pip if necessary."""
    try:
        import pip
        print(f"pip {pip.__version__} is already installed.")
    except ImportError:
        print("pip missing, installing via ensurepip...")
        import ensurepip
        ensurepip.bootstrap(upgrade=True)
        print("pip installed successfully.")

def install_dependencies(packages):
    """Install a list of packages via pip."""
    for package in packages:
        subprocess.check_call([
            sys.executable, "-m", "pip", "install",
            "--quiet", package
        ])
        print(f"  {package} installed.")

ensure_pip()
# install_dependencies(["requests", "click", "rich"])

Minor Improvements

  • The tracemalloc module allows tracking memory allocations of the interpreter.
  • functools.singledispatch enables function overloading based on the type of the first argument.
  • The selectors module provides a high-level abstraction for I/O multiplexing.
  • hashlib gains new hash functions (BLAKE2, SHA-3).
  • The dis module improves bytecode disassembly display.

Deprecations and Removals

Notable changes:

  • The asyncio module is marked as provisional (API subject to change).
  • The imp module is deprecated in favor of importlib.
  • The formatter module is deprecated.

Sources