Nové vlastnosti Pythonu 3.14 v praxi: vliv odstranění GIL a využití více interpretrů

21. 10. 2025
Doba čtení: 27 minut

Sdílet

Python 3.14
Autor: Python
Zaměříme se na dvě technologie, které Python 3.14 nabízí: podporu pro interpret bez GIL a knihovnu s API, které umožňuje využít větší množství interpretrů spouštěných v rámci jednoho procesu.

Obsah

1. Nové vlastnosti Pythonu 3.14 v praxi: vliv odstranění GILu a využití více interpretrů

2. Překlad Pythonu 3.14 s vypnutým GILem

3. První benchmark: klasický neefektivní algoritmus bublinkového řazení

4. Porovnání rychlosti sekvenční varianty benchmarku pro různé verze Pythonu

5. Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s GILem i bez GILu

6. Složitější výpočty s velkým množstvím numerických operací

7. Výsledky benchmarku pro různé verze Pythonu

8. Paralelní varianta výpočtu: využití proměnného počtu vláken

9. Výsledky benchmarku pro různé verze Pythonu

10. Rozdíly celkové doby výpočtů v Pythonu 3.14: GIL vs noGIL

11. Podpora pro větší množství interpretrů v běžícím procesu

12. Výpis interpretrů, konstrukce nového interpretru

13. Spuštění kódu v novém interpretru

14. Spuštění kódu v novém vláknu

15. Sdílí interpretry svůj stav?

16. Chování programu při spuštění dvou souběžných úloh v různých interpretrech

17. Chování programu v případě, že je v interpretru vyhozena nezachycená výjimka

18. Odkazy na články s problematikou souběžnosti a paralelnosti v Pythonu

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

20. Odkazy na Internetu

1. Nové vlastnosti Pythonu 3.14 v praxi: vliv odstranění GILu a využití více interpretrů

Na článek Python 3.14: t-řetězce, barvičky, lepší nápověda a odcházející GIL, který byl vydán minulý týden, dnes navážeme. Zaměříme se na dvě technologie, které Python 3.14 nabízí. V první řadě se jedná o volitelné odstranění GILu (což sice není úplně žhavá novinka, ale v rámci Pythonu 3.14 došlo v této oblasti k různým vylepšením).

Ve druhé části článku si popíšeme API, které umožňuje využít větší množství interpretrů spouštěných v rámci jednoho procesu. Tyto interpretry jsou od sebe do značné míry izolovány, ale v případě potřeby mezi sebou mohou v nich spuštěné programy komunikovat přes fronty zpráv (message queue), což je metoda, která je v ekosystému jazyka Python používaná i v případě běžného multithreadingu a multiprocessingu.

2. Překlad Pythonu 3.14 s vypnutým GILem

Ještě před tím, než si vyzkoušíme benchmarky popsané v rámci dalších kapitol, je nutné si přeložit Python 3.14 ve verzi s GILem (běžná instalace) i taktéž bez GILu (budeme tedy mít dva samostatné interpretry). Průběh překladu 3.14 byl již popsán minule, takže jen velmi krátce:

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):

$ cd Python-3.14.0
$ ./configure
$ make

Výsledkem bude spustitelný interpret jazyka Python 3.14 s povoleným GILem.

Pro překlad varianty interpretru se zakázaným GILem je nutné příkaz configure volat s tímto parametrem:

$ ./configure --disable-gil

Tento příkaz vygeneruje novou variantu souboru Makefile:

...
...
...
configure: creating ./config.status
config.status: creating Makefile.pre
config.status: creating Misc/python.pc
config.status: creating Misc/python-embed.pc
config.status: creating Misc/python-config.sh
config.status: creating Modules/Setup.bootstrap
config.status: creating Modules/Setup.stdlib
config.status: creating Modules/ld_so_aix
config.status: creating pyconfig.h
config.status: pyconfig.h is unchanged
configure: creating Modules/Setup.local
configure: creating Makefile
configure:

V nové variantě Makefile by se měly nově objevit tyto dva řádky:

# configure script arguments
CONFIG_ARGS=     '--disable-gil'

Další překlad již proběhne naprosto standardním způsobem:

$ make

Následováno příkazem:

$ sudo make install

Ověřme si, jakou variantu interpretru jsme vlastně přeložili a nainstalovali:

$ python3.14

Ve vypsané hlavičce by se měl objevit i nápis free-threading build, který u „běžného“ interpretru chybí:

