Python 3.15 přináší líné importy, neměnný slovník a experimentální JIT

Python 3.15
Autor: Georgi Ker a Marie Nordin, podle licence: Rights Managed
Python 3.15
Minulý měsíc byl vydán Python 3.15, který přináší malé změny ve vlastním jazyce (nové specifické klíčové slovo), úpravy typového systému, samozřejmě ještě více barviček a navíc i experimentální just in time (JIT) překladač.

Novinky představené v Pythonu 3.15

Co se dozvíte v článku
  1. Novinky představené v Pythonu 3.15
  2. Instalace Pythonu 3.15 s využitím nástrojů pdm nebo uv
  3. Překlad Pythonu 3.15 ze zdrojových kódů
  4. Základní otestování právě přeloženého Pythonu 3.15
  5. Líný import balíčků
  6. Benchmark: skript, který importuje většinu standardních balíčků
  7. Líný import všech balíčků
  8. Porovnání výsledků
  9. Malé ohlédnutí za staršími verzemi Pythonu: vestavěný typ frozenset
  10. Nový vestavěný datový typ – frozendict
  11. Jak je tomu s neměnitelnými seznamy?
  12. Jaký význam má termín „immutable“ v jazyce Python?
  13. Rozbalení hodnot v programových konstrukcích využívajících generátorovou notaci
  14. Podpora pro rozbalení prvků v konstrukci generátorové notace seznamů
  15. Podpora pro rozbalení prvků v konstrukci generátorové notace množin
  16. Podpora pro rozbalení prvků v konstrukci generátorové notace slovníků
  17. Další vylepšení a rozšíření Pythonu 3.15
  18. Příloha: rezervovaná klíčová slova a „měkká klíčová slova“
  19. Repositář s demonstračními příklady
  20. Odkazy na Internetu

Přibližně před měsícem, konkrétně 9. října 2026, byl vydán Python 3.15. Jak je posledních několik let zvykem, přináší každá další verze Pythonu několik vylepšení, nové vlastnosti, ale i zrychlení interpretru. To pochopitelně platí i pro Python 3.15.

S nejvýznamnějšími vylepšeními a novými vlastnostmi se seznámíme v dnešním článku. Konkrétně si v dnešním článku a v článcích navazujících popíšeme následující změny a vylepšení:

Číslo PEPu Plný název PEPu
PEP 810 Explicit lazy imports for faster startup times
PEP 814 Add frozendict built-in type
PEP 661 Add sentinel built-in type
PEP 798 Unpacking in comprehensions
PEP 686 Python now uses UTF-8 as the default encoding
PEP 829 Package startup configuration files
PEP 799 Tachyon: High frequency statistical sampling profiler
PEP 799 A dedicated profiling package for organizing Python profiling tools
PEP 728 TypedDict with typed extra items
PEP 747 Annotating type forms with TypeForm
PEP 800 Disjoint bases in the type system
Poznámka: zkratka PEP znamená Python Enhancement Proposal. Jedná se o formální popis rozšíření Pythonu, který prochází schvalovacím procesem a teprve poté je rozhodnuto, zda (a jak) bude rozšíření implementováno.

Pro úplnost si uveďme, jaké novinky byly představeny v předchozích verzích Pythonu 3.x:

Verze Pythonu Datum vydání této verze Nejvýznamnější novinky
3.0 3. prosince 2008  
3.1 27. června 2009  
3.2 20. února 2011  
3.3 29. září 2012  
3.4 16. března 2014  
3.5 13. září 2015 sémantika pro async a await, operátor @, unpack
3.6 23. prosince 2016 f-řetězce, async-IO
3.7 27. června 2018 rezervovaná klíčová slova async a await
3.8 14. října 2019 mroží operátor, poziční parametry funkcí
3.9 5. října 2020 generické datové typy
3.10 4. října 2021 vylepšení zápisu datových typů
3.11 24. října 2022 skupiny výjimek
3.12 2. října 2023 klíčové slovo type + jeho sémantika
3.13 7. října 2024 experimentální podpora pro běh bez GIL
3.14 7. října 2025 t-řetězce, více interpretrů, možnost odstranění GILu, více barviček atd.
3.15 9. října 2026 téma dnešního článku
Poznámka: povšimněte si velké pravidelnosti ve vydávání verzí, ke které došlo od verze 3.8.

Instalace Pythonu 3.15 s využitím nástrojů pdm nebo uv

Python 3.15 je možné nainstalovat několika způsoby. Nejjednodušší je situace v případě, že je tato verze Pythonu dostupná přímo v balíčcích vámi používané distribuce Linuxu. V takovém (ideálním) případě postačuje použít apt, dnf atd. a popř. si upravit alternatives.

Pokud Python 3.15 dostupný není (což je docela dobře možné), můžeme se ho pokusit nainstalovat s využitím nástrojů pdm nebo uv, které Python zvolené verze (nebo verzí) dokážou pro uživatele doinstalovat, a to bez nutnosti mít k dispozici administrátorská práva. Ukažme si celý postup při použití balíčku uv.

Poznámka: to je ovšem v současnosti problematické, protože varianta pro Linux běžící na mikroprocesorech s architekturou x86-64 není v době psaní tohoto článku dostupná. Postup nicméně uvádím, protože v budoucnosti bude funkční.

Zjištění, které verze Pythonu jsou již nainstalovány:

$ uv python list
 
cpython-3.14.7-linux-x86_64-gnu                 /usr/bin/python3.14
cpython-3.14.7-linux-x86_64-gnu                 /usr/bin/python3 -> python3.14
cpython-3.14.7-linux-x86_64-gnu                 /usr/bin/python -> ./python3
cpython-3.14.0+freethreaded-linux-x86_64-gnu    /usr/local/bin/python3.14t
cpython-3.14.0+freethreaded-linux-x86_64-gnu    /usr/local/bin/python3.14
cpython-3.14.0+freethreaded-linux-x86_64-gnu    /usr/local/bin/python3 -> python3.14
cpython-3.14.0rc2-linux-x86_64-gnu              .local/bin/python3.14 -> .local/share/uv/python/cpython-3.14.0rc2-linux-x86_64-gnu/bin/python3.14
cpython-3.14.0rc2-linux-x86_64-gnu              .local/share/uv/python/cpython-3.14.0rc2-linux-x86_64-gnu/bin/python3.14
cpython-3.13.7-linux-x86_64-gnu                 .local/bin/python3.13 -> .local/share/uv/python/cpython-3.13.7-linux-x86_64-gnu/bin/python3.13
cpython-3.13.7-linux-x86_64-gnu                 .local/share/uv/python/cpython-3.13.7-linux-x86_64-gnu/bin/python3.13
cpython-3.11.16-linux-x86_64-gnu                /usr/bin/python3.11

Instalace Pythonu 3.15:

$ uv python install 3.15

Pokud (až!) bude verze 3.15 dostupná pro váš operační systém a architekturu mikroprocesoru, měla by se vypsat následující zpráva:

Installed Python 3.15.0 in 3.74s
 + cpython-3.15.0-linux-x86_64-gnu (python3.15)

Překlad Pythonu 3.15 ze zdrojových kódů

Pokud si chcete vyzkoušet stabilní a oficiálně vydaný Python 3.15 a výše uvedený postup nefunguje, postačuje si ze stránek www.python.org/downloads stáhnout archiv (zkomprimovaný tarball), ten rozbalit a použít klasickou kombinaci příkazů (je nutné mít nainstalován překladač céčka, včetně jeho sady nástrojů, a navíc i nástroj make):

$ ./configure

Postup při generování konfiguračních souborů a skriptů:

checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for Python interpreter freezing... ./_bootstrap_python
checking for python3.15... no
checking for python3.15... no
checking for python3.14... python3.14
checking Python for regen version... Python 3.14.0rc2
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
...
...
...
config.status: pyconfig.h is unchanged
configure: creating Modules/Setup.local
configure: creating Makefile
configure:
 
If you want a release build with all stable optimizations active (PGO, etc),
please run ./configure --enable-optimizations

Vlastní překlad lze spustit příkazem:

$ make

Ovšem mnohem rychlejší bude vynucení paralelního překladu. V mém případě se pokusím vytížit všech šestnáct jader:

$ make -j 16

