Hlavní navigace

Projekt py2many: dokončení

20. 6. 2024
Doba čtení: 28 minut

Sdílet

 Autor: Root.cz s využitím DALL-E
Na článek o „univerzálním“ transpileru py2many dnes navážeme a dokončíme popis možností a schopností tohoto nástroje. Ukážeme si způsob překladu konstrukcí async a await, try a except, použití pattern matchingu i překlad kódů s definicí tříd.

Obsah

1. Projekt py2many: dokončení

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ů

18. Závěrečné zhodnocení

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

20. Odkazy na Internetu

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
Poznámka: k tomuto výsledku není co dodat :)

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");}
 
 
Poznámka: popravdě je to poprvé, co vidím znak ! použitý v identifikátoru jazyka Go. Výsledek je podle očekávání nepřeložitelný.

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
 
Poznámka: ve skutečnosti se používá makro @async pro vytvoření asynchronní úlohy. A podle očekávání není await! definovaný symbol.

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");
}
Poznámka: minimálně snaha je zde patrná – task je asynchronní (v Rustu 2015) funkcí; na druhou stranu await v této podobě nelze použít. To je škoda, protože řešení by zde mělo být možné navrhnout.

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
Poznámka: bez komentáře.

pattern_matching_factorial.go

FAILED
Poznámka: bez komentáře.

pattern_matching_factorial.jl

FAILED
Poznámka: bez komentáře.

pattern_matching_factorial.rs

FAILED
Poznámka: bez komentáře.

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''
Poznámka: a to je škoda, protože minimálně v případě Rustu by se mohlo jednat o pěknou a užitečnou vlastnost.

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
  }
}
Poznámka: zajímavé je, že bloky catch nejsou uvedeny ve správném pořadí, takže první blok bude použit pro zachycení všech výjimek. Ovšem v dalších ohledech řešení odpovídá původnímu kódu.

safe_div.go

FAILED
Poznámka: bez komentáře :-)

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
 
Poznámka: zde se ve skutečnosti nezachytí korektní výjimka, protože se vyhodí InexactError(NaN) a nikoli dělení nulou. Příklad je tedy sémanticky nekorektní.

safe_div.rs

FAILED
Poznámka: bez komentáře :-)

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
Poznámka: mimochodem – tato třída o jisté míry implementuje návrhový vzor visitor.

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; }
};
Poznámka: poněkud mě překvapilo použití šablon, ale to je způsobeno tím, že v původní třídě není deklarován typ parametru metody on_visit. Tento kód může sloužit jako základ pro další úpravy.

visitor.go

FAILED
Poznámka: bez komentáře :-)

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
 
Poznámka: platí podobné výtky jako v případě jazyka C++ – kvůli chybějícím typovým informacím není výsledek spustitelný a vyžaduje další explicitní úpravy. O problematice přetížení operátoru * se ještě zmíníme v dalších kapitolách.

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;
    }
}
Poznámka: nebyl pochopen význam operátoru *, takže si překladač Rustu správně stěžuje na kombinaci řetězců s čísly. Taktéž Rust nedovoluje zápis do atributu nest_level, protože parametr self není předáván s modifikátorem mut.

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
Poznámka: povšimněte si použití operátoru * pro opakování řetězce ve speciální metodě __str__. I tento prvek Pythonu je pro transpiler problematický.

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); }
};
Poznámka: až na několik nepatrných problémů se vlastně jedná o dobré řešení.

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)}
 
 
 
Poznámka: zde transpřekladač v první řadě nepochopil význam operátoru „násobení řetězce“. Ovšem opět se jedná o poměrně dobře vytvořenou šablonu, kterou je však nutné ručně doladit, aby byla přeložitelná a spustitelná.

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
 
Poznámka: třídní OOP není v programovacím jazyce Julia použito, metody jsou nahrazeny funkcemi. Opět zde došlo k nepochopení významu operátoru „násobení řetězců“ a významu speciálních metod.

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;
    }
}
Poznámka: je nutné doplnit datové typy, vyřešit problém s operátorem + (operator+) i * atd., ovšem jako základní kostra řešení je tento kód poměrně dobře použitelný.

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;
}
Poznámka: po transpřekladu do programovacího jazyka C++ je patrné, že se ztratil význam speciálních metod, takže se sice posléze vyhodnocuje výraz f1 + f2, ovšem to pochopitelně není platné. Taktéž se (což nás ovšem již nepřekvapí) ztratil význam operátoru *.

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));}
 
 
Poznámka: nepřeložitelné (podle očekávání) a současně i sémanticky nevyhovující. Zdrojové kódy s třídami evidentně nejsou nástrojem py2many ani jazykem Go příliš podporovány.

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
 
Poznámka: opět zde vidíme relativně dobře použitou šablonu, ovšem příklad vyžaduje další specifické ruční úpravy.

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));
}
Poznámka: základní kostra programu je v pořádku a pravděpodobně by vše bylo ještě lepší, kdybychom použili typové anotace (což si vyzkoušíme v rámci dalšího textu). Bez typových anotací je totiž výsledný zdrojový kód nepřeložitelný (nekompatibilní operator+ atd.).

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); }
};
Poznámka: příklad sice obsahuje části, které budou vyžadovat ruční úpravy, ale nyní se již jedná o poměrně dobře čitelný a upravovatelný kód.

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)
}
Poznámka: až na nerozepsání funkce operátoru * pro opakování řetězce je tento zdrojový kód v pořádku. Typové informace zde velmi pomohly.

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
 
Poznámka: opět prakticky bezproblémové řešení, samozřejmě až na to, že zde není použit OOP přístup založený na třídách.

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);
    }
}
Poznámka: jediný problém spočívá (opět) v nerozeznání funkce operátoru *, ovšem jinak se jedná o korektní kód.

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:

CS24 tip temata

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.

# Příklad Stručný popis Adresa
1 hello.py jednotlivý příkaz volaný mimo funkci nebo metodu https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello.py
2 hello.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello.cpp
3 hello.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello.go
4 hello.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello.jl
5 hello.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello.rs
       
6 hello_func2.py definice samostatné funkce bez jejího volání https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func2.py
7 hello_func2.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func2.cpp
8 hello_func2.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func2.go
9 hello_func2.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func2.jl
10 hello_func2.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func2.rs
       
11 hello_func.py definice funkce s jejím voláním https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello_func.py
12 hello_func.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/he­llo_func.cpp
13 hello_func.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello_func.go
14 hello_func.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello_func.jl
15 hello_func.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/hello_func.rs
       
16 factorial.py definice složitější funkce bez typových informací https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/factorial.py
17 factorial.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/factorial.cpp
18 factorial.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/factorial.go
19 factorial.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/factorial.jl
20 factorial.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/factorial.rs
       
21 ackermann_untyped.py Ackermannova funkce definovaná bez datových typů (hintů) https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_untyped.py
22 ackermann_untyped.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_untyped.cpp
23 ackermann_untyped.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_untyped.go
24 ackermann_untyped.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_untyped.jl
25 ackermann_untyped.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_untyped.rs
       
26 ackermann_typed.py Ackermannova funkce definovaná s datovými typy (hinty) https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_typed.py
27 ackermann_typed.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_typed.cpp
28 ackermann_typed.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_typed.go
29 ackermann_typed.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_typed.jl
30 ackermann_typed.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ac­kermann_typed.rs
       
31 bubble.py algoritmus bublinkového řazení https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/bubble.py
32 bubble.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/bubble.cpp
33 bubble.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/bubble.go
34 bubble.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/bubble.jl
35 bubble.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/bubble.rs
       
36 list_untyped.py práce se seznamy, bez typových informací (hintů) https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_untyped.py
37 list_untyped.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_untyped.cpp
38 list_untyped.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_untyped.go
39 list_untyped.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_untyped.jl
40 list_untyped.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_untyped.rs
       
41 list_typed.py práce se seznamy, s typovými informacemi (hinty) https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/list_typed.py
42 list_typed.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/lis­t_typed.cpp
43 list_typed.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/list_typed.go
44 list_typed.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/list_typed.jl
45 list_typed.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/list_typed.rs
       
46 async_await.py použití jazykových konstrukcí async a await https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/a­sync_await.py
47 async_await.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/a­sync_await.cpp
48 async_await.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/a­sync_await.go
49 async_await.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/a­sync_await.jl
50 async_await.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/a­sync_await.rs
       
51 safe_div.py realizace funkce pro podíl dvou hodnot s detekcí dělení nulou https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/safe_div.py
52 safe_div.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/safe_div.cpp
53 safe_div.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/safe_div.go
54 safe_div.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/safe_div.jl
55 safe_div.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/safe_div.rs
       
56 visitor.py jednoduchá třída s několika metodami https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/visitor.py
57 visitor.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/visitor.cpp
58 visitor.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/visitor.go
59 visitor.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/visitor.jl
60 visitor.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/visitor.rs
       
61 pattern_matching_factorial.py pattern matching použitý při výpočtu faktoriálu https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/pat­tern_matching_factorial.py
62 pattern_matching_factorial.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/pat­tern_matching_factorial.cpp
63 pattern_matching_factorial.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/pat­tern_matching_factorial.go
64 pattern_matching_factorial.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/pat­tern_matching_factorial.jl
65 pattern_matching_factorial.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/pat­tern_matching_factorial.rs
       
66 add_method1.py třída se speciálními metodami https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method1.py
67 add_method1.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method1.cpp
68 add_method1.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method1.go
69 add_method1.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method1.jl
70 add_method1.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method1.rs
       
71 add_method2.py třída se speciálními metodami, instanciace třídy atd. https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method2.py
72 add_method2.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method2.cpp
73 add_method2.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method2.go
74 add_method2.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method2.jl
75 add_method2.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method2.rs
       
76 add_method3.py třída s typovými informacemi https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method3.py
77 add_method3.cpp výsledek transpřekladu do jazyka C++ https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method3.cpp
78 add_method3.go výsledek transpřekladu do jazyka Go https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method3.go
79 add_method3.jl výsledek transpřekladu do jazyka Julia https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method3.jl
80 add_method3.rs výsledek transpřekladu do jazyka Rust https://github.com/tisnik/most-popular-python-libs/blob/master/py2many/ad­d_method3.rs

20. Odkazy na Internetu

  1. py2many na GitHubu
    https://github.com/py2many/py2many
  2. py2many na PyPi
    https://pypi.org/project/py2many/
  3. Awesome Transpilers
    https://github.com/milahu/awesome-transpilers
  4. Pseudocode (Wikipedia)
    https://en.wikipedia.org/wi­ki/Pseudocode
  5. Pseudokód (Wikipedia)
    https://cs.wikipedia.org/wi­ki/Pseudok%C3%B3d
  6. Cython (home page)
    http://cython.org/
  7. Cython (wiki)
    https://github.com/cython/cython/wiki
  8. Cython (Wikipedia)
    https://en.wikipedia.org/wiki/Cython
  9. Cython (GitHub)
    https://github.com/cython/cython
  10. Seriál Programovací jazyk Julia
    https://www.root.cz/seria­ly/programovaci-jazyk-julia/
  11. Seriál programovací jazyk Rust
    https://www.root.cz/seria­ly/programovaci-jazyk-rust/
  12. Ackermann function
    https://en.wikipedia.org/wi­ki/Ackermann_function
  13. EmbeddingCython
    https://github.com/cython/cyt­hon/wiki/EmbeddingCython
  14. The Basics of Cython
    http://docs.cython.org/en/la­test/src/tutorial/cython_tu­torial.html
  15. Compiling to WebAssembly: It’s Happening!
    https://hacks.mozilla.org/2015/12/com­piling-to-webassembly-its-happening/
  16. WebAssembly
    https://webassembly.org/
  17. A quick guide about Python implementations
    https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321
ikonka

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.