Python 3.14.0 free-threading build (main, Oct 18 2025, 10:08:58) [GCC 14.2.1 20240912 (Red Hat 14.2.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

3. První benchmark: klasický neefektivní algoritmus bublinkového řazení

Jako první benchmark pro zjištění výkonnosti interpretru Pythonu 3.14 byl zvolen algoritmus bublinkového řazení, který intenzivně pracuje s operační pamětí a v uvedené implementaci operuje s prvky seznamů. Samozřejmě se jedná o velmi neefektivní algoritmus (což vlastně benchmarky mají být :-). Čistě sekvenční varianta tohoto benchmarku, která žádným způsobem nevyužívá více threadů, vypadá následovně:

from time import perf_counter
import random
 
def bubble_sort(size):
    a = [random.randrange(0, 10000) for i in range(size)]
 
    t1 = perf_counter()
 
    for i in range(size - 1, 0, -1):
        for j in range(0, i):
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
 
    t2 = perf_counter()
 
    print(f"Sorted in {t2-t1} seconds:")
 
 
t1 = perf_counter()
 
for i in range(100):
    bubble_sort(5000)
 
t2 = perf_counter()
 
print(f"Total time: {t2-t1} seconds:")

Jak je z tohoto zdrojového kódu patrné, voláme v tomto benchmarku stokrát funkci, která vytvoří pole (realizované seznamem) se size prvky s náhodnou hodnotou (zde pro jednoduchost náhodné číslo v rozsahu 0..10000). Toto pole je následně seřazeno. Pro úplnost se ještě měří čas seřazení pole (provedeno stokrát pro různá pole) a taktéž celkový čas všech výpočtů.

Paralelní či možná lépe řečeno pseudoparalelní varianta benchmarku je prakticky stejná, ovšem oněch sto volání funkce bubble_sort není provedeno sekvenčně, ale využívá se namísto toho standardní třída nazvaná ThreadPoolExecutor. Hodnotou max_workers můžeme omezit počet úloh spouštěných současně:

from concurrent.futures.thread import ThreadPoolExecutor
from time import perf_counter
import random
 
 
def bubble_sort(size):
    a = [random.randrange(0, 10000) for i in range(size)]
 
    t1 = perf_counter()
 
    for i in range(size - 1, 0, -1):
        for j in range(0, i):
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
 
    t2 = perf_counter()
 
    print(f"Sorted in {t2-t1} seconds:")
 
 
t1 = perf_counter()
 
with ThreadPoolExecutor(max_workers=8) as executor:
    for i in range(100):
        executor.submit(bubble_sort, 5000)
 
t2 = perf_counter()
 
print(f"Total time: {t2-t1} seconds:")

4. Porovnání rychlosti sekvenční varianty benchmarku pro různé verze Pythonu

V následující tabulce jsou zobrazeny výsledky získané pro „sekvenční“ variantu benchmarku, tedy pro tu variantu, která nevyužívá spuštění výpočtů ve více vláknech. Benchmarky byly pochopitelně spuštěny na stejném počítači se stejně nastaveným režimem práce CPU (performance mode – to proto, že aby se CPU nezpomaloval, pokud se zahřeje):

Verze Pythonu Varianta Celkový čas
3.12 GIL 73s
3.13 GIL 74s
3.14 GIL 54s
3.14 noGIL 66s

Povšimněte si, že Python 3.14 je skutečně podstatně rychlejší, než předchozí dvě verze Pythonu (připomeňme, že výchozí konfigurace Pythonu 3.14 stále používá GIL). noGIL varianta Pythonu 3.14 je sice stále rychlejší, než předchozí verze Pythonu (s GILem), ovšem pomalejší, než Python 3.14 s GILem. Z pohledu tohoto benchmarku – sekvenční výpočty – se tedy noGIL verze nevyplatí.

Obrázek 1: Porovnání rychlosti sekvenční varianty benchmarku v grafické podobě.

Obrázek 1: Porovnání rychlosti sekvenční varianty benchmarku v grafické podobě. Zde jasně vyhrává Python 3.14 s GILem.

Autor: tisnik, podle licence: Rights Managed

5. Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s GILem i bez GILu

Zajímavější bude porovnání paralelní varianty benchmarku, protože zde by se měla projevit existence resp. naopak neexistence GILu. Nejprve porovnáme interpretry verze 3.12, 3.13 i 3.14, všechny s GILem:

Verze Pythonu Varianta Celkový čas
3.12 GIL 73s
3.13 GIL 73s
3.14 GIL 54s

Zajímavé je, že časy dokončení benchmarku jsou prakticky stejné jako v případě jeho sekvenční varianty. Jinými slovy nám složitější zápis algoritmu nijak nepomohl, ovšem dobré je, že ani nijak neuškodil.

Obrázek 2: Porovnání rychlosti paralelní varianty benchmarku (interpretry s GIL) v grafické podobě.

Obrázek 2: Porovnání rychlosti paralelní varianty benchmarku (interpretry s GIL) v grafické podobě. Opět vyhrává Python 3.14 s GILem.

Autor: tisnik, podle licence: Rights Managed

Přejděme nyní na Python 3.14 bez GILu. Benchmark budeme spouštět pro různý počet workerů a tím pádem i pro různý počet vláken, které budou moci být provedeny souběžně:

Počet vláken Varianta Celkový čas
4 noGIL 18s
8 noGIL 11s
16 noGIL 9s

Podle očekávání se čas běhu benchmarku postupně snižuje, což je ještě lépe patrné při pohledu na graf (pokud první hodnotu vynásobíme počtem vláken, dostaneme přibližně rychlost pro sekvenční variantu benchmarku):

Obrázek 3: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Obrázek 3: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Autor: tisnik, podle licence: Rights Managed

Pro úplnost se podívejme na celkové časy běhu paralelní verze benchmarku pro počet vláken od 1 do 20:

Workers: 1  total time: 62.31223807000788 seconds
Workers: 2  total time: 33.75166744997841 seconds
Workers: 3  total time: 23.33235937001882 seconds
Workers: 4  total time: 17.860626637993846 seconds
Workers: 5  total time: 15.856282107997686 seconds
Workers: 6  total time: 13.856884196982719 seconds
Workers: 7  total time: 12.629000126005849 seconds
Workers: 8  total time: 11.608698377996916 seconds
Workers: 9  total time: 11.077733537007589 seconds
Workers: 10  total time: 10.40789418600616 seconds
Workers: 11  total time: 10.052095271006692 seconds
Workers: 12  total time: 9.38940597101464 seconds
Workers: 13  total time: 9.442261223011883 seconds
Workers: 14  total time: 9.424980993004283 seconds
Workers: 15  total time: 9.394032383017475 seconds
Workers: 16  total time: 9.34619775399915 seconds
Workers: 17  total time: 9.31484580898541 seconds
Workers: 18  total time: 9.448251099995105 seconds
Workers: 19  total time: 9.392468684003688 seconds

Na mém CPU by teoretické zrychlení mělo dosahovat osminásobku (CPU má osm skutečných jader, které se tváří jako šestnáct jader virtuálních), ovšem v praxi se v tomto případě nedosáhlo ani sedminásobku (i tak ovšem nejsou výsledky úplně špatné):

Obrázek 4: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Obrázek 4: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Autor: tisnik, podle licence: Rights Managed

6. Složitější výpočty s velkým množstvím numerických operací

Větší počet jader mikroprocesoru, které mohou v ideálním případě pracovat paralelně, oceníme především ve chvíli, kdy je nutné vykonat nějaké složitější výpočty, neboť takové úlohy typicky nejsou omezeny rychlostí vstupně-výstupních operací a mnohdy ani neprovádějí velké množství čtení a zápisů do operační paměti. Jednou typicky výpočetně náročnou úlohou (kterou jsme v nepatrně pozměněné podobě již použili při testování nástroje Numba a mypyc), je výpočet nějakého fraktálu v komplexní rovině.

Obrázek 5: Jedna varianta fraktálu vykreslená benchmarkem.

Zkusme si tedy nechat vykreslit několik Juliových množin v nastaveném rozlišení (zde je konkrétně použito 256×256 pixelů) a s využitím většího maximálního počtu iterací (1000). Následující benchmark sekvenčně vypočítá několik desítek obrázků Juliových množin a uloží je na disk:

#!/usr/bin/env python
 
"""Renderer of the classic Julia fractal."""
 
import math
from time import perf_counter
 
from PIL import Image
 
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
 
def julia(cx, cy, zx, zy, maxiter):
    c = complex(cx, cy)
    z = complex(zx, zy)
    for i in range(maxiter):
        if abs(z) > 2:
            return i
        z = z * z + c
    return 0
 
 
def recalc_fractal(filename, palette, xmin, ymin, xmax, ymax, cx, cy, maxiter=1000):
    """Recalculate the whole fractal and render the set into given image."""
    t1 = perf_counter()
    image = Image.new("RGB", (IMAGE_WIDTH, IMAGE_HEIGHT))
 
    width, height = image.size
    stepx = (xmax - xmin) / width
    stepy = (ymax - ymin) / height
 
    y1 = ymin
    for y in range(height):
        x1 = xmin
        for x in range(width):
            i = julia(cx, cy, x1, y1, maxiter)
            i = 3 * i % 256
            color = (palette[i][0], palette[i][1], palette[i][2])
            image.putpixel((x, y), color)
            x1 += stepx
        y1 += stepy
 
    image.save(filename)
    t2 = perf_counter()
    # print("Done", filename, t2-t1)
 
 
def main():
    import palette_mandmap
 
    for angle in range(0, 360, 5):
        rad = math.radians(angle)
        cx = 1.0 * math.cos(rad)
        cy = 1.0 * math.sin(rad)
        filename = f"anim_{angle:03d}.png"
        # print(filename)
 
        recalc_fractal(filename, palette_mandmap.palette, -1.5, -1.5, 1.5, 1.5, cx, cy, 1000)
 
 
if __name__ == "__main__":
    t1 = perf_counter()
    main()
    t2 = perf_counter()
    print(f"Threads: no   Rendering time: {t2-t1} seconds")

Obrázek 6: Další varianta fraktálu vykreslená benchmarkem.

7. Výsledky benchmarku pro různé verze Pythonu

V případě, že benchmark, jehož zdrojový kód byl uvedený v šesté kapitole, spustíme v různých verzích interpretru jazyka Python, zjistíme, že časy výpočtu jsou prakticky totožné. Python 3.14 je sice nepatrně rychlejší, než 3.12 nebo 3.13, ale rozdíly nejsou tak markantní, jako tomu bylo v případě benchmarku s bublinkovým řazením. To znamená, že počítaná smyčka a výpočty s komplexními čísly (tam benchmark stráví nejvíce času) se prakticky nijak neurychlily:

Verze Pythonu Varianta Celkový čas
3.12 GIL 8,7s
3.13 GIL 8,5s
3.14 GIL 8,4s

Prakticky totožné časy výpočtu jsou dobře patrné i z grafické podoby výsledků:

Obrázek 7: Porovnání rychlosti sekvenční varianty benchmarku v grafické podobě.

Obrázek 7: Porovnání rychlosti sekvenční varianty benchmarku v grafické podobě.

Autor: tisnik, podle licence: Rights Managed

8. Paralelní varianta výpočtu: využití proměnného počtu vláken

Výpočet z předchozích dvou kapitol je možné snadno převést do varianty, ve které se výpočet každého rastrového obrázku s Juliovou množinou odehraje v samostatném vláknu. V závislosti na konkrétní variantě interpretru Pythonu (s GILem či bez GILu) potom tato vlákna běží buď souběžně (concurrent) nebo paralelně (parallel). Počet vytvořených vláken lze specifikovat parametrem předávaným do konstruktoru ThreadPoolExecutor podobně, jako tomu bylo u benchmarku pro bublinkové řazení:

#!/usr/bin/env python
 
"""Renderer of the classic Julia fractal."""
 
import math
import sys
from concurrent.futures.thread import ThreadPoolExecutor
from time import perf_counter
 
from PIL import Image
 
IMAGE_WIDTH = 256
IMAGE_HEIGHT = 256
 
 
def julia(cx, cy, zx, zy, maxiter):
    c = complex(cx, cy)
    z = complex(zx, zy)
    for i in range(maxiter):
        if abs(z) > 2:
            return i
        z = z * z + c
    return 0
 
 
def recalc_fractal(filename, palette, xmin, ymin, xmax, ymax, cx, cy, maxiter=1000):
    """Recalculate the whole fractal and render the set into given image."""
    t1 = perf_counter()
    image = Image.new("RGB", (IMAGE_WIDTH, IMAGE_HEIGHT))
 
    width, height = image.size
    stepx = (xmax - xmin) / width
    stepy = (ymax - ymin) / height
 
    y1 = ymin
    for y in range(height):
        x1 = xmin
        for x in range(width):
            i = julia(cx, cy, x1, y1, maxiter)
            i = 3 * i % 256
            color = (palette[i][0], palette[i][1], palette[i][2])
            image.putpixel((x, y), color)
            x1 += stepx
        y1 += stepy
 
    image.save(filename)
    t2 = perf_counter()
    # print("Done", filename, t2-t1)
 
 
def main(threads):
    import palette_mandmap
 
    with ThreadPoolExecutor(max_workers=threads) as executor:
        for angle in range(0, 360, 5):
            rad = math.radians(angle)
            cx = 1.0 * math.cos(rad)
            cy = 1.0 * math.sin(rad)
            filename = f"anim_{angle:03d}.png"
            # print(filename)
 
            executor.submit(recalc_fractal, filename, palette_mandmap.palette, -1.5, -1.5, 1.5, 1.5, cx, cy, 1000)
 
 
if __name__ == "__main__":
    threads = 8
    if len(sys.argv) > 1:
        threads = int(sys.argv[1])
 
    t1 = perf_counter()
    main(threads)
    t2 = perf_counter()
    print(f"Threads: {threads}   Rendering time: {t2-t1} seconds")
Poznámka: povšimněte si, že počet vláken lze nastavit z příkazového řádku.

9. Výsledky benchmarku pro různé verze Pythonu

Opět si ukažme výsledky pro různé verze interpretru programovacího jazyka Python. Použity byly interpretry s GILem (ostatně starší varianty Pythonu běh bez GILu neměly implementován):

Verze Pythonu Varianta Celkový čas
3.12 GIL 7,9s
3.13 GIL 8,3s
3.14 GIL 8,5s

Zajímavé je, že v tomto případě vychází Python 3.14 vlastně nejhůře.

Poznámka: jedná se o průměrné časy získané z několika spuštění benchmarků, protože jsem se snažil v co největší míře eliminovat vliv přepínání mezi P a E jádry.

10. Rozdíly celkové doby výpočtů v Pythonu 3.14: GIL vs noGIL

Nyní se zaměřme pouze na interpret Pythonu 3.14, který – jak již víme – může být přeložen takovým způsobem, že nebude používat GIL. Benchmark spustíme ve verzi s GILem pro počet vláken, který se postupně mění od 1 do 16 (pro úplnost jsou na začátku vypsány i časy „sekvenční“ varianty benchmarku):

Threads: no   Rendering time: 8.26075068900002 seconds
Threads: 1   Rendering time: 8.611623036995297 seconds
Threads: 2   Rendering time: 8.259407158999238 seconds
Threads: 3   Rendering time: 8.441360425989842 seconds
Threads: 4   Rendering time: 8.47209037898574 seconds
Threads: 5   Rendering time: 8.53605182300089 seconds
Threads: 6   Rendering time: 8.031249245000026 seconds
Threads: 7   Rendering time: 8.194169141999964 seconds
Threads: 8   Rendering time: 8.253845249000051 seconds
Threads: 9   Rendering time: 8.439028205 seconds
Threads: 10   Rendering time: 8.730016430999967 seconds
Threads: 11   Rendering time: 8.830254810999918 seconds
Threads: 12   Rendering time: 8.628374089999966 seconds
Threads: 13   Rendering time: 8.723840595999945 seconds
Threads: 14   Rendering time: 8.796107125000049 seconds
Threads: 15   Rendering time: 8.895810613000094 seconds
Threads: 16   Rendering time: 8.817675302999987 seconds
Poznámka: z těchto výsledků je patrné, že počet vláken, do kterých se práce rozdělí, nemá vliv na celkový čas výpočtů, protože prakticky všechna případná urychlení vlivem paralelního kódu „zarazí“ GIL, který vykonávání příkazů serializuje.

Naprosto odlišná však bude situace ve chvíli, kdy použijeme Python 3.14 se zakázaným GILem. Opět si ukažme výsledky pro benchmark rozdělený do jednoho až šestnácti vláken:

Threads: 1   Rendering time: 9.435709450015565 seconds
Threads: 2   Rendering time: 5.053133198001888 seconds
Threads: 3   Rendering time: 3.562595502997283 seconds
Threads: 4   Rendering time: 2.833458012988558 seconds
Threads: 5   Rendering time: 2.4556286619917955 seconds
Threads: 6   Rendering time: 2.1379492330015637 seconds
Threads: 7   Rendering time: 1.9770731370081194 seconds
Threads: 8   Rendering time: 1.872884926997358 seconds
Threads: 9   Rendering time: 1.864864025003044 seconds
Threads: 10   Rendering time: 1.7349536379915662 seconds
Threads: 11   Rendering time: 1.734476473997347 seconds
Threads: 12   Rendering time: 1.9110816899919882 seconds
Threads: 13   Rendering time: 1.775416452990612 seconds
Threads: 14   Rendering time: 1.7973549320013262 seconds
Threads: 15   Rendering time: 1.8662592969776597 seconds
Threads: 16   Rendering time: 1.843843137001386 seconds

Teoreticky by se mělo pro počet vláken přesahujících hodnotu 8 dosáhnout osminásobného urychlení (což je počet reálných jader), ve skutečnosti však urychlení dosáhne jen 5,5 násobku původního výpočtu. To nejsou zcela špatné výsledky, ovšem na druhou stranu by mohly být i lepší a naznačuje to existenci zámků buď přímo v interpretru nebo v použité knihovně:

Obrázek 8: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Obrázek 8: Porovnání rychlosti paralelní varianty benchmarku pro Python 3.14 s vypnutým GILem.

Autor: tisnik, podle licence: Rights Managed

11. Podpora pro větší množství interpretrů v běžícím procesu

V Pythonu 3.14 je k dispozici nová standardní knihovna concurrent.interpreters. Jak již název této knihovny naznačuje, poskytuje možnost konstrukce nových instancí interpretru Pythonu, spuštění kódu v těchto interpretrech a taktéž komunikaci mezi programy, které běží v jednotlivých interpretrech a jsou od sebe izolovány (což znamená, že obecně nesdílí svoji paměť). V názvu této knihovny je i slovo concurrent, protože jednotlivé interpretry běží souběžně (a v některých případech i paralelně, což je silnější podmínka, než souběžnost).

Poznámka: jak uvidíme dále, je knihovna plně funkční, ovšem sami její autoři přiznávají, že celý koncept mnoha běžících interpretrů není po technické stránce dokonalý. Příkladem jsou poměrně velké požadavky na paměť. To se však bude v dalších verzích Pythonu zlepšovat – ostatně tvůrci Pythonu dokázali, že se skutečně ve své práci vyznají a Python se postupně zrychluje.

12. Výpis interpretrů, konstrukce nového interpretru

Práce s interpretry na úrovni zdrojového kódu je relativně snadná a přímočará. Nejprve se podívejme na to, jakým způsobem se vlastně získá seznam všech dostupných interpretrů (ty mohou, ale také nemusí běžet, jsou však již zkonstruovány):

from concurrent import interpreters
 
for interpreter in interpreters.list_all():
    print(interpreter)

Při inicializaci Pythonu 3.14 je zkonstruován jen jediný interpret. Ten je tímto skriptem vypsán (resp. je vypsáno jméno objektu reprezentujícího rozhraní k interpretru):

Interpreter(0)

Nový interpret se zkonstruuje funkcí interpreters.create. I když není v tomto interpretru spuštěn žádný kód, bude i tento interpretr vypsán:

from concurrent import interpreters
 
interp = interpreters.create()
 
for interpreter in interpreters.list_all():
    print(interpreter)

Výsledky:

Interpreter(0)
Interpreter(1)

13. Spuštění kódu v novém interpretru

V nově vytvořeném interpretru je pochopitelně možné spustit nějaký kód. Pro tento účel slouží metody call a exec. Ukažme si použití metody call, která akceptuje identifikátor funkce a její případné parametry. Funkce je spuštěna „izolovaně“ v novém interpretru:

from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    print("Hello from new interpreter")
 
print("Executing run()")
interp.call(run)
 
print("Hello from the original interpreter")

Výsledky:

Executing run()
Hello from new interpreter
Hello from the original interpreter

Přičemž prostřední zpráva je vypsána kódem funkce run běžící v novém interpretru.

Podobně lze použít i exec:

from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    print("Hello from new interpreter")
 
print("Executing run()")
interp.exec(run)
 
print("Hello from the original interpreter")

Metodou exec je možné spustit i externí skript, kdežto metoda call je omezena na zavolání funkcí:

from concurrent import interpreters
 
interp = interpreters.create()
 
print("Executing run()")
interp.exec(open("hello.py").read())
 
print("Hello from the original interpreter")

14. Spuštění kódu v novém vláknu

Ve světě mikroprocesorů s velkým množstvím procesorových jader je velmi užitečné, že interpret může spustit zadanou funkci v novém vláknu. To je ukázáno na dalším demonstračním příkladu, v němž funkci run spustíme nejenom v novém interpretru, ale navíc i v novém vláknu, které může běžet souběžně s původním vláknem:

from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    print("Hello from new interpreter")
 
print("Executing run()")
t = interp.call_in_thread(run)
 
print("Hello from original interpreter")
 
t.join()

V tomto demonstračním příkladu navíc explicitně čekáme na dokončení vlákna, ve kterém byla funkce run spuštěna:

Executing run()
Hello from original interpreter
Hello from new interpreter

15. Sdílí interpretry svůj stav?

V předchozích odstavcích bylo napsáno, že funkce (či jiný kód) běžící v jednotlivých interpretrech je odizolován od kódu z jiných interpretrů. Zda tomu tak je se můžeme pokusit zjistit různými způsoby. Ukažme si ten nejjednodušší z nich. Ve funkci run se pokusíme o modifikaci obsahu globální proměnné:

from concurrent import interpreters
 
interp = interpreters.create()
 
x = []
 
def run():
    x.append("*")
 
print("x=", x)
 
run()
print("x=", x)
 
t = interp.call_in_thread(run)
t.join()
 
print("x=", x)

Spuštění tohoto skriptu povede k běhové chybě:

x= []
x= ['*']
x= []
x= ['*']
Exception in thread Thread-1 (_call):
Exception: ModuleNotFoundError: No module named '<fake __main__>'
 
The above exception was the direct cause of the following exception:
 
concurrent.interpreters.NotShareableError: object could not be unpickled
 
The above exception was the direct cause of the following exception:
 
Traceback (most recent call last):
  File "/usr/local/lib/python3.14t/threading.py", line 1081, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/local/lib/python3.14t/threading.py", line 1023, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14t/concurrent/interpreters/__init__.py", line 215, in _call
    res, excinfo = _interpreters.call(self._id, callable, args, kwargs, restrict=True)
                   ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
concurrent.interpreters.NotShareableError: func not shareable
Poznámka: z vypsaných zpráv je patrné, že interně je situace poněkud komplikovanější. Podrobnosti si řekneme příště při popisu způsobu komunikace mezi interpretry – i to je totiž možné.

To ovšem neznamená, že kód v různých interpretrech nedokáže komunikovat s jiným kódem. Komunikace možná je, ovšem přes fronty, což si taktéž příště ukážeme.

16. Chování programu při spuštění dvou souběžných úloh v různých interpretrech

Ještě jednou se vraťme k problematice spuštění kódu jak v samostatném interpretru, tak i v samostatném vláknu. Budeme zkoumat, jak se takový program bude chovat v čase běhu, tedy v runtime. Nejprve nepatrně upravíme skript, v němž se funkce run spouští „jen“ v novém interpretru, ale pořád ve stejném vlákně. Přidáme volání funkce sleep:

from time import sleep
from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    from time import sleep
    sleep(5)
    print("Hello from new interpreter")
 
print("Executing run()")
interp.exec(run)
 
sleep(5)
print("Hello from the original interpreter")

Podle očekávání se zprávy zobrazí s přibližně pětisekundovými prodlevami:

Executing run()
<5 sekund>
Hello from new interpreter
<5 sekund>
Hello from the original interpreter

Jak se ovšem chování změní tehdy, pokud bude run spuštěna v novém vláknu?

from time import sleep
from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    from time import sleep
    sleep(5)
    print("Hello from new interpreter")
 
print("Executing run()")
t = interp.call_in_thread(run)
 
sleep(5)
print("Hello from original interpreter")
 
t.join()

Druhé dvě zprávy se zobrazí v přibližně stejný okamžik, protože je sice před nimi pětisekundová prodleva, ta však byla vykonána souběžně jak v původním vláknu, tak i ve vláknu novém:

Executing run()
<5 sekund>
Hello from original interpreter
Hello from new interpreter

17. Chování programu v případě, že je v interpretru vyhozena nezachycená výjimka

Zajímavé bude zjistit, jak se bude celý program chovat ve chvíli, kdy je z funkce spuštěné v novém (samostatném) interpretru vyhozena výjimka, která není odchycena. Může se jednat například o chybu při dělení atd., tj. o takový typ výjimky, který se (většinou) přímo neošetřuje. Nejprve spustíme kód v novém interpretru s využitím metody exec, tj. vše poběží v jediném vláknu:

from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    print("Hello from new interpreter")
    0/0
 
print("Executing run()")
interp.exec(run)
 
print("Hello from the original interpreter")

Pokusme se tento skript spustit a sledovat, jaké operace se vykonají:

$ python3.14 interpreter_exception_1.py
 
Executing run()
Hello from new interpreter
Traceback (most recent call last):
  File "/home/ptisnovs/src/most-popular-python-libs/python3.14/interpreters/interpreter_exception_1.py", line 10, in
    interp.exec(run)
    ~~~~~~~~~~~^^^^^
  File "/usr/local/lib/python3.14/concurrent/interpreters/__init__.py", line 212, in exec
    raise ExecutionFailed(excinfo)
concurrent.interpreters.ExecutionFailed: ZeroDivisionError: division by zero
 
Uncaught in the interpreter:
 
Traceback (most recent call last):
  File "/home/ptisnovs/src/most-popular-python-libs/python3.14/interpreters/interpreter_exception_1.py", line 7, in run
    0/0
    ~^~
ZeroDivisionError: division by zero

V tomto případě došlo k „běžnému“ pádu celé aplikace, což znamená, že interpretry jsou sice od sebe izolovány, ale vlastní volání funkce v interpretru může zhavarovat pokud dojde k vyhození výjimky.

Spuštění kódu v novém vláknu (a v novém interpretru) se bude chovat odlišně, i když dojde k vyhození stejné výjimky:

from time import sleep
from concurrent import interpreters
 
interp = interpreters.create()
 
def run():
    print("Hello from new interpreter")
    0/0
 
print("Executing run()")
t = interp.call_in_thread(run)
 
print("Hello from the original interpreter")
 
sleep(5)
 
print("Original interpreter is still alive")
 
t.join()

Nyní je výjimkou ukončen běh kódu v jiném vláknu, ovšem původní interpret stále poběží, což je patrné z hlášení, která jsou zobrazena:

$ python3.14 interpreter_exception_2.py
 
Executing run()
Hello from the original interpreter
Hello from new interpreter
Exception in thread Thread-1 (_call):
Traceback (most recent call last):
  File "/usr/local/lib/python3.14/threading.py", line 1081, in _bootstrap_inner
    self._context.run(self.run)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/local/lib/python3.14/threading.py", line 1023, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.14/concurrent/interpreters/__init__.py", line 217, in _call
    raise ExecutionFailed(excinfo)
concurrent.interpreters.ExecutionFailed: ZeroDivisionError: division by zero

Uncaught in the interpreter:

Traceback (most recent call last):
  File "/home/ptisnovs/src/most-popular-python-libs/python3.14/interpreters/interpreter_exception_2.py", line 8, in run
    0/0
    ~^~
ZeroDivisionError: division by zero
Original interpreter is still alive

18. Odkazy na články s problematikou souběžnosti a paralelnosti v Pythonu

Na stránkách Roota jsme se již několikrát setkali s problematikou souběžnosti, paralelnosti a asynchronního běhu v Pythonu. Různé varianty spouštění a řízení více vláken, procesů a asynchronních úloh naleznete v následujících článcích (všechny v článcích uvedené demonstrační příklady by měly být spustitelné i v interpretru Pythonu 3.14 bez GILu):

  1. Souběžné a paralelně běžící úlohy naprogramované v Pythonu
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu/
  2. Souběžné a paralelně běžící úlohy naprogramované v Pythonu (2)
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu-2/
  3. Souběžné a paralelně běžící úlohy naprogramované v Pythonu – Curio a Trio
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu-curio-a-trio/
  4. Souběžné a paralelně běžící úlohy naprogramované v Pythonu – knihovna Trio
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu-knihovna-trio/
  5. Souběžné a paralelně běžící úlohy naprogramované v Pythonu – knihovna Trio (2)
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu-knihovna-trio-2/
  6. Souběžné a paralelně běžící úlohy naprogramované v Pythonu – závěrečné zhodnocení
    https://www.root.cz/clanky/soubezne-a-paralelne-bezici-ulohy-naprogramovane-v-pythonu-zaverecne-zhodnoceni/
  7. Interpret Pythonu bez GILu: vyplatí se odstranění velkého zámku?
    https://www.root.cz/clanky/interpret-pythonu-bez-gilu-vyplati-se-odstraneni-velkeho-zamku/

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

Všechny dnes popsané demonstrační příklady jsou vypsány v následující tabulce:

# Příklad Stručný popis příkladu Adresa
1 bubble-sort/bubble_sort_seq.py benchmark provádějící sekvenční volání algoritmu pro bublinkové řazení https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/bubble-sort/bubble_sort_seq.py
2 bubble-sort/bubble_sort_parallel.py benchmark provádějící souběžné volání algoritmu pro bublinkové řazení https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/bubble-sort/bubble_sort_parallel.py
       
3 julia-renderer/julia_seq_anim.py sekvenční výpočet animace Juliových množin https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/julia-renderer/julia_seq_anim.py
4 julia-renderer/julia_parallel_anim.py souběžný popř. paralelní výpočet animace Juliových množin https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/julia-renderer/julia_parallel_anim.py
5 julia-renderer/palette_mandmap.py pomocný soubor s barvovou mapou použitý dvěma předchozími benchmarky https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/julia-renderer/palette_mandmap.py
       
6 interpreters/list-interpreters.py výpis seznamu inicializovaných interpretrů https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/list-interpreters.py
7 interpreters/new-interpreter.py konstrukce nového interpretru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/new-interpreter.py
8 interpreters/exec.py zavolání funkce ve zvoleném interpretru (ve stejném vlákně) https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/exec.py
9 interpreters/call_in_thread.py zavolání funkce ve zvoleném interpretru (v novém vlákně) https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/call_in_thread­.py
10 interpreters/shared-variables.py jsou proměnné sdíleny mezi interpretry? https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/shared-variables.py
11 interpreters/behaviour_exec.py chování dvou interpretrů v čase běhu (metoda exec) https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/behaviour_exec­.py
12 interpreters/behaviour_ca­ll_in_thread.py chování dvou interpretrů v čase běhu (metoda call_in_thread) https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/behaviour_call_in_thre­ad.py
13 interpreters/interpreter_ex­ception1.py chování interpretrů při vyhození nezachycené výjimky https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/interpreter_ex­ception1.py
14 interpreters/interpreter_ex­ception2.py chování interpretrů při vyhození nezachycené výjimky https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/in­terpreters/interpreter_ex­ception2.py

Demonstrační příklady vytvořené pro Python verze 3.14 a popsané v minulé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 Adresa
1 argparse_test.py skript s definicí přepínačů použitelných na příkazovém řádku https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/ar­gparse_test.py
       
2 syntax_error1.py skript obsahující syntaktické chyby: chybějící či naopak přebývající písmeno v klíčovém slovu nebo identifikátoru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/syn­tax_error1.py
2 syntax_error2.py skript obsahující syntaktické chyby: chybějící či naopak přebývající písmeno v klíčovém slovu nebo identifikátoru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/syn­tax_error2.py
3 syntax_error3.py skript obsahující syntaktické chyby: chybějící či naopak přebývající písmeno v klíčovém slovu nebo identifikátoru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/syn­tax_error3.py
4 syntax_error4.py skript obsahující syntaktické chyby: chybějící či naopak přebývající písmeno v klíčovém slovu nebo identifikátoru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/syn­tax_error4.py
5 syntax_error5.py skript obsahující syntaktické chyby: chybějící či naopak přebývající písmeno v klíčovém slovu nebo identifikátoru https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/syn­tax_error5.py
       
6 primes.py realizace výpočtu prvočísel https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/primes.py
7 test_primes.py jednotkové testy pro modul primes.py https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/tes­t_primes.py
       
8 pep-758-motivation-1.py zachycení většího množství výjimek v bloku except – motivační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-758-motivation-1.py
9 pep-758-motivation-2.py zachycení většího množství výjimek v bloku except – motivační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-758-motivation-2.py
10 pep-758-usage.py nový způsob zachycení výjimek definovaný v PEP-758 https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-758-usage.py
11 pep-758-usage-as.py klauzule as a nový způsob zachycení výjimek definovaný v PEP-758 https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-758-usage-as.py
       
12 pep-765-motivation-1.py detekce opuštění bloku finally, první demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-765-motivation-1.py
13 pep-765-motivation-2.py detekce opuštění bloku finally, druhý demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-765-motivation-2.py
14 pep-765-motivation-3.py detekce opuštění bloku finally, třetí demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-765-motivation-3.py
15 pep-765-motivation-4.py detekce opuštění bloku finally, čtvrtý demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/pep-765-motivation-4.py
       
16 f-string-1.py rozdíl mezi f-řetězci a t-řetězci, první demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/f-string-1.py
17 t-string-1.py rozdíl mezi f-řetězci a t-řetězci, první demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/t-string-1.py
18 f-string-2.py rozdíl mezi f-řetězci a t-řetězci, druhý demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/f-string-2.py
19 t-string-2.py rozdíl mezi f-řetězci a t-řetězci, druhý demonstrační příklad https://github.com/tisnik/most-popular-python-libs/blob/master/python3.14/t-string-2.py

20. Odkazy na Internetu

  1. Python 3.14.0
    https://test.python.org/dow­nloads/release/python-3140/
  2. PEP 765 – Disallow return/break/continue that exit a finally block
    https://peps.python.org/pep-0765/
  3. PEP 758 – Allow except and except* expressions without parentheses
    https://peps.python.org/pep-0758/
  4. What’s new in Python 3.14 (official)
    https://docs.python.org/3/what­snew/3.14.html
  5. What’s New In Python 3.13 (official)
    https://docs.python.org/3/what­snew/3.13.html
  6. What’s New In Python 3.12 (official)
    https://docs.python.org/3/what­snew/3.12.html
  7. What’s New In Python 3.11 (official)
    https://docs.python.org/3/what­snew/3.11.html
  8. What’s New In Python 3.12
    https://dev.to/mahiuddindev/python-312–4n43
  9. PEP 698 – Override Decorator for Static Typing
    https://peps.python.org/pep-0698/
  10. PEP 484 – Type Hints
    https://www.python.org/dev/peps/pep-0484/
  11. What’s New In Python 3.5
    https://docs.python.org/3­.5/whatsnew/3.5.html
  12. 26.1. typing — Support for type hints
    https://docs.python.org/3­.5/library/typing.html#mo­dule-typing
  13. Type Hints – Guido van Rossum – PyCon 2015 (youtube)
    https://www.youtube.com/wat­ch?v=2wDvzy6Hgxg
  14. Python 3.5 is on its way
    https://lwn.net/Articles/650904/
  15. Type hints
    https://lwn.net/Articles/640359/
  16. Stránka projektu PDM
    https://pdm.fming.dev/latest/
  17. PDF na GitHubu
    https://github.com/pdm-project/pdm
  18. PEP 582 – Python local packages directory
    https://peps.python.org/pep-0582/
  19. PDM na PyPi
    https://pypi.org/project/pdm/
  20. Which Python package manager should you use?
    https://towardsdatascience.com/which-python-package-manager-should-you-use-d0fd0789a250
  21. How to Use PDM to Manage Python Dependencies without a Virtual Environment
    https://www.youtube.com/wat­ch?v=qOIWNSTYfcc
  22. What are the best Python package managers?
    https://www.slant.co/topics/2666/~best-python-package-managers
  23. PEP 621 – Storing project metadata in pyproject.toml
    https://peps.python.org/pep-0621/
  24. Pick a Python Lockfile and Improve Security
    https://blog.phylum.io/pick-a-python-lockfile-and-improve-security/
  25. PyPA specifications
    https://packaging.python.or­g/en/latest/specification­s/
  26. Creation of virtual environments
    https://docs.python.org/3/li­brary/venv.html
  27. How to Use virtualenv in Python
    https://learnpython.com/blog/how-to-use-virtualenv-python/
  28. Python Virtual Environments: A Primer
    https://realpython.com/python-virtual-environments-a-primer/
  29. virtualenv Cheatsheet
    https://aaronlelevier.git­hub.io/virtualenv-cheatsheet/
  30. Installing Python Modules
    https://docs.python.org/3/in­stalling/index.html
  31. Python: The Documentary | An origin story
    https://www.youtube.com/wat­ch?v=GfH4QL4VqJ0
  32. History of Python
    https://en.wikipedia.org/wi­ki/History_of_Python
  33. History of Python
    https://www.geeksforgeeks­.org/python/history-of-python/
  34. 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/
  35. 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/
  36. PEP 684 – A Per-Interpreter GIL
    https://peps.python.org/pep-0684/
  37. What Is the Python Global Interpreter Lock (GIL)?
    https://realpython.com/python-gil/
  38. PEP 703 – Making the Global Interpreter Lock Optional in CPython
    https://peps.python.org/pep-0703/
  39. GlobalInterpreterLock
    https://wiki.python.org/mo­in/GlobalInterpreterLock
  40. What is the Python Global Interpreter Lock (GIL)
    https://www.geeksforgeeks.org/what-is-the-python-global-interpreter-lock-gil/
  41. Let's remove the Global Interpreter Lock
    https://www.pypy.org/posts/2017/08/lets-remove-global-interpreter-lock-748023554216649595.html
  42. Global interpreter lock
    https://en.wikipedia.org/wi­ki/Global_interpreter_lock
  43. 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/
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.