Seria o zmianach w Pythonie od 3.8 do 3.15, przy każdej wersji z wybranymi nowościami. Pierwsza część obejmuje wersje 3.8 i 3.9: operator :=, parametry tylko pozycyjne, operator | dla słowników i moduł zoneinfo.
Operator przypisania w wyrażeniu :=
data = [3, 8, 1, 9]
if (n := len(data)) > 3:
print(f"Za dużo elementów: {n}") # Za dużo elementów: 4
while (line := input("> ")) != "koniec":
print(line.upper())
squares = [y for x in data if (y := x * x) > 10] # [64, 81]
Parametry tylko pozycyjne
def clamp(value, /, low=0, high=100):
return max(low, min(value, high))
clamp(150) # 100
clamp(150, high=120) # 120
clamp(value=150) # TypeError: clamp() got some positional-only arguments passed as keyword arguments: 'value'
Specyfikator = w f-stringach
x, y = 3, 4
print(f"{x=}, {y=}") # x=3, y=4
print(f"{x * y = }") # x * y = 12
print(f"{3.14159=:.2f}") # 3.14159=3.14
importlib.metadata
from importlib.metadata import version, requires
version("requests") # '2.32.3'
requires("requests") # ['charset_normalizer<4,>=2', 'idna<4,>=2.5', ...]
typing.Literal
from typing import Literal
def set_mode(mode: Literal["r", "w"]) -> None: ...
set_mode("r") # OK
set_mode("x") # type checker: "x" nie należy do Literal["r", "w"]
typing.Final
from typing import Final, final
MAX_RETRIES: Final = 3
MAX_RETRIES = 5 # type checker: nie można przypisać do stałej Final
@final
class Config: ...
class MyConfig(Config): ... # type checker: Config jest final
typing.Protocol
from typing import Protocol
class Closable(Protocol):
def close(self) -> None: ...
class Connection: # nie dziedziczy po Closable
def close(self) -> None:
print("zamknięte")
def shutdown(resource: Closable) -> None:
resource.close()
shutdown(Connection()) # OK: Connection ma metodę close()
shutdown("tekst") # type checker: str nie ma close()
typing.TypedDict
from typing import TypedDict
class User(TypedDict):
name: str
age: int
user: User = {"name": "Ala", "age": 30} # OK
bad: User = {"name": "Ala", "age": "30"} # type checker: age musi być int
functools.cached_property
from functools import cached_property
class Report:
def __init__(self, rows):
self.rows = rows
@cached_property
def total(self) -> int:
print("liczę...")
return sum(self.rows)
r = Report([1, 2, 3])
r.total # liczę... 6
r.total # 6 (bez ponownego liczenia)
math.prod(), math.isqrt(), math.dist()
import math
math.prod([2, 3, 4]) # 24
math.isqrt(17) # 4
math.dist((0, 0), (3, 4)) # 5.0
multiprocessing.shared_memory
from multiprocessing import shared_memory
shm = shared_memory.SharedMemory(create=True, size=4)
shm.buf[:4] = b"abcd"
# w innym procesie: dostęp po nazwie bloku
other = shared_memory.SharedMemory(name=shm.name)
bytes(other.buf[:4]) # b'abcd'
other.close()
shm.close()
shm.unlink()
REPL asyncio
$ python -m asyncio
>>> import asyncio
>>> await asyncio.sleep(1, result="gotowe")
'gotowe'
Operatory | i |= dla słowników
defaults = {"host": "localhost", "port": 5432}
config = defaults | {"port": 6543} # {'host': 'localhost', 'port': 6543}
defaults |= {"user": "admin"} # modyfikacja w miejscu
defaults # {'host': 'localhost', 'port': 5432, 'user': 'admin'}
Generyki wbudowanych kolekcji
def average(values: list[float]) -> dict[str, float]:
return {"avg": sum(values) / len(values)}
point: tuple[int, int] = (3, 4)
tags: set[str] = {"python", "typing"}
Swobodniejsze dekoratory
from functools import cache, lru_cache
caches = {"bez_limitu": cache, "maly": lru_cache(maxsize=16)}
@caches["maly"] # przed 3.9: SyntaxError
def square(n: int) -> int:
return n * n
str.removeprefix() i str.removesuffix()
"test_settings.py".removeprefix("test_") # 'settings.py'
"test_settings.py".lstrip("test_") # 'ings.py' (usuwa znaki t, e, s, _)
"raport.csv".removesuffix(".csv") # 'raport'
zoneinfo
from datetime import datetime
from zoneinfo import ZoneInfo
meeting = datetime(2026, 3, 30, 15, 0, tzinfo=ZoneInfo("Europe/Warsaw"))
print(meeting.astimezone(ZoneInfo("America/New_York")))
# 2026-03-30 09:00:00-04:00
graphlib.TopologicalSorter
from graphlib import TopologicalSorter
deps = {
"app": ["db", "cache"],
"db": ["config"],
"cache": ["config"],
}
list(TopologicalSorter(deps).static_order())
# ['config', 'db', 'cache', 'app']
functools.cache
from functools import cache
@cache
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(80) # 23416728348467685, natychmiast
fib.cache_info() # CacheInfo(hits=78, misses=81, maxsize=None, currsize=81)
math.lcm()
import math
math.lcm(4, 6, 10) # 60
math.gcd(12, 18, 24) # 6 (od 3.9 przyjmuje dowolną liczbę argumentów)
typing.Annotated
from typing import Annotated, get_type_hints
Port = Annotated[int, "zakres 1–65535"]
def connect(port: Port) -> None: ...
get_type_hints(connect, include_extras=True)["port"]
# typing.Annotated[int, 'zakres 1–65535']
Parser PEG