Průběh překladu (jen krátká ukázka):

gcc -c -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall    -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal -I./Include/internal/mimalloc  -I. -I./Include    -DPy_BUILD_CORE -o Programs/python.o ./Programs/python.c
gcc -c -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall    -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal -I./Include/internal/mimalloc  -I. -I./Include    -DPy_BUILD_CORE -o Parser/token.o Parser/token.c
gcc -c -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O3 -Wall    -std=c11 -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wstrict-prototypes -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal -I./Include/internal/mimalloc  -I. -I./Include    -DPy_BUILD_CORE -o Parser/pegen.o Parser/pegen.c
...
...
...
Written build/lib.linux-x86_64-3.15/_sysconfigdata__linux_x86_64-linux-gnu.py
Written build/lib.linux-x86_64-3.15/_sysconfig_vars__linux_x86_64-linux-gnu.json
./python -E -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform
./python -E ./Tools/build/generate-build-details.py `cat pybuilddir.txt`/build-details.json
gcc -shared      Modules/_elementtree.o   -o Modules/_elementtree.cpython-315-x86_64-linux-gnu.so
gcc -shared      Modules/_testinternalcapi.o Modules/_testinternalcapi/test_lock.o Modules/_testinternalcapi/pytime.o Modules/_testinternalcapi/set.o Modules/_testinternalcapi/test_critical_sections.o Modules/_testinternalcapi/complex.o Modules/_testinternalcapi/interpreter.o Modules/_testinternalcapi/tuple.o   -o Modules/_testinternalcapi.cpython-315-x86_64-linux-gnu.so
gcc -shared      Modules/_ctypes/_ctypes_test.o -lm  -o Modules/_ctypes_test.cpython-315-x86_64-linux-gnu.so
The necessary bits to build these optional modules were not found:
_dbm                  _decimal              _gdbm
_tkinter              _uuid                 readline
To find the necessary bits, look in configure.ac and config.log.
 
Checked 116 modules (37 built-in, 72 shared, 1 n/a on linux-x86_64, 0 disabled, 6 missing, 0 failed on import)
Poznámka: z vypsaných informací je patrné, že se přeložil „pouze“ interpret Pythonu a nikoli například IDLE atd. To nám však nebude vůbec vadit.

Výsledkem je spustitelný soubor python, který je již pěkný bumbrlíček (mimochodem: Python 1 se vešel na jedinou disketu):

$ ls -lah python
-rwxr-xr-x. 1 ptisnovs ptisnovs 37M Oct 26 11:54 python

Velikost spustitelného binárního souboru s interpretrem lze zmenšit odstraněním symbolů příkazem strip:

$ strip python

Výsledek je již menší:

$ ls -lah python
-rwxr-xr-x. 1 ptisnovs ptisnovs 6.2M Oct 26 11:57 python

Po překladu je možné Python nainstalovat příkazem (to ale není nutné, pokud si jen potřebujete otestovat základní funkcionalitu interpretru):

$ sudo make install

popř. je možné pouze symlinkovat výsledný spustitelný soubor python do ~/bin/ atd. (podle nastavení cest).

Základní otestování právě přeloženého Pythonu 3.15

Přeložili jsme vůbec korektní verzi Pythonu? To zjistíme snadno:

$ ./python --version
 
Python 3.15.1

Následuje poslední test: spuštění interpretru Pythonu:

$ ./python
 
Python 3.15.1 (main, Oct 26 2026, 11:54:26) [GCC 16.2.1 20260819 (Red Hat 16.2.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

Další volby a přepínače příkazové řádky se od Pythonu 3.14 prakticky nezměnily:

usage: ./python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options (and corresponding environment variables):
-b     : issue warnings about converting bytes/bytearray to str and comparing
         bytes/bytearray with str or bytes with int. (-bb: issue errors)
         deprecated since 3.15 and will become no-op in 3.17.
-B     : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x
-c cmd : program passed in as string (terminates option list)
-d     : turn on parser debugging output (for experts only, only works on
         debug builds); also PYTHONDEBUG=x
-E     : ignore PYTHON* environment variables (such as PYTHONPATH)
-h     : print this help message and exit (also -? or --help)
-i     : inspect interactively after running script; forces a prompt even
         if stdin does not appear to be a terminal; also PYTHONINSPECT=x
-I     : isolate Python from the user's environment (implies -E, -P and -s)
-m mod : run library module as a script (terminates option list)
-O     : remove assert and __debug__-dependent statements; add .opt-1 before
         .pyc extension; also PYTHONOPTIMIZE=x
-OO    : do -O changes and also discard docstrings; add .opt-2 before
         .pyc extension
-P     : don't prepend a potentially unsafe path to sys.path; also
         PYTHONSAFEPATH
-q     : don't print version and copyright messages on interactive startup
-s     : don't add user site directory to sys.path; also PYTHONNOUSERSITE=x
-S     : don't imply 'import site' on initialization
-u     : force the stdout and stderr streams to be unbuffered;
         this option has no effect on stdin; also PYTHONUNBUFFERED=x
-v     : verbose (trace import statements); also PYTHONVERBOSE=x
         can be supplied multiple times to increase verbosity
-V     : print the Python version number and exit (also --version)
         when given twice, print more information about the build
-W arg : warning control; arg is action:message:category:module:lineno
         also PYTHONWARNINGS=arg
-x     : skip first line of source, allowing use of non-Unix forms of #!cmd
-X opt : set implementation-specific option
--check-hash-based-pycs always|default|never:
         control how Python invalidates hash-based .pyc files
--help-env: print help about Python environment variables and exit
--help-xoptions: print help about implementation-specific -X options and exit
--help-all: print complete help information and exit
 
Arguments:
file   : program read from script file
-      : program read from stdin (default; interactive mode if a tty)
arg ...: arguments passed to program in sys.argv[1:]

Líný import balíčků

Prvním vylepšením Pythonu, se kterým se v dnešním článku setkáme, je rozšíření sady klíčových slov o nové klíčové slovo lazy. Jedná se o takzvané „měkké“ klíčové slovo, tj. takové slovo, které má sice speciální význam, ale pouze v určitém kontextu. Do stejné kategorie patří i (taktéž relativně nová) klíčová slova match a case (viz přílohu). Ovšem vraťme se ke klíčovému slovu lazy. To má význam pouze společně s příkazem import resp. from a označuje se jím balíček, který má být naimportován „líně“:

lazy import balíček_x
lazy from balíček_y import funkce_1, funkce_2, funkce_3

Interpret Pythonu se nepokusí takový balíček celý načíst a inicializovat, ale pouze si zapamatuje, že se balíček má načíst později – až ve chvíli, kdy bude skutečně použit. Počítá se s tím, že některé balíčky jsou jen importovány a nejsou nikdy skutečně použity. Příkladem jsou balíčky s typovými informacemi atd.

V jiném kontextu není lazy rezervovaným klíčovým slovem, což zajišťuje zpětnou kompatibilitu, například s následujícím skriptem (který je plně funkční i v Pythonu 3.15):

lazy = 42
print(lazy)

Benchmark: skript, který importuje většinu standardních balíčků

Abychom si ověřili, zda má slovo lazy nějaký efekt na rychlost vykonávání skriptů, jsem vytvořil poněkud umělý příklad (ostatně mnoho benchmarků je umělých). V něm je proveden import prakticky všech standardních balíčků dodávaných společně s Pythonem 3.15. Vynechal jsem pouze několik balíčků specifických pro Microsoft Windows a taktéž balíčků vyžadujících knihovnu Tk:

import abc
import argparse
import array
import ast
import asyncio
import atexit
import base64
import bdb
import binascii
import bisect
import builtins
import bz2
import calendar
import cmath
import cmd
import code
import codecs
import codeop
import collections
import colorsys
import compileall
import compression
import concurrent
import concurrent.futures
import concurrent.interpreters
import configparser
import contextlib
import contextvars
import copy
import copyreg
import csv
import ctypes
import ctypes.util
import ctypes.wintypes
import curses
import curses.ascii
import curses.has_key
import curses.panel
import curses.textpad
import dataclasses
import datetime
import dbm
import dbm.dumb
import dbm.sqlite3
import decimal
import difflib
import dis
import doctest
import email
import email.message
import email.mime
import email.parser
import email.policy
import email.quoprimime
import email.utils
import encodings
import encodings.aliases
import encodings.ascii
import encodings.base64_codec
import encodings.big5
import encodings.big5hkscs
import encodings.bz2_codec
import encodings.charmap
import encodings.cp037
import encodings.cp1006
import encodings.cp1026
import encodings.cp1125
import encodings.cp1140
import encodings.cp1250
import encodings.cp1251
import encodings.cp1252
import encodings.cp1253
import encodings.cp1254
import encodings.cp1255
import encodings.cp1256
import encodings.cp1257
import encodings.cp1258
import encodings.cp273
import encodings.cp424
import encodings.cp437
import encodings.cp500
import encodings.cp720
import encodings.cp737
import encodings.cp775
import encodings.cp850
import encodings.cp852
import encodings.cp855
import encodings.cp856
import encodings.cp857
import encodings.cp858
import encodings.cp860
import encodings.cp861
import encodings.cp862
import encodings.cp863
import encodings.cp864
import encodings.cp865
import encodings.cp866
import encodings.cp869
import encodings.cp874
import encodings.cp875
import encodings.cp932
import encodings.cp949
import encodings.cp950
import encodings.euc_jis_2004
import encodings.euc_jisx0213
import encodings.euc_jp
import encodings.euc_kr
import encodings.gb18030
import encodings.gb2312
import encodings.gbk
import encodings.hex_codec
import encodings.hp_roman8
import encodings.hz
import encodings.idna
import encodings.iso2022_jp
import encodings.iso2022_jp_1
import encodings.iso2022_jp_2
import encodings.iso2022_jp_2004
import encodings.iso2022_jp_3
import encodings.iso2022_jp_ext
import encodings.iso2022_kr
import encodings.iso8859_1
import encodings.iso8859_10
import encodings.iso8859_11
import encodings.iso8859_13
import encodings.iso8859_14
import encodings.iso8859_15
import encodings.iso8859_16
import encodings.iso8859_2
import encodings.iso8859_3
import encodings.iso8859_4
import encodings.iso8859_5
import encodings.iso8859_6
import encodings.iso8859_7
import encodings.iso8859_8
import encodings.iso8859_9
import encodings.johab
import encodings.koi8_r
import encodings.koi8_t
import encodings.koi8_u
import encodings.kz1048
import encodings.latin_1
import encodings.mac_arabic
import encodings.mac_croatian
import encodings.mac_cyrillic
import encodings.mac_farsi
import encodings.mac_greek
import encodings.mac_iceland
import encodings.mac_latin2
import encodings.mac_roman
import encodings.mac_romanian
import encodings.mac_turkish
import encodings.palmos
import encodings.ptcp154
import encodings.punycode
import encodings.quopri_codec
import encodings.raw_unicode_escape
import encodings.rot_13
import encodings.shift_jis
import encodings.shift_jis_2004
import encodings.shift_jisx0213
import encodings.tis_620
import encodings.undefined
import encodings.unicode_escape
import encodings.utf_16
import encodings.utf_16_be
import encodings.utf_16_le
import encodings.utf_32
import encodings.utf_32_be
import encodings.utf_32_le
import encodings.utf_7
import encodings.utf_8
import encodings.utf_8_sig
import encodings.uu_codec
import encodings.zlib_codec
import ensurepip
import ensurepip.__main__
import ensurepip._uninstall
import enum
import errno
import faulthandler
import fcntl
import filecmp
import fileinput
import fnmatch
import fractions
import ftplib
import functools
import gc
import genericpath
import getopt
import getpass
import gettext
import glob
import graphlib
import grp
import gzip
import hashlib
import heapq
import hmac
import html
import html.entities
import html.parser
import http
import http.client
import http.cookiejar
import http.cookies
import http.server
import imaplib
import importlib
import importlib._abc
import importlib._bootstrap
import importlib._bootstrap_external
import importlib.abc
import importlib.machinery
import importlib.metadata
import importlib.metadata._adapters
import importlib.metadata._collections
import importlib.metadata._functools
import importlib.metadata._itertools
import importlib.metadata._meta
import importlib.metadata._text
import importlib.metadata.diagnose
import importlib.readers
import importlib.resources
import importlib.resources._adapters
import importlib.resources._common
import importlib.resources._functional
import importlib.resources._itertools
import importlib.resources.abc
import importlib.resources.readers
import importlib.resources.simple
import importlib.simple
import importlib.util
import inspect
import io
import ipaddress
import itertools
import json
import json.__main__
import json.decoder
import json.encoder
import json.scanner
import json.tool
import keyword
import linecache
import locale
import logging
import logging.config
import logging.handlers
import lzma
import mailbox
import marshal
import math
import mimetypes
import mmap
import modulefinder
import multiprocessing
import multiprocessing.connection
import multiprocessing.context
import multiprocessing.dummy
import multiprocessing.dummy.connection
import multiprocessing.forkserver
import multiprocessing.heap
import multiprocessing.managers
import multiprocessing.pool
import multiprocessing.popen_fork
import multiprocessing.popen_forkserver
import multiprocessing.process
import multiprocessing.queues
import multiprocessing.reduction
import multiprocessing.resource_sharer
import multiprocessing.resource_tracker
import multiprocessing.shared_memory
import multiprocessing.sharedctypes
import multiprocessing.spawn
import multiprocessing.synchronize
import multiprocessing.util
import netrc
import numbers
import opcode
import operator
import optparse
import os
import os.path
import pathlib
import pathlib._os
import pathlib.types
import pdb
import pickle
import pickletools
import pkgutil
import platform
import plistlib
import poplib
import posix
import posixpath
import pprint
import pstats
import pty
import pwd
import py_compile
import pyclbr
import pydoc
import pydoc_data
import pydoc_data.topics
import pyexpat
import pyexpat.errors
import pyexpat.model
import queue
import quopri
import random
import re
import re._casefix
import re._compiler
import re._constants
import re._parser
import reprlib
import resource
import rlcompleter
import runpy
import sched
import secrets
import select
import selectors
import shelve
import shlex
import shutil
import signal
import site
import smtplib
import socket
import socketserver
import sqlite3
import sqlite3.__main__
import sqlite3.dbapi2
import sqlite3.dump
import ssl
import stat
import statistics
import string
import string.templatelib
import stringprep
import struct
import subprocess
import symtable
import sys
import sysconfig
import sysconfig.__main__
import syslog
import tabnanny
import tarfile
import tempfile
import termios
import textwrap
import threading
import time
import timeit
import token
import tokenize
import tomllib
import tomllib._parser
import tomllib._re
import tomllib._types
import trace
import traceback
import tracemalloc
import tty
import types
import typing
import unicodedata
import urllib
import urllib.error
import urllib.parse
import urllib.request
import urllib.response
import urllib.robotparser
import uuid
import warnings
import wave
import weakref
import webbrowser
import wsgiref
import wsgiref.handlers
import wsgiref.headers
import wsgiref.simple_server
import wsgiref.types
import wsgiref.util
import wsgiref.validate
import xml
import xml.dom
import xml.etree
import xml.parsers
import xml.parsers.expat
import xml.sax
import xml.sax.handler
import xml.sax.saxutils
import xml.sax.xmlreader
import xmlrpc
import xmlrpc.client
import xmlrpc.server
import zipapp
import zipfile
import zipimport
import zlib
import zoneinfo

Líný import všech balíčků

Předchozí skript nyní upravíme tak, aby před každým klíčovým slovem import byl zapsán modifikátor lazy. Tím se v Pythonu 3.15 dosáhne toho, že si interpret zapamatuje, že má provést import, ale provede ho až později – když to bude nezbytně nutné (nebo nikdy, pokud to nutné nebude):

lazy import abc
lazy import argparse
lazy import array
lazy import ast
lazy import asyncio
lazy import atexit
lazy import base64
lazy import bdb
lazy import binascii
lazy import bisect
lazy import builtins
lazy import bz2
lazy import calendar
lazy import cmath
lazy import cmd
lazy import code
lazy import codecs
lazy import codeop
lazy import collections
lazy import colorsys
lazy import compileall
lazy import compression
lazy import concurrent
lazy import concurrent.futures
lazy import concurrent.interpreters
lazy import configparser
lazy import contextlib
lazy import contextvars
lazy import copy
lazy import copyreg
lazy import csv
lazy import ctypes
lazy import ctypes.util
lazy import ctypes.wintypes
lazy import curses
lazy import curses.ascii
lazy import curses.has_key
lazy import curses.panel
lazy import curses.textpad
lazy import dataclasses
lazy import datetime
lazy import dbm
lazy import dbm.dumb
lazy import dbm.sqlite3
lazy import decimal
lazy import difflib
lazy import dis
lazy import doctest
lazy import email
lazy import email.message
lazy import email.mime
lazy import email.parser
lazy import email.policy
lazy import email.quoprimime
lazy import email.utils
lazy import encodings
lazy import encodings.aliases
lazy import encodings.ascii
lazy import encodings.base64_codec
lazy import encodings.big5
lazy import encodings.big5hkscs
lazy import encodings.bz2_codec
lazy import encodings.charmap
lazy import encodings.cp037
lazy import encodings.cp1006
lazy import encodings.cp1026
lazy import encodings.cp1125
lazy import encodings.cp1140
lazy import encodings.cp1250
lazy import encodings.cp1251
lazy import encodings.cp1252
lazy import encodings.cp1253
lazy import encodings.cp1254
lazy import encodings.cp1255
lazy import encodings.cp1256
lazy import encodings.cp1257
lazy import encodings.cp1258
lazy import encodings.cp273
lazy import encodings.cp424
lazy import encodings.cp437
lazy import encodings.cp500
lazy import encodings.cp720
lazy import encodings.cp737
lazy import encodings.cp775
lazy import encodings.cp850
lazy import encodings.cp852
lazy import encodings.cp855
lazy import encodings.cp856
lazy import encodings.cp857
lazy import encodings.cp858
lazy import encodings.cp860
lazy import encodings.cp861
lazy import encodings.cp862
lazy import encodings.cp863
lazy import encodings.cp864
lazy import encodings.cp865
lazy import encodings.cp866
lazy import encodings.cp869
lazy import encodings.cp874
lazy import encodings.cp875
lazy import encodings.cp932
lazy import encodings.cp949
lazy import encodings.cp950
lazy import encodings.euc_jis_2004
lazy import encodings.euc_jisx0213
lazy import encodings.euc_jp
lazy import encodings.euc_kr
lazy import encodings.gb18030
lazy import encodings.gb2312
lazy import encodings.gbk
lazy import encodings.hex_codec
lazy import encodings.hp_roman8
lazy import encodings.hz
lazy import encodings.idna
lazy import encodings.iso2022_jp
lazy import encodings.iso2022_jp_1
lazy import encodings.iso2022_jp_2
lazy import encodings.iso2022_jp_2004
lazy import encodings.iso2022_jp_3
lazy import encodings.iso2022_jp_ext
lazy import encodings.iso2022_kr
lazy import encodings.iso8859_1
lazy import encodings.iso8859_10
lazy import encodings.iso8859_11
lazy import encodings.iso8859_13
lazy import encodings.iso8859_14
lazy import encodings.iso8859_15
lazy import encodings.iso8859_16
lazy import encodings.iso8859_2
lazy import encodings.iso8859_3
lazy import encodings.iso8859_4
lazy import encodings.iso8859_5
lazy import encodings.iso8859_6
lazy import encodings.iso8859_7
lazy import encodings.iso8859_8
lazy import encodings.iso8859_9
lazy import encodings.johab
lazy import encodings.koi8_r
lazy import encodings.koi8_t
lazy import encodings.koi8_u
lazy import encodings.kz1048
lazy import encodings.latin_1
lazy import encodings.mac_arabic
lazy import encodings.mac_croatian
lazy import encodings.mac_cyrillic
lazy import encodings.mac_farsi
lazy import encodings.mac_greek
lazy import encodings.mac_iceland
lazy import encodings.mac_latin2
lazy import encodings.mac_roman
lazy import encodings.mac_romanian
lazy import encodings.mac_turkish
lazy import encodings.palmos
lazy import encodings.ptcp154
lazy import encodings.punycode
lazy import encodings.quopri_codec
lazy import encodings.raw_unicode_escape
lazy import encodings.rot_13
lazy import encodings.shift_jis
lazy import encodings.shift_jis_2004
lazy import encodings.shift_jisx0213
lazy import encodings.tis_620
lazy import encodings.undefined
lazy import encodings.unicode_escape
lazy import encodings.utf_16
lazy import encodings.utf_16_be
lazy import encodings.utf_16_le
lazy import encodings.utf_32
lazy import encodings.utf_32_be
lazy import encodings.utf_32_le
lazy import encodings.utf_7
lazy import encodings.utf_8
lazy import encodings.utf_8_sig
lazy import encodings.uu_codec
lazy import encodings.zlib_codec
lazy import ensurepip
lazy import ensurepip.__main__
lazy import ensurepip._uninstall
lazy import enum
lazy import errno
lazy import faulthandler
lazy import fcntl
lazy import filecmp
lazy import fileinput
lazy import fnmatch
lazy import fractions
lazy import ftplib
lazy import functools
lazy import gc
lazy import genericpath
lazy import getopt
lazy import getpass
lazy import gettext
lazy import glob
lazy import graphlib
lazy import grp
lazy import gzip
lazy import hashlib
lazy import heapq
lazy import hmac
lazy import html
lazy import html.entities
lazy import html.parser
lazy import http
lazy import http.client
lazy import http.cookiejar
lazy import http.cookies
lazy import http.server
lazy import imaplib
lazy import importlib
lazy import importlib._abc
lazy import importlib._bootstrap
lazy import importlib._bootstrap_external
lazy import importlib.abc
lazy import importlib.machinery
lazy import importlib.metadata
lazy import importlib.metadata._adapters
lazy import importlib.metadata._collections
lazy import importlib.metadata._functools
lazy import importlib.metadata._itertools
lazy import importlib.metadata._meta
lazy import importlib.metadata._text
lazy import importlib.metadata.diagnose
lazy import importlib.readers
lazy import importlib.resources
lazy import importlib.resources._adapters
lazy import importlib.resources._common
lazy import importlib.resources._functional
lazy import importlib.resources._itertools
lazy import importlib.resources.abc
lazy import importlib.resources.readers
lazy import importlib.resources.simple
lazy import importlib.simple
lazy import importlib.util
lazy import inspect
lazy import io
lazy import ipaddress
lazy import itertools
lazy import json
lazy import json.__main__
lazy import json.decoder
lazy import json.encoder
lazy import json.scanner
lazy import json.tool
lazy import keyword
lazy import linecache
lazy import locale
lazy import logging
lazy import logging.config
lazy import logging.handlers
lazy import lzma
lazy import mailbox
lazy import marshal
lazy import math
lazy import mimetypes
lazy import mmap
lazy import modulefinder
lazy import multiprocessing
lazy import multiprocessing.connection
lazy import multiprocessing.context
lazy import multiprocessing.dummy
lazy import multiprocessing.dummy.connection
lazy import multiprocessing.forkserver
lazy import multiprocessing.heap
lazy import multiprocessing.managers
lazy import multiprocessing.pool
lazy import multiprocessing.popen_fork
lazy import multiprocessing.popen_forkserver
lazy import multiprocessing.process
lazy import multiprocessing.queues
lazy import multiprocessing.reduction
lazy import multiprocessing.resource_sharer
lazy import multiprocessing.resource_tracker
lazy import multiprocessing.shared_memory
lazy import multiprocessing.sharedctypes
lazy import multiprocessing.spawn
lazy import multiprocessing.synchronize
lazy import multiprocessing.util
lazy import netrc
lazy import numbers
lazy import opcode
lazy import operator
lazy import optparse
lazy import os
lazy import os.path
lazy import pathlib
lazy import pathlib._os
lazy import pathlib.types
lazy import pdb
lazy import pickle
lazy import pickletools
lazy import pkgutil
lazy import platform
lazy import plistlib
lazy import poplib
lazy import posix
lazy import posixpath
lazy import pprint
lazy import pstats
lazy import pty
lazy import pwd
lazy import py_compile
lazy import pyclbr
lazy import pydoc
lazy import pydoc_data
lazy import pydoc_data.topics
lazy import pyexpat
lazy import pyexpat.errors
lazy import pyexpat.model
lazy import queue
lazy import quopri
lazy import random
lazy import re
lazy import re._casefix
lazy import re._compiler
lazy import re._constants
lazy import re._parser
lazy import reprlib
lazy import resource
lazy import rlcompleter
lazy import runpy
lazy import sched
lazy import secrets
lazy import select
lazy import selectors
lazy import shelve
lazy import shlex
lazy import shutil
lazy import signal
lazy import site
lazy import smtplib
lazy import socket
lazy import socketserver
lazy import sqlite3
lazy import sqlite3.__main__
lazy import sqlite3.dbapi2
lazy import sqlite3.dump
lazy import ssl
lazy import stat
lazy import statistics
lazy import string
lazy import string.templatelib
lazy import stringprep
lazy import struct
lazy import subprocess
lazy import symtable
lazy import sys
lazy import sysconfig
lazy import sysconfig.__main__
lazy import syslog
lazy import tabnanny
lazy import tarfile
lazy import tempfile
lazy import termios
lazy import textwrap
lazy import threading
lazy import time
lazy import timeit
lazy import token
lazy import tokenize
lazy import tomllib
lazy import tomllib._parser
lazy import tomllib._re
lazy import tomllib._types
lazy import trace
lazy import traceback
lazy import tracemalloc
lazy import tty
lazy import types
lazy import typing
lazy import unicodedata
lazy import urllib
lazy import urllib.error
lazy import urllib.parse
lazy import urllib.request
lazy import urllib.response
lazy import urllib.robotparser
lazy import uuid
lazy import warnings
lazy import wave
lazy import weakref
lazy import webbrowser
lazy import wsgiref
lazy import wsgiref.handlers
lazy import wsgiref.headers
lazy import wsgiref.simple_server
lazy import wsgiref.types
lazy import wsgiref.util
lazy import wsgiref.validate
lazy import xml
lazy import xml.dom
lazy import xml.etree
lazy import xml.parsers
lazy import xml.parsers.expat
lazy import xml.sax
lazy import xml.sax.handler
lazy import xml.sax.saxutils
lazy import xml.sax.xmlreader
lazy import xmlrpc
lazy import xmlrpc.client
lazy import xmlrpc.server
lazy import zipapp
lazy import zipfile
lazy import zipimport
lazy import zlib
lazy import zoneinfo

Porovnání výsledků

Oba skripty spustíme v interpretru Pythonu 3.15 a budeme měřit čas jejich dokončení.

Nejprve změříme dobu, kterou trvá dokončení prvního skriptu:

$ time ./python normal_import.py
 
real    0m0.480s
user    0m0.434s
sys     0m0.040s

Stejným způsobem změříme dobu trvání skriptu druhého:

$ time ./python lazy_import.py
 
real    0m0.030s
user    0m0.022s
sys     0m0.008s

V tomto (extrémním) případě je tedy druhý skript 16× rychlejší. Ovšem skutečně se jedná o extrémní příklad, který byl zvolen z toho důvodu, aby se ukázalo, že slovo lazy má skutečně nějaký efekt.

Malé ohlédnutí za staršími verzemi Pythonu: vestavěný typ frozenset

Již v předchozích verzích programovacího jazyka Python bylo možné využít vestavěný datový typ nazvaný frozenset, což je varianta množiny, která je neměnitelná (immutable), pochopitelně se všemi přednostmi i zápory, které to přináší. Zápis konstrukce „zamrzlé množiny“ je snadný a může vypadat následovně:

# Supported attachment content types
ATTACHMENT_CONTENT_TYPES = frozenset(
    {
        "text/plain",
        "application/json",
        "application/yaml",
        "application/xml",
        "image/jpeg",
        "image/png",
        "image/webp",
    }
)

Popř. můžeme tento zápis doplnit o typové informace (záleží, jakou „štábní kulturu“ váš projekt dodržuje):

# Supported attachment content types
ATTACHMENT_CONTENT_TYPES: Final[frozenset[str]] = frozenset(
    {
        "text/plain",
        "application/json",
        "application/yaml",
        "application/xml",
        "image/jpeg",
        "image/png",
        "image/webp",
    }
)
Poznámka: povšimněte si, že identifikátor frozenset není nutné importovat. Je tomu tak z toho důvodu, že je vestavěným datovým typem. A současně stojí za povšimnutí, že frozenset vystupuje ve dvou rolích: jedná se jak o název typu (zápis typové informace), tak i jméno konstruktoru zmrzlé množiny.

O tom, že je takto definovaná množina skutečně neměnitelná, se můžeme snadno přesvědčit pokusem o přidání nového prvku (či odebrání prvku atd.):

ATTACHMENT_CONTENT_TYPES = frozenset(
    {
        "text/plain",
        "application/json",
        "application/yaml",
        "application/xml",
        "image/jpeg",
        "image/webp",
    }
)
 
ATTACHMENT_CONTENT_TYPES.add("image/png")

Interpret Pythonu tyto operace nepovolí provést:

$ python frozen_set.py
 
Traceback (most recent call last):
  File "/home/ptisnovs/src/most-popular-python-libs/python3.15/frozen_set.py", line 5, in <module>
    ATTACHMENT_CONTENT_TYPES.add("image/png")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'frozenset' object has no attribute 'add'

Další operace s neměnitelnými množinami jsou ovšem plně podporovány. Například se jedná o operaci sjednocení množin:

DOCUMENT_CONTENT_TYPES: Final[frozenset[str]] = frozenset(
    {
        "text/plain",
        "application/json",
        "application/yaml",
        "application/xml",
    }
)
 
IMAGE_CONTENT_TYPES: Final[frozenset[str]] = frozenset(
    {
        "image/jpeg",
        "image/png",
        "image/webp",
    }
)
 
ATTACHMENT_CONTENT_TYPES = DOCUMENT_CONTENT_TYPES | IMAGE_CONTENT_TYPES
 
print(ATTACHMENT_CONTENT_TYPES)

Výsledek:

frozenset({'application/yaml', 'text/plain', 'application/xml', 'image/webp', 'image/jpeg', 'application/json', 'image/png'})

Nový vestavěný datový typ – frozendict

Do Pythonu 3.15 byl přidán vestavěný datový typ, který sice již vznikl v minulosti, ale bylo ho nutné explicitně importovat. Jedná se o neměnitelný slovník (frozendict). V předchozích verzích Pythonu se s tímto slovníkem pracovalo následovně (povšimněte si explicitního importu):

from frozendict import frozendict
 
d = frozendict(x=1, y=2, z="foo")
print(d)

V Pythonu 3.15 je možné import vynechat a použít tak vestavěný datový typ se stejným jménem:

d = frozendict(x=1, y=2, z="foo")
print(d)

Výsledky:

frozendict({'x': 1, 'y': 2, 'z': 'foo'})

A vzhledem k tomu, že je tento slovník skutečně neměnitelný, povede snaha o modifikaci jeho prvku, přidání prvku nebo vymazání prvku, k běhové chybě:

d = frozendict(x=1, y=2, z="foo")
print(d)
 
d["a"] = "bar"
 
del d["z"]
frozendict({'x': 1, 'y': 2, 'z': 'foo'})
Traceback (most recent call last):
  File "/tmp/ramdisk/Python-3.15.1/frozen_dict_2.py", line 4, in <module>
    d["a"] = "bar"
    ~^^^^^
TypeError: 'frozendict' object does not support item assignment

Podobně jako v případě typu frozenset i v případě neměnitelných slovníků platí, že frozendict je současně názvem typu i konstruktorem:

d: frozendict = frozendict(x=1, y=2, z="foo")
print(d)
 
d["a"] = "bar"
 
del d["z"]

Neměnitelný slovník lze vytvořit ze slovníku běžného. Co se týče pořadí prvků (při iteraci), je v obou případech zachování jejich pořadí:

planets = frozendict({
    "Mercury": 57_910_000,
    "Venus": 108_200_000,
    "Earth": 149_600_000,
    "Mars": 227_900_000,
    "Jupiter": 778_500_000,
    "Saturn": 1_434_000_000,
    "Uranus": 2_871_000_000,
    "Neptune": 4_495_000_000,
})
 
d = frozendict(planets)
 
for name, distance in d.items():
    print(name, distance)

Výsledky:

Mercury 57910000
Venus 108200000
Earth 149600000
Mars 227900000
Jupiter 778500000
Saturn 1434000000
Uranus 2871000000
Neptune 4495000000

Jak je tomu s neměnitelnými seznamy?

Množiny set mají svoji neměnitelnou variantu nazvanou frozenset a nově mají i slovníky (dict) standardní neměnitelnou variantu nazvanou frozendict. Možná si někdo položí otázku, jak je tomu se seznamy (list)? Typ frozenlist skutečně prozatím neexistuje, i když se už několik vývojářů snažilo o jeho přidání – a v registru balíčků PyPI nalezneme i balíčky s jeho implementací. Je tomu tak z toho důvodu, že v Pythonu je k dispozici datový typ n-tice (tuple). Tento datový typ je sice z hlediska sémantiky „přetížen“ (využívá se v několika oblastech pro několik zcela rozdílných účelů), ovšem je ho možné použít i ve funkci plnohodnotných neměnitelných seznamů:

x: tuple[str, ...] = ("foo", "bar", "baz")
print(x)
 
x[1] = "xyzzy"
print(x)

Modifikace n-tice není možná:

('foo', 'bar', 'baz')
Traceback (most recent call last):
  File "/tmp/ramdisk/Python-3.15.1/tuple_1.py", line 4, in <module>
    x[1] = "xyzzy"
    ~^^^
TypeError: 'tuple' object does not support item assignment

Taktéž není povoleno přidávání prvků do n-tice nebo jejich mazání:

x: tuple[str, ...] = ("foo", "bar", "baz")
print(x)
 
x.append("xyzzy")
print(x)

Výsledky:

('foo', 'bar', 'baz')
Traceback (most recent call last):
  File "/tmp/ramdisk/Python-3.15.1/tuple_2.py", line 4, in <module>
    x.append("xyzzy")
    ^^^^^^^^
AttributeError: 'tuple' object has no attribute 'append'. Did you mean to use a 'list' object?

Jaký význam má termín „immutable“ v jazyce Python?

V předchozích kapitolách jsem několikrát použil termín neměnitelnost (immutability). Ten se ovšem v souvislosti s datovými typy (a především s kontejnery) používá ve dvou významech:

  1. Úplná neměnitelnost jak kontejneru (nelze přidávat ani mazat prvky), tak i hodnot v něm uložených (prvky nelze modifikovat).
  2. Neměnitelný je jen kontejner (opět: nelze přidávat ani mazat prvky), ovšem samotné prvky mohou být měnitelné. To záleží na jejich typu (numerické hodnoty a řetězce jsou neměnitelné), vložené seznamy měnitelné atd.

V Pythonu je použit druhý způsob, což si ostatně můžeme velmi snadno ověřit. Například n-tice je neměnitelná, ale modifikovat lze její prvek (typu seznam):

t = ("foo", "bar", "baz", [])
 
print(t)
 
t[3].append(1)
t[3].append(2)
t[3].append(3)
t[3].append(3)
 
print(t)

Z výsledků je patrné, že seznam lze skutečně modifikovat:

('foo', 'bar', 'baz', [])
('foo', 'bar', 'baz', [1, 2, 3, 3])

Podívejme se na složitější (poněkud umělý) příklad, ve kterém se používá neměnitelný slovník, jehož prvky jsou seznamy. I tyto seznamy lze modifikovat:

planets = frozendict({
    "Mercury": [57_910_000],
    "Venus": [108_200_000],
    "Earth": [149_600_000],
    "Mars": [227_900_000],
    "Jupiter": [778_500_000],
    "Saturn": [1_434_000_000],
    "Uranus": [2_871_000_000],
    "Neptune": [4_495_000_000],
})
 
d = frozendict(planets)
 
for name, distance in d.items():
    print(name, distance)
 
print("-"*60)
 
d["Mercury"][0] = 10000
d["Earth"].append("foo")
 
for name, distance in d.items():
    print(name, distance)

Výsledky opět ukazují modifikovatelnost prvků neměnitelného seznamu:

Mercury [57910000]
Venus [108200000]
Earth [149600000]
Mars [227900000]
Jupiter [778500000]
Saturn [1434000000]
Uranus [2871000000]
Neptune [4495000000]
------------------------------------------------------------
Mercury [10000]
Venus [108200000]
Earth [149600000, 'foo']
Mars [227900000]
Jupiter [778500000]
Saturn [1434000000]
Uranus [2871000000]
Neptune [4495000000]
Poznámka: z tohoto pohledu je možná dobré, že se v Pythonu používá termín frozen a nikoli immutable.

Rozbalení hodnot v programových konstrukcích využívajících generátorovou notaci

V Pythonu je pro seznamy, slovníky i množiny podporována syntaktická konstrukce označovaná jako generátorová notace. Ta umožňuje zápis „funkcionálních“ operací typu map a filter idiomatickým způsobem (pro Python).

Ukažme si příklad zápisu generátorové notace pro seznamy:

seznam = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
seznam2 = [item * 2 for item in seznam]
 
seznam3 = [item for item in seznam if item % 3 == 0]
 
print(seznam)
print(seznam2)
print(seznam3)

Podobným způsobem lze zapsat generátorovou notaci pro množiny:

s = {x*2 for x in range(11)}
 
print(s)

Generátorová notace slovníku se zapisuje následujícím způsobem:

d = {x: x*2 for x in range(11) if x%3 != 0}
 
print(d)

V Pythonu 3.15 byly tyto programové konstrukce rozšířeny o možnost rozbalení jednotlivých prvků, které jsou ze seznamu, množiny atd. získávány. To například umožňuje zploštění (flattening) datových struktur atd. Nový zápis využívá znaků * a **. V navazujících kapitolách budou uvedeny praktické ukázky.

Podpora pro rozbalení prvků v konstrukci generátorové notace seznamů

V následujícím skriptu je definován seznam, jehož prvky obsahují taktéž seznamy (s různým počtem prvků). Na dalším řádku je zapsána konstrukce generátorové notace seznamu, která v tomto případě pouze transformuje vstupní seznam do seznamu nového. Prvky tohoto nového seznamu jsou pouze zkopírovány ze seznamu původního, což si ověříme postupným výpisem všech prvků:

lists = [["foo"], ["bar", "baz"], ["x", "y", "z"]]
 
x = [L for L in lists]
 
for i, item in enumerate(x):
    print(i, item)

Výsledek:

0 ['foo']
1 ['bar', 'baz']
2 ['x', 'y', 'z']

Pokud by bylo zapotřebí prvky původního seznamu rozbalit a následně je vložit v rozbalené podobě do seznamu výsledného, je nutné v Pythonu 3.14 (i ve starších verzích Pythonu) použít například následující zápis využívající generátorovou notaci:

lists = [["foo"], ["bar", "baz"], ["x", "y", "z"]]
 
x = [item for L in lists for item in L]
 
for i, item in enumerate(x):
    print(i, item)

Výsledek:

0 foo
1 bar
2 baz
3 x
4 y
5 z

V Pythonu 3.15 je podporován kratší a do jisté míry i čitelnější zápis, který vypadá následovně:

lists = [["foo"], ["bar", "baz"], ["x", "y", "z"]]
 
x = [*L for L in lists]
 
for i, item in enumerate(x):
    print(i, item)

Výsledek bude totožný s předchozí verzí skriptu:

0 foo
1 bar
2 baz
3 x
4 y
5 z
Poznámka: podobné využití znaku * jsme už mohli vidět v minulosti (přesněji řečeno v předchozích verzích Pythonu), konkrétně při realizaci operace rozbalení seznamu či množiny při volání funkce:
values = [1, 2, 3]
print(*values)

Podpora pro rozbalení prvků v konstrukci generátorové notace množin

Podobným způsobem můžeme řešit „zploštění“ seznamu množin do jediné množiny. Zápis této operace bez využití nových vlastností Pythonu 3.15 by mohl vypadat následovně:

sets = [{"foo"}, {"bar", "baz"}, {"x", "y", "z"}]
 
x = {x for S in sets for x in S}
 
for i, item in enumerate(x):
    print(i, item)

Výsledky:

0 baz
1 foo
2 x
3 bar
4 z
5 y
Poznámka: povšimněte si, že množina nezajišťuje zachování pořadí vložených prvků.

Nová konstrukce podporovaná Pythonem 3.15 umožňuje zápis zkrátit a zpřehlednit:

sets = [{"foo"}, {"bar", "baz"}, {"x", "y", "z"}]
 
x = {*S for S in sets}
 
for i, item in enumerate(x):
    print(i, item)

Opět se podívejme na výsledky, které mohou vypadat následovně:

0 bar
1 foo
2 y
3 z
4 x
5 baz

Pro úplnost se ještě podívejme na chování skriptu v případě, že původní seznam obsahuje množiny, které mají stejné prvky:

sets = [{"foo", "bar"}, {"bar", "baz"}, {"foo", "baz"}]
 
x = {*S for S in sets}
 
for i, item in enumerate(x):
    print(i, item)

Výsledkem je tato množina:

0 foo
1 baz
2 bar

Podpora pro rozbalení prvků v konstrukci generátorové notace slovníků

Práce se slovníky je v několika ohledech odlišná od zpracování seznamů a množin. Největší rozdíl spočívá v tom, že (většinou) musíme explicitně zpracovávat nejenom hodnoty uložené do slovníku, ale i klíče, ke kterým jsou tyto hodnoty přiřazeny. To se projevuje i v odlišném způsobu zápisu rozbalení prvků v generátorové notaci.

Opět se podívejme na jednoduchý příklad, ve kterém budeme spojovat několik slovníků (uložených například v seznamu) do slovníku jediného. V předchozích verzích Pythonu by mohl zápis s využitím generátorové notace vypadat například takto:

dicts = [{"foo": 1}, {"bar": 2}, {"baz": 3}, {"foo": 4}]
 
x = {k: v for D in dicts for k,v in D.items()}
 
for key, value in x.items():
    print(key, value)

Z vypsaných výsledků je patrné, že došlo k přepisu první dvojice klíč-hodnota dvojicí poslední (foo:4):

foo 4
bar 2
baz 3

Nový způsob zápisu používá v generátorové notaci dvě hvězdičky (což odpovídá expanzi slovníku při volání funkce s keyword parametry – viz další odstavec):

dicts = [{"foo": 1}, {"bar": 2}, {"baz": 3}, {"foo": 4}]
 
x = {**D for D in dicts}
 
for key, value in x.items():
    print(key, value)

Opět pro jistotu zkontrolujeme chování tohoto skriptu. Výsledný slovník by měl vypadat následovně:

foo 4
bar 2
baz 3

V předchozím odstavci jsem zmínil expanzi slovníku při volání funkce s keyword parametry. Jedná se o následující konstrukci (existuje i ve starších verzích Pythonu):

def login(name, password):
    print(name, password)
 
data = {"name": "root", "password": "nbusr123"}
greet(**data)

Otázka na závěr. Jaké zprávy vypíše následující skript a proč?

dicts = [{"foo": 1, "bar": 1}, {"bar": 2, "baz": 2}, {"baz": 3, "foo": 4}]
 
x = {**D for D in dicts}
 
for key, value in x.items():
    print(key, value)

Další vylepšení a rozšíření Pythonu 3.15

V Pythonu 3.15 nalezneme i některá další vylepšení a rozšíření. Jedná se především o vylepšení profileru, úpravy just-in-time (JIT) překladače, vylepšení chybových hlášení (ta pomohou nejenom vývojářům, ale i AI agentům) atd. Těmito vylepšeními se budeme podrobněji zabývat v navazujícím článku, protože především modifikace provedené v JIT překladači mohou poměrně výrazně urychlit skripty i větší aplikace naprogramované v Pythonu. A pochopitelně nezapomeneme ani na porovnání celkové rychlosti Pythonu 3.15 v porovnání s jeho staršími verzemi.

Příloha: rezervovaná klíčová slova a „měkká klíčová slova“

V předchozím textu jsme si mj. popsali i nové klíčové slovo lazy, které patří mezi klíčová slova platná pouze v rámci určitého kontextu (zde konkrétně společně s příkazem import). Klíčová slova jsou dostupná i přímo v běžícím programu (resp. v interpretru Pythonu). Následující příkaz ukazuje, jak vypsat skutečná rezervovaná klíčová slova i jak zjistit, jestli nějaký řetězec reprezentuje klíčové slovo nebo jen „měkké“ klíčové slovo:

import keyword
print(keyword.kwlist)
print(keyword.iskeyword("match"))
print(keyword.issoftkeyword("match"))

Výsledek pro Python 3.15 bude stejný, jako například i pro Python 3.14:

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for',
'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not',
'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
False
True

Podobně můžeme otestovat řetězec „lazy“:

import keyword
print(keyword.kwlist)
print(keyword.iskeyword("lazy"))
print(keyword.issoftkeyword("lazy"))

Předchozí verze Pythonu (<=3.14) vypíšou tyto informace (včetně False na konci):

Wazuh školení

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for',
'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not',
'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
False
False

Naproti tomu Python 3.15 na konci vypíše hodnotu True:

['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for',
'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not',
'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
False
True

Repositář s demonstračními příklady

Demonstrační příklady vytvořené pro Python verze 3.15 a popsané v dnešním článku najdete v repositáři https://github.com/tisnik/most-popular-python-libs/. Následují odkazy na jednotlivé příklady:

# Příklad Stručný popis příkladu Adresa
1 keywords1.py výpis rezervovaných klíčových slov a test, zda je match klíčovým slovem https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/ke­ywords1.py
2 keywords2.py výpis rezervovaných klíčových slov a test, zda je lazy klíčovým slovem https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/ke­ywords2.py
3 lazy_keyword.py klíčové slovo lazy, ovšem použité mimo kontext konstrukce import https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/la­zy_keyword.py
       
4 normal_import.py import většiny balíčků ze standardní knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/nor­mal_import.py
5 lazy_import.py líný import většiny balíčků ze standardní knihovny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/la­zy_import.py
       
6 frozen_set_1.py vestavěný datový typ frozenset https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_set_1.py
7 frozen_set_2.py vestavěný datový typ frozenset https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_set_2.py
8 frozen_dict_1.py vestavěný datový typ frozendict, korektní způsob použití https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_dict_1.py
9 frozen_dict_2.py vestavěný datový typ frozendict, nekorektní způsob použití https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_dict_2.py
10 frozen_dict_3.py vestavěný datový typ frozendict, typové anotace https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_dict_3.py
11 frozen_dict_4.py konstrukce frozendict z běžného slovníku https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/fro­zen_dict_4.py
12 tuple_1.py pokus o modifikaci n-tice https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/tuple_1.py
13 tuple_2.py pokus o modifikaci n-tice https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/tuple_2.py
       
14 immutability_1.py neměnitelné sekvence mohou obsahovat měnitelné prvky, příklad číslo 1 https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/im­mutability_1.py
15 immutability_2.py neměnitelné sekvence mohou obsahovat měnitelné prvky, příklad číslo 2 https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/im­mutability_2.py
       
16 list_comprehension.py generátorová notace seznamu https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/lis­t_comprehension.py
17 set_comprehension.py generátorová notace množiny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/set_com­prehension.py
18 dict_comprehension.py generátorová notace slovníku https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/dic­t_comprehension.py
       
19 unpacking_list_comprehension_1.py běžná generátorová notace seznamu https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_list_comprehension_1­.py
20 unpacking_list_comprehension_2.py explicitní rozbalení prvků v generátorové notaci seznamu https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_list_comprehension_2­.py
21 unpacking_list_comprehension_3.py nově podporované rozbalení prvků v generátorové notaci seznamu https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_list_comprehension_3­.py
22 unpacking_set_comprehension_1.py explicitní rozbalení prvků v generátorové notaci množiny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_set_comprehension_1­.py
23 unpacking_set_comprehension_2.py nově podporované rozbalení prvků v generátorové notaci množiny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_set_comprehension_2­.py
24 unpacking_set_comprehension_3.py nově podporované rozbalení prvků v generátorové notaci množiny https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_set_comprehension_3­.py
25 unpacking_dict_comprehension_1.py explicitní rozbalení prvků v generátorové notaci slovníku https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_dict_comprehension_1­.py
26 unpacking_dict_comprehension_2.py nově podporované rozbalení prvků v generátorové notaci slovníku https://github.com/tisnik/most-popular-python-libs/blob/master/python3.15/un­packing_dict_comprehension_2­.py

Odkazy na Internetu

  1. Python 3.15.0
    https://www.python.org/dow­nloads/release/python-3150/
  2. Python 3.14.0
    https://test.python.org/dow­nloads/release/python-3140/
  3. PEP 765 – Disallow return/break/continue that exit a finally block
    https://peps.python.org/pep-0765/
  4. PEP 758 – Allow except and except* expressions without parentheses
    https://peps.python.org/pep-0758/
  5. stdlib-list
    https://github.com/pypi/stdlib-list
  6. What’s new in Python 3.15 (official)
    https://docs.python.org/3/what­snew/3.15.html
  7. What’s new in Python 3.14 (official)
    https://docs.python.org/3/what­snew/3.14.html
  8. What’s New In Python 3.13 (official)
    https://docs.python.org/3/what­snew/3.13.html
  9. What’s New In Python 3.12 (official)
    https://docs.python.org/3/what­snew/3.12.html
  10. What’s New In Python 3.11 (official)
    https://docs.python.org/3/what­snew/3.11.html
  11. What’s New In Python 3.12
    https://dev.to/mahiuddindev/python-312-4n43
  12. PEP 698 – Override Decorator for Static Typing
    https://peps.python.org/pep-0698/
  13. PEP 484 – Type Hints
    https://www.python.org/dev/peps/pep-0484/
  14. What’s New In Python 3.5
    https://docs.python.org/3­.5/whatsnew/3.5.html
  15. 26.1. typing — Support for type hints
    https://docs.python.org/3­.5/library/typing.html#mo­dule-typing
  16. Type Hints – Guido van Rossum – PyCon 2015 (youtube)
    https://www.youtube.com/wat­ch?v=2wDvzy6Hgxg
  17. Python 3.5 is on its way
    https://lwn.net/Articles/650904/
  18. Type hints
    https://lwn.net/Articles/640359/
  19. Stránka projektu PDM
    https://pdm.fming.dev/latest/
  20. PDF na GitHubu
    https://github.com/pdm-project/pdm
  21. PEP 582 – Python local packages directory
    https://peps.python.org/pep-0582/
  22. PDM na PyPi
    https://pypi.org/project/pdm/
  23. Which Python package manager should you use?
    https://towardsdatascience.com/which-python-package-manager-should-you-use-d0fd0789a250
  24. How to Use PDM to Manage Python Dependencies without a Virtual Environment
    https://www.youtube.com/wat­ch?v=qOIWNSTYfcc
  25. What are the best Python package managers?
    https://www.slant.co/topics/2666/~best-python-package-managers
  26. PEP 621 – Storing project metadata in pyproject.toml
    https://peps.python.org/pep-0621/
  27. Pick a Python Lockfile and Improve Security
    https://blog.phylum.io/pick-a-python-lockfile-and-improve-security/
  28. PyPA specifications
    https://packaging.python.or­g/en/latest/specification­s/
  29. Creation of virtual environments
    https://docs.python.org/3/li­brary/venv.html
  30. How to Use virtualenv in Python
    https://learnpython.com/blog/how-to-use-virtualenv-python/
  31. Python Virtual Environments: A Primer
    https://realpython.com/python-virtual-environments-a-primer/
  32. virtualenv Cheatsheet
    https://aaronlelevier.git­hub.io/virtualenv-cheatsheet/
  33. Installing Python Modules
    https://docs.python.org/3/in­stalling/index.html
  34. Python: The Documentary | An origin story
    https://www.youtube.com/wat­ch?v=GfH4QL4VqJ0
  35. History of Python
    https://en.wikipedia.org/wi­ki/History_of_Python
  36. History of Python
    https://www.geeksforgeeks­.org/python/history-of-python/
  37. IPython: jedno z nejpropracovanějších interaktivních prostředí pro práci s Pythonem
    https://www.root.cz/clanky/ipython-jedno-z-nejpropracova-nejsich-interaktivnich-prostredi-pro-praci-s-pythonem/
  38. Další kulaté výročí v IT: dvacet let existence Pythonu 2
    https://www.root.cz/clanky/dalsi-kulate-vyroci-v-it-dvacet-let-existence-pythonu-2/
  39. PEP 684 – A Per-Interpreter GIL
    https://peps.python.org/pep-0684/
  40. What Is the Python Global Interpreter Lock (GIL)?
    https://realpython.com/python-gil/
  41. PEP 703 – Making the Global Interpreter Lock Optional in CPython
    https://peps.python.org/pep-0703/
  42. GlobalInterpreterLock
    https://wiki.python.org/mo­in/GlobalInterpreterLock
  43. What is the Python Global Interpreter Lock (GIL)
    https://www.geeksforgeeks.org/what-is-the-python-global-interpreter-lock-gil/
  44. Let's remove the Global Interpreter Lock
    https://www.pypy.org/posts/2017/08/lets-remove-global-interpreter-lock-748023554216649595.html
  45. Global interpreter lock
    https://en.wikipedia.org/wi­ki/Global_interpreter_lock
  46. Rychlost CPythonu 3.11 a 3.12 v porovnání s JIT a AOT překladači
    https://www.root.cz/clanky/rychlost-cpythonu-3-11-a-3-12-v-porovnani-s-jit-a-aot-prekladaci-pythonu/
  47. Dokumentace Pythonu: balíček queue
    https://docs.python.org/3/li­brary/queue.html
  48. Dokumentace Pythonu: balíček threading
    https://docs.python.org/3/li­brary/threading.html?
  49. Dokumentace Pythonu: balíček multiprocessing
    https://docs.python.org/3/li­brary/multiprocessing.html
  50. Dokumentace Pythonu: balíček asyncio
    https://docs.python.org/3/li­brary/asyncio.html
  51. Synchronization Primitives
    https://docs.python.org/3/li­brary/asyncio-sync.html
  52. Coroutines
    https://docs.python.org/3/li­brary/asyncio-task.html
  53. Queues
    https://docs.python.org/3/li­brary/asyncio-queue.html
  54. python-csp
    https://python-csp.readthedocs.io/en/latest/
  55. TrellisSTM
    http://peak.telecommunity­.com/DevCenter/TrellisSTM
  56. Python Multithreading and Multiprocessing Tutorial
    https://www.toptal.com/pyt­hon/beginners-guide-to-concurrency-and-parallelism-in-python
  57. PEP 810: Explicit lazy imports
    https://peps.python.org/pep-0810/
  58. PEP 814: Add frozendict built-in type
    https://peps.python.org/pep-0814/
  59. PEP 661: Add sentinel built-in type
    https://peps.python.org/pep-0661/
  60. PEP 798: Unpacking in comprehensions
    https://peps.python.org/pep-0798/
  61. PEP 686: Python now uses UTF-8 as the default encoding
    https://peps.python.org/pep-0686/
  62. PEP 829: Package startup configuration files
    https://peps.python.org/pep-0829/
  63. Immutable (Python list of terms)
    https://docs.python.org/3/glos­sary.html#term-immutable
  64. builtins — Built-in objects
    https://docs.python.org/3/li­brary/builtins.html#module-builtins
  65. Frozendict
    https://docs.python.org/3/bu­iltins/stdtypes.html#frozen­dict
  66. Frozenset
    https://docs.python.org/3/bu­iltins/stdtypes.html#frozen­set
  67. PEP 744 – JIT Compilation
    https://peps.python.org/pep-0744/
  68. Why is FrozenList different from tuple?
    https://stackoverflow.com/qu­estions/25676107/why-is-frozenlist-different-from-tuple
  69. frozenlist 1.8.0 (PyPI)
    https://pypi.org/project/frozenlist/
  70. frozenlist (GitHub)
    https://github.com/aio-libs/frozenlist
  71. Python's frozendict: The Immutable Dictionary
    https://realpython.com/python315-frozendict/#freeze-an-existing-mapping
  72. Comprehensions in Python – GeeksforGeeks
    https://www.geeksforgeeks­.org/python/comprehensions-in-python/
  73. PEP 728 – TypedDict with Typed Extra Items
    https://peps.python.org/pep-0728/
  74. PEP 800 – Disjoint bases in the type system
    https://peps.python.org/pep-0800/
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.