Obsah
2. Skript s konstrukcemi async a await
3. Překlad konstrukcí async a await do dalších jazyků
4. Základní konstrukce s pattern matchingem v Pythonu
5. Překlad pattern matchingu do dalších jazyků
6. Programové bloky try-except v Pythonu
7. Překlad bloku try-except do dalších jazyků
8. Podpora objektově orientovaného programování v Pythonu
9. Definice třídy s několika metodami v Pythonu
10. Překlad definice třídy do dalších programovacích jazyků
11. Definice třídy se speciálními metodami
12. Překlad třídy se speciálními metodami do dalších jazyků
13. Definice třídy, vytvoření instancí třídy, nepřímé volání speciálních metod
14. Překlad třídy i s její instanciací a voláním speciálních metod
15. Definice třídy s doplněním všech typových informací
16. Překlad třídy s doplněnými typovými informacemi
17. Doplněná tabulka se srovnáním úspěchu transpřekladu jednotlivých příkladů
19. Repositář s demonstračními příklady
1. Projekt py2many: dokončení
Jak již bylo napsáno v perexu dnešního článku, navážeme na úvodní článek o do jisté míry univerzálním transpileru py2many, který dokáže (či by alespoň měl dokázat) překládat programy psané v Pythonu do dalších jazyků, a to včetně C++, Rustu, Go či jazyka Julia. Zatímco minule jsme si ukázali způsoby překladu základních programových konstrukcí, dnes se zaměříme na ukázky překladu jazykových konstrukcí pro asynchronní programování (async a await), překladu bloků pro zachycení výjimek (try a except, resp. catch), ale i na použití pattern matchingu. Nezapomeneme ani na další důležitou součást jazyka Python – na podporu objektově orientovaného programování založeného na využití tříd.
2. Skript s konstrukcemi async a await
U některých typů aplikací se setkáme s tím, že je nutné volat určité části kódu pro několik požadavků současně či alespoň souběžně. Využití vláken je v Pythonu obecně (stále) problematické kvůli GILu, ovšem alternativní řešení těchto problémů může spočívat v tom, že nějakou vhodnou technologií umožníme asynchronní vykonávání kódu v těch okamžicích, v nichž jsou jiné části kódu pozastaveny, protože čekají na dokončení nějakých vstupně-výstupních operací. Naprosto typickým příkladem jsou některé virtuální stroje JavaScriptu, které povětšinou umožňují běh aplikace pouze v jediném vláknu, ale i v takových případech je pro některé typy operací umožněno zajistit souběh několika operací. Jak do JavaScriptu, tak i později do Pythonu byla přidána nová klíčová slova async a await, která asynchronní operace umožní naprogramovat.
Bude tedy užitečné zjistit, jak a zda vůbec se tyto konstrukce přeloží do dalších programovacích jazyků. Použijeme přitom následující skript, který ukazuje způsob realizace asynchronně běžící úlohy:
async_await.py
async def task(): print("task started") await asyncio.sleep(5) print("task finished") def main(): task1 = asyncio.create_task(task()) print("task created") await task1 print("done")
3. Překlad konstrukcí async a await do dalších jazyků
Podívejme se na způsob překladu výše uvedeného skriptu:
async_await.cpp
FAILED
async_await.go
package main import ( "fmt") #[async] func task() { fmt.Printf("%v\n","task started"); await!(asyncio.sleep(5)); fmt.Printf("%v\n","task finished");} func Main() { task1 := asyncio.create_task(task()) fmt.Printf("%v\n","task created"); await!(task1); fmt.Printf("%v\n","done");}
async_await.jl
#[async] function task() println(join(["task started"], " ")); await!(sleep(asyncio, 5)); println(join(["task finished"], " ")); end function main() task1 = create_task(asyncio, task()) println(join(["task created"], " ")); await!(task1); println(join(["done"], " ")); end
async_await.rs
//! ```cargo //! [package] //! edition = "2018" //! [dependencies] //! //! ``` #![allow(clippy::collapsible_else_if)] #![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 #![allow(clippy::map_identity)] #![allow(clippy::needless_return)] #![allow(clippy::print_literal)] #![allow(clippy::ptr_arg)] #![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 #![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_parens)] pub async fn task() { println!("{}", "task started"); asyncio.sleep(5).await; println!("{}", "task finished"); } pub fn main() { let task1 = asyncio.create_task(task()); println!("{}", "task created"); task1.await; println!("{}", "done"); }
4. Základní konstrukce s pattern matchingem v Pythonu
Podívejme se na další (stále možná poněkud specifickou) vlastnost programovacího jazyka Python. Tento jazyk totiž podporuje (relativně jednoduchou) formu pattern matchingu. V té naprosto nejjednodušší podobě může pattern matching nahradit sérii podmínek if-elseif-elseif…. A právě tuto formu pattern matchingu si otestujeme na „školním“ příkladu pro výpočet faktoriálu:
pattern_matching_factorial.py
def factorial(n): match n: case 0: return 1 case 1: return 1 case x: return x * factorial(x-1)
5. Překlad pattern matchingu do dalších jazyků
A jak dopadne překlad tohoto skriptu do dalších programovacích jazyků? Nyní to bude rychlá kontrola:
pattern_matching_factorial.cpp
FAILED
pattern_matching_factorial.go
FAILED
pattern_matching_factorial.jl
FAILED
pattern_matching_factorial.rs
FAILED
Mimochodem – problém spočívá v tom, že pattern matching není (alespoň prozatím) nástrojem py2many podporován, takže příslušné uzly v AST stromu nejsou rozpoznány. Toto chování je patrné z chybových hlášení vypisovaných při pokusu o (trans)překlad:
pattern_matching_factorial.py ... pattern_matching_factorial.cpp pattern_matching_factorial.py:1:0: py2many.exceptions.AstNotImplementedError: sequence item 0: expected str instance, NoneType found pattern_matching_factorial.py ... pattern_matching_factorial.rs pattern_matching_factorial.py:1:0: py2many.exceptions.AstNotImplementedError: sequence item 0: expected str instance, NoneType found Error: ['rustfmt', '--edition=2018', 'pattern_matching_factorial.rs'] (code: 1): b'error: expected one of `!` or `::`, found `<eof>`\n --> /home/ptisnovs/xy/pattern_matching_factorial.rs:1:1\n |\n1 | FAILED\n | ^^^^^^ expected one of `!` or `::`\n\n'b'' pattern_matching_factorial.py ... pattern_matching_factorial.jl pattern_matching_factorial.py:1:0: py2many.exceptions.AstNotImplementedError: sequence item 0: expected str instance, NoneType found Error: Could not format: pattern_matching_factorial.jl Due to: FileNotFoundError [Errno 2] No such file or directory: 'format.jl' pattern_matching_factorial.py ... pattern_matching_factorial.go pattern_matching_factorial.py:1:0: py2many.exceptions.AstNotImplementedError: sequence item 0: expected str instance, NoneType found Error: ['gofmt', '-w', 'pattern_matching_factorial.go'] (code: 2): b"pattern_matching_factorial.go:1:1: expected 'package', found FAILED\n"b''
6. Programové bloky try-except v Pythonu
Další programovou konstrukcí, kterou v programovacím jazyku Python nalezneme a která se velmi často používá, je podpora pro zachycení výjimek v bloku try-except. V moderním Pythonu může být tento blok poměrně komplikovaný (zachycení skupiny výjimek atd.), ovšem prozatím si ukažme jeho zcela nejprimitivnější podobu, v níž zachytíme pokus o dělení nulou a namísto chyby vrátíme zvolenou konstantu (taktéž nulu). Funkce s tímto blokem bude plně otypována:
safe_div.py
def safe_div(x: int, y: int) -> int: try: return x//y except Exception: return 0
7. Překlad bloku try-except do dalších jazyků
Jak si s blokem try-except poradil transpiler py2many je zřejmé z okomentovaných výsledků transpřekladu:
safe_div.cpp
#include <iostream> // NOLINT(build/include_order) inline int safe_div(int x, int y) { try { return x / y; } catch (const std::exception &e) { ExceptHandler /*unimplemented()*/ } catch (const std::overflow_error &e) { std::cout << "OVERFLOW ERROR" << std::endl; } catch (const std::runtime_error &e) { std::cout << "RUNTIME ERROR" << std::endl; } catch (...) { std::cout << "UNKNOWN ERROR" << std::endl; 0 } }
safe_div.go
FAILED
safe_div.jl
function safe_div(x::Int64, y::Int64)::Int64 try return x / y catch exn if exn isa Exception return 0 end end end
safe_div.rs
FAILED
8. Podpora objektově orientovaného programování v Pythonu
Programovací jazyk Python patří do skupiny jazyků podporujících objektově orientované programování (OOP) založené na třídách. To znamená, že základní vlastnosti objektu (zde konkrétně dostupné metody a popř. třídní atributy) jsou definovány ve třídě a objekt je vytvořen zavoláním konstruktoru příslušné třídy. A i některé další programovací jazyky, zejména C++, takto pojaté objektově orientované programování podporují.
Ovšem na druhou stranu další jazyky, které nás v kontextu dnešního článku zajímají, naopak „třídní OOP“ nemají. Sem spadá jazyk Go (má podporu pro rozhraní a metody, nikoli pro třídy), Julia (zaměřeno spíše na multiple dispatching) i Rust (podporuje traity, což je sémanticky poněkud odlišná technologie). Proto bude zajímavé zjistit, jakým způsobem se vlastně přeloží, popř. naopak nepřeloží kód naprogramovaný v Pythonu, který obsahuje deklaraci třídy s několika metodami i konstruktorem (poněkud nepřesně zde považuji speciální metodu __init__ za konstruktor), třídy se speciálními metodami atd.
9. Definice třídy s několika metodami v Pythonu
Nejprve se podívejme na velmi jednoduchou třídu, která obsahuje definici konstruktoru a dvou běžných metod nazvaných on_visit a on_leave. Jedná se o nestatické a netřídní metody:
visitor.py
class Visitor(): def __init__(self): self.nest_level = 0 def on_visit(self, text): indent = " " * self.nest_level * 2 print(indent, text) self.nest_level += 1 def on_leave(self, node): self.nest_level -= 1
10. Překlad definice třídy do dalších programovacích jazyků
visitor.cpp
#include <iostream> // NOLINT(build/include_order) class Visitor { public: int nest_level; inline void __init__() { int this->nest_level = 0; } template <typename T0> void on_visit(T0 text) { std::string indent = (std::string{" "} * (this->nest_level)) * 2; std::cout << indent; std::cout << " "; std::cout << text; std::cout << std::endl; this->nest_level += 1; } template <typename T0> void on_leave(T0 node) { this->nest_level -= 1; } };
visitor.go
FAILED
visitor.jl
struct Visitor nest_level::Int64 end function __init__(self::Visitor) self.nest_level = 0 end function on_visit{T0}(self::Visitor, text::T0) indent = " "*self.nest_level*2 println(join([indent, text], " ")); self.nest_level += 1 end function on_leave{T0}(self::Visitor, node::T0) self.nest_level -= 1 end
visitor.rs
//! ```cargo //! [package] //! edition = "2018" //! [dependencies] //! //! ``` #![allow(clippy::collapsible_else_if)] #![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 #![allow(clippy::map_identity)] #![allow(clippy::needless_return)] #![allow(clippy::print_literal)] #![allow(clippy::ptr_arg)] #![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 #![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_parens)] pub struct Visitor { pub nest_level: i32, } impl Visitor { pub fn __init__(&self) { self.nest_level = 0; } pub fn on_visit<T0>(&self, text: T0) { pub const indent: &str = ((" " * self.nest_level) * 2); println!("{} {}", indent, text); self.nest_level += 1; } pub fn on_leave<T0>(&self, node: T0) { self.nest_level -= 1; } }
11. Definice třídy se speciálními metodami
I v dalším demonstračním příkladu bude použita definice třídy v Pythonu. Tentokrát však bude třída kromě konstruktoru, což je vlastně speciálně pojmenovaná metoda, obsahovat i další dvě metody se speciálním významem. První z těchto metod se jmenuje __add__ a slouží pro definici chování binárního operátoru +. Druhá speciální metoda se jmenuje __str__ a volá se ve chvíli, kdy je nutné objekt reprezentovat formou řetězce. Podívejme se tedy na zdrojový kód takové třídy:
add_method1.py
class Foo: def __init__(self, value): self._value = value def __add__(self, other): return Foo(self._value + other._value) def __str__(self): return "*" * self._value
12. Překlad třídy se speciálními metodami do dalších jazyků
Při (trans)překladu výše uvedeného zdrojového kódu můžeme předpokládat, že realizace v programovacím jazyce C++ by měla být (minimálně) možná, protože C++ podporuje třídní OOP. V dalších jazycích se ovšem bude pravděpodobně postupovat stylem „best effort“ a výsledek nebude přeložitelný a někdy ani sémanticky totožný.
add_method1.cpp
class Foo { public: auto _value; template <typename T0> void __init__(T0 value) { auto this->_value = value; } template <typename T0> Foo __add__(T0 other) { return Foo((this->_value) + (other._value)); } inline std::string __str__() { return std::string{"*"} * (this->_value); } };
add_method1.go
package main type Foo struct { _value ST0 } func __init__[T0 any](self Foo, value T0 any) { self._value = value} func __add__[T0 any](self Foo, other T0 any) Foo { return Foo{_value: (self._value + other._value)}} func __str__(self Foo) string { return ("*"*self._value)}
add_method1.jl
struct Foo _value:: end function __init__{T0}(self::Foo, value::T0) self._value = value end function __add__{T0}(self::Foo, other::T0)::Foo return Foo(self._value + other._value) end function __str__(self::Foo)::String return "*"*self._value end
add_method1.rs
//! ```cargo //! [package] //! edition = "2018" //! [dependencies] //! //! ``` #![allow(clippy::collapsible_else_if)] #![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 #![allow(clippy::map_identity)] #![allow(clippy::needless_return)] #![allow(clippy::print_literal)] #![allow(clippy::ptr_arg)] #![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 #![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_parens)] pub struct Foo { pub _value: _, } impl Foo { pub fn __init__<T0>(&self, value: T0) { self._value = value; } pub fn __add__<T0>(&self, other: T0) -> Foo { return Foo { _value: (self._value + other._value), }; } pub fn __str__(&self) -> &'static str { return ("*" * self._value) as &'static str; } }
13. Definice třídy, vytvoření instancí třídy, nepřímé volání speciálních metod
Nyní si vyzkoušejme zdrojový kód s definicí třídy Foo upravit takovým způsobem, že do něj přidáme pomocnou funkci, ve které se vytvoří dvě instance této třídy, tyto instance se sečtou (zavolá se tedy speciální metoda __add__) a výsledek se vypíše ve formě řetězce (zavolá se tedy speciální metoda __str__). V samotném Pythonu lze tento algoritmus zapsat přímočaře:
add_method2.py
class Foo: def __init__(self, value): self._value = value def __add__(self, other): return Foo(self._value + other._value) def __str__(self): return "*" * self._value def test_adding(): f1 = Foo(1) f2 = Foo(2) print(f1 + f2)
14. Překlad třídy i s její instanciací a voláním speciálních metod
add_method2.cpp
#include <iostream> // NOLINT(build/include_order) class Foo { public: auto _value; template <typename T0> void __init__(T0 value) { auto this->_value = value; } template <typename T0> Foo __add__(T0 other) { return Foo((this->_value) + (other._value)); } inline std::string __str__() { return std::string{"*"} * (this->_value); } }; inline void test_adding() { Foo f1 = Foo(1); Foo f2 = Foo(2); std::cout << f1 + f2; std::cout << std::endl; }
add_method2.go
package main import ( "fmt") type Foo struct { _value ST0 } func __init__[T0 any](self Foo, value T0 any) { self._value = value} func __add__[T0 any](self Foo, other T0 any) Foo { return Foo{_value: (self._value + other._value)}} func __str__(self Foo) string { return ("*"*self._value)} func TestAdding() { var f1 Foo = Foo{_value: 1} var f2 Foo = Foo{_value: 2} fmt.Printf("%v\n",(f1 + f2));}
add_method2.jl
struct Foo _value:: end function __init__{T0}(self::Foo, value::T0) self._value = value end function __add__{T0}(self::Foo, other::T0)::Foo return Foo(self._value + other._value) end function __str__(self::Foo)::String return "*"*self._value end function test_adding() f1 = Foo(1) f2 = Foo(2) println(join([f1 + f2], " ")); end
add_method2.rs
//! ```cargo //! [package] //! edition = "2018" //! [dependencies] //! //! ``` #![allow(clippy::collapsible_else_if)] #![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 #![allow(clippy::map_identity)] #![allow(clippy::needless_return)] #![allow(clippy::print_literal)] #![allow(clippy::ptr_arg)] #![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 #![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_parens)] pub struct Foo { pub _value: _, } impl Foo { pub fn __init__<T0>(&self, value: T0) { self._value = value; } pub fn __add__<T0>(&self, other: T0) -> Foo { return Foo { _value: (self._value + other._value), }; } pub fn __str__(&self) -> &'static str { return ("*" * self._value) as &'static str; } } pub fn test_adding() { let f1: Foo = Foo { _value: 1 }; let f2: Foo = Foo { _value: 2 }; println!("{}", (f1 + f2)); }
15. Definice třídy s doplněním všech typových informací
V posledním demonstračním příkladu, který si v dnešním článku ukážeme, rozšíříme deklaraci naší třídy o typové informace. To by mělo pomoci především při překladu do jazyků se statickým typovým systémem (stále jsou však použity speciální metody atd.):
add_method3.py
class Foo: def __init__(self, value: int): self._value = value def __add__(self, other: Foo) -> Foo: return Foo(self._value + other._value) def __str__(self) -> str: return "*" * self._value
16. Překlad třídy s doplněnými typovými informacemi
add_method3.cpp
class Foo { public: int _value; inline void __init__(int value) { int this->_value = value; } inline Foo __add__(Foo other) { return Foo((this->_value) + (other._value)); } inline std::string __str__() { return std::string{"*"} * (this->_value); } };
add_method3.go
package main type Foo struct { _value int } func __init__(self Foo, value int) { self._value = value } func __add__(self Foo, other Foo) Foo { return Foo{_value: (self._value + other._value)} } func __str__(self Foo) string { return ("*" * self._value) }
add_method3.jl
struct Foo _value::Int64 end function __init__(self::Foo, value::Int64) self._value = value end function __add__(self::Foo, other::Foo)::Foo return Foo(self._value + other._value) end function __str__(self::Foo)::String return "*"*self._value end
add_method3.rs
//! ```cargo //! [package] //! edition = "2018" //! [dependencies] //! //! ``` #![allow(clippy::collapsible_else_if)] #![allow(clippy::double_parens)] // https://github.com/adsharma/py2many/issues/17 #![allow(clippy::map_identity)] #![allow(clippy::needless_return)] #![allow(clippy::print_literal)] #![allow(clippy::ptr_arg)] #![allow(clippy::redundant_static_lifetimes)] // https://github.com/adsharma/py2many/issues/266 #![allow(clippy::unnecessary_cast)] #![allow(clippy::upper_case_acronyms)] #![allow(clippy::useless_vec)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(unused_imports)] #![allow(unused_mut)] #![allow(unused_parens)] pub struct Foo { pub _value: i32, } impl Foo { pub fn __init__(&self, value: i32) { self._value = value; } pub fn __add__(&self, other: Foo) -> Foo { return Foo { _value: (self._value + other._value), }; } pub fn __str__(&self) -> &str { return ("*" * self._value); } }
17. Doplněná tabulka se srovnáním úspěchu transpřekladu jednotlivých příkladů
Již minule jsme mohli vidět, že některé výsledky transpilace jsou nevalné. Dnes si tabulku doplníme o informace o tom, jak dobře či špatně jsou transpřekládány další programové konstrukce Pythonu. Hodnoty × a ઙ mají zřejmý význam, hodnota 1/2 pak představuje řešení, které sice není přímo přeložitelné a spustitelné, ale není zcela špatné – malým ručním zásahem je možné transpilované zdrojové kódy opravit:
Program | Testuje se | C++ | Go | Julia | Rust |
---|---|---|---|---|---|
hello.py | jednotlivý příkaz mimo funkci | × | × | ✓ | × |
hello_func2.py | definice samostatné funkce | ✓ | ✓ | ✓ | ✓ |
hello_func.py | definice funkce s jejím voláním | × | × | ✓ | × |
factorial.py | definice funkce bez typových informací | × | × | × | × |
ackermann_untyped.py | Ackermannova funkce definovaná bez datových typů | ✓ | × | 1/2 | ? |
ackermann_typed.py | Ackermannova funkce definovaná s datovými typy | ✓ | ✓ | 1/2 | ✓ |
bubble.py | realizace algoritmu bublinkového řazení | × | × | 1/2 | 1/2 |
list_untyped.py | konstrukce a naplnění seznamu; bez typů | 1/2 | × | 1/2 | 1/2 |
list_typed.py | konstrukce a naplnění seznamu; s typy | 1/2 | 1/2 | 1/2 | 1/2 |
async_await.py | konstrukce async a await | × | × | × | 1/2 |
pattern_matching_factorial.py | základní pattern matching | × | × | × | × |
safe_div.py | funkce s blokem try-except | 1/2 | × | × | × |
visitor.py | třída s metodami | 1/2 | × | 1/2 | 1/2 |
add_method1.py | třída se speciálními metodami | 1/2 | 1/2 | 1/2 | 1/2 |
add_method2.py | třída se speciálními metodami | 1/2 | 1/2 | 1/2 | 1/2 |
add_method3.py | třída s doplněnými typovými informacemi | 1/2 | 1/2 | 1/2 | 1/2 |
18. Závěrečné zhodnocení
Ani zdaleka jsme si neukázali všechny jazykové konstrukce Pythonu ani způsob jejich (trans)překladu do dalších jazyků podporovaných nástrojem py2many, ovšem už ze základních ukázek je patrné, že se tento nástroj v současnosti nehodí na transpilaci celých projektů. Výsledné kódy v některých případech poměrně dobře odpovídají Pythonu, jindy však py2many dosti halucinuje (to je nyní moderní slovo, kterým se mávnutím ruky tak trošku zpochybňují nedostatky AI) a některé jazykové konstrukce nejsou přeloženy vůbec, i když by to mělo být možné. Navíc pro Python pravděpodobně neexistuje sofistikovanější nástroj z této skupiny, takže je nutné si buď vystačit s py2many nebo transpilaci přímo přes AST/CST neprovádět. Navíc je při transpřekladu prakticky nutné používat typové anotace (hinty), které mají i další výhody, zejména pro rozsáhlejší projekty.
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python a pro nástroj py2many byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs.
20. Odkazy na Internetu
- py2many na GitHubu
https://github.com/py2many/py2many - py2many na PyPi
https://pypi.org/project/py2many/ - Awesome Transpilers
https://github.com/milahu/awesome-transpilers - Pseudocode (Wikipedia)
https://en.wikipedia.org/wiki/Pseudocode - Pseudokód (Wikipedia)
https://cs.wikipedia.org/wiki/Pseudok%C3%B3d - Cython (home page)
http://cython.org/ - Cython (wiki)
https://github.com/cython/cython/wiki - Cython (Wikipedia)
https://en.wikipedia.org/wiki/Cython - Cython (GitHub)
https://github.com/cython/cython - Seriál Programovací jazyk Julia
https://www.root.cz/serialy/programovaci-jazyk-julia/ - Seriál programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Ackermann function
https://en.wikipedia.org/wiki/Ackermann_function - EmbeddingCython
https://github.com/cython/cython/wiki/EmbeddingCython - The Basics of Cython
http://docs.cython.org/en/latest/src/tutorial/cython_tutorial.html - Compiling to WebAssembly: It’s Happening!
https://hacks.mozilla.org/2015/12/compiling-to-webassembly-its-happening/ - WebAssembly
https://webassembly.org/ - A quick guide about Python implementations
https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321