Modern Python Standard Library: Pathlib, Secrets, Zoneinfo, Dataclasses, Logging, f‑strings, Tomllib and Setuptools
This article reviews the most useful additions to the Python standard library—Pathlib, Secrets, Zoneinfo, Dataclasses, proper logging, f‑strings, Tomllib and Setuptools—explaining why they replace older modules, showing concise code examples, and offering guidance on adopting them in everyday projects.
Each new Python release adds modules and improved ways of doing things; while we are used to older libraries, it is time to upgrade and take advantage of the newer, better‑designed standard‑library features.
Pathlib instead of os.path
Pathlib, part of the standard library since Python 3.4, provides an object‑oriented interface for filesystem paths, offering clearer and more readable code than the string‑based os.path functions.
<code>from pathlib import Path
import os.path
# old way
two_dirs_up = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# new way, more readable
two_dirs_up = Path(__file__).resolve().parent.parent
</code>Path objects expose many useful properties and methods, allowing you to create a path once and then query its attributes:
<code>readme = Path("README.md").resolve()
print(f"Absolute path: {readme.absolute()}")
print(f"File name: {readme.name}")
print(f"Path root: {readme.root}")
print(f"Parent directory: {readme.parent}")
print(f"File extension: {readme.suffix}")
print(f"Is it absolute: {readme.is_absolute()}")
</code>Pathlib also overloads the / operator for path concatenation:
<code># Operators:
etc = Path('/etc')
joined = etc / "cron.d" / "anacron"
print(f"Exists? - {joined.exists()}")
</code>Pathlib replaces os.path but not the entire os module; it also includes glob functionality.
Secrets instead of os.urandom
Since Python 3.6 the secrets module should be used for cryptographic‑secure random data instead of os.urandom or the older random module.
<code># old way:
import os
value = os.urandom(64)
print(f"Bytes: {value}")
print(f"Hex: {value.hex()}")
# new way:
import secrets
value = secrets.token_bytes(64)
print(f"Bytes: {value}")
value = secrets.token_hex(64)
print(f"Hex: {value}")
</code>Zoneinfo instead of pytz
Python 3.9 introduced zoneinfo as the standard way to handle time zones, eliminating the need for the external pytz package. On systems lacking tzdata, install the tzdata package.
<code>from datetime import datetime
import pytz
dt = datetime(2022, 6, 4)
nyc = pytz.timezone("America/New_York")
localized = nyc.localize(dt)
print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")
# new way:
from zoneinfo import ZoneInfo
nyc = ZoneInfo("America/New_York")
localized = datetime(2022, 6, 4, tzinfo=nyc)
print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")
</code>Dataclasses
Python 3.7 added the dataclasses module as a modern replacement for namedtuple , offering mutability, automatic __repr__ , __eq__ , __init__ , default values, inheritance, and optional frozen or slots support.
<code># old way (namedtuple):
from typing import NamedTuple
import sys
User = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])
u = User("John", "Doe", b'tfeL+uD...')
print(f"Size: {sys.getsizeof(u)}")
# new way (dataclass):
from dataclasses import dataclass
@dataclass()
class User:
name: str
surname: str
password: bytes
u = User("John", "Doe", b'tfeL+uD...')
print(u)
print(f"Size: {sys.getsizeof(u)}, {sys.getsizeof(u) + sys.getsizeof(vars(u))}")
</code>Speed differences are negligible for typical use; the main advantage is clearer, more flexible code.
Proper Logging instead of print
For production‑ready programs, use the logging module rather than print . A simple configuration provides timestamps, file locations, and log levels.
<code>import logging
logging.basicConfig(
filename='application.log',
level=logging.WARNING,
format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
datefmt='%H:%M:%S'
)
logging.error("Some serious error occurred.")
logging.warning('Some warning.')
</code>f‑strings instead of str.format
f‑strings are more readable and faster than str.format or the old % formatting, though % is still useful for deferred formatting in logging.
<code>import logging
things = "something happened..."
logger = logging.getLogger(__name__)
logger.error("Message: %s", things) # deferred formatting
logger.error(f"Message: {things}") # immediate evaluation
</code>Tomllib instead of tomli
Starting with Python 3.11, the built‑in tomllib module parses TOML files, removing the need for the external tomli package.
<code>import tomllib
with open("pyproject.toml", "rb") as f:
config = tomllib.load(f)
print(config)
toml_string = """
[project]
name = "another-app"
description = "Example Package"
version = "0.1.1"
"""
config = tomllib.loads(toml_string)
print(config)
</code>Setuptools instead of distutils
Since distutils is deprecated, migrate to setuptools for packaging; PEP 632 provides migration guidance.
Conclusion
Every new Python release brings valuable features. Regularly review the release notes for “New Modules”, “Deprecated Modules”, and “Removed Modules” to keep your projects up‑to‑date and incorporate best practices.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.