Vai al contenuto
Leo

Leo vs Python

Stesso compito, due linguaggi

Undici programmi concreti, scritti in Leo e in Python uno accanto all'altro. Per ciascuno diciamo dove Leo è più comodo e, senza giri di parole, dove Python resta la scelta migliore. Ogni esempio Leo qui sotto è stato eseguito davvero: l'uscita che vedi è quella vera.

Leo gira sopra Python, quindi non è una gara fra motori: è lo stesso motore con un volante italiano. La domanda vera non è «chi è più potente», ma «con quale dei due questo lavoro viene più semplice, più chiaro e più sicuro».

Italiano nelle parole e negli errori Zero installazioni: tutto incluso Numeri esatti su denaro e misure Onestà: dove Python vince lo scriviamo

Caso 1 di 11

Ciao, mondo

Il primo programma: una riga di saluto a schermo.

Python
ciao.py
print("Ciao, mondo!")
Leo
ciao.leo
stampa("Ciao, mondo!")
Ciao, mondo!

Dove Leo è più comodo

La parola stampa è italiana e il programma si avvia con python3 -m leo ciao.leo: Python e tutta la libreria sono già dentro Leo, non si installa nulla.

Dove Python resta avanti

print è conosciuto da chiunque e gira ovunque ci sia Python, senza passare da Leo. L'avvio di Python “nudo” è anche più rapido di qualche millisecondo.

Caso 2 di 11

Una classe con ereditarietà

Definire un tipo di oggetto, con campi, metodi e una sottoclasse che specializza il genitore.

Python
veicoli.py
class Veicolo:
    def __init__(self, nome, ruote=4):
        self.nome = nome
        self.ruote = ruote

    def descrivi(self):
        return f"{self.nome}: {self.ruote} ruote"


class Moto(Veicolo):
    def __init__(self, nome):
        super().__init__(nome, ruote=2)

    def descrivi(self):
        return super().descrivi() + " (una moto)"


print(Veicolo("Panda").descrivi())
print(Moto("Vespa").descrivi())
Leo
veicoli.leo
classe Veicolo
    ha nome
    ha ruote = 4
    azione descrivi() dà testo
        restituisci "{questo.nome}: {questo.ruote} ruote"
    fine
fine

classe Moto estende Veicolo
    ha ruote = 2
    azione descrivi() dà testo
        restituisci genitore.descrivi() + " (una moto)"
    fine
fine

variabile v = Veicolo("Panda")
variabile m = Moto("Vespa")
stampa(v.descrivi())
stampa(m.descrivi())
Panda: 4 ruote
Vespa: 2 ruote (una moto)

Dove Leo è più comodo

I campi si dichiarano con ha, i metodi con azione, questo è l'oggetto corrente e genitore richiama il metodo del genitore. Il costruttore è automatico: Veicolo("Panda") basta e avanza, niente __init__ né self da ripetere.

Dove Python resta avanti

La programmazione a oggetti di Python è più profonda: self esplicito, metodi speciali (__len__, __eq__…), proprietà, classi astratte, ereditarietà multipla e decoratori. Per librerie grandi quella ricchezza conta.

Caso 3 di 11

Fette di testi e liste

Prendere una porzione di una stringa o di una lista con un solo taglio.

Python
fette.py
parola = "primavera"
print(parola[0:5])      # primi 5 caratteri
print(parola[-3:])      # ultimi 3

numeri = [10, 20, 30, 40, 50, 60]
print(numeri[1:4])
print(numeri[3:6])
Leo
fette.leo
variabile parola = "primavera"
stampa(parola[0..4])
stampa(parola[-3..-1])

variabile numeri = [10, 20, 30, 40, 50, 60]
stampa(numeri[1..3])
stampa(numeri[3..5])
prima
era
[20, 30, 40]
[40, 50, 60]

Dove Leo è più comodo

Leo usa l'intervallo da..a con estremi inclusi, come quando diciamo “dal 1º al 5”: parola[0..4] sono i caratteri da 0 a 4, tutti e cinque. Funzionano anche gli indici negativi.

Dove Python resta avanti

Lo slicing di Python ([inizio:fine:passo]) ha il passo: lista[::-1] inverte, lista[::2] prende uno sì e uno no. Leo oggi non ha il passo; per invertire usa inverti.

Caso 4 di 11

Denaro esatto

Sommare dei prezzi e applicare uno sconto senza gli errori dei decimali.

Python
prezzi.py
# Con i float normali i centesimi non tornano:
print(0.1 + 0.2)        # 0.30000000000000004

from decimal import Decimal
carrello = [Decimal("4.99"), Decimal("0.90"), Decimal("12.50")]
totale = sum(carrello)
print(f"Totale: {totale} EUR")
print(f"Con sconto 20%: {totale * Decimal('0.8')}")
Leo
prezzi.leo
stampa("Con i decimali normali: {0.1 + 0.2}")

variabile carrello = [soldi("4.99"), soldi("0.90"), soldi("12.50")]
variabile totale = soldi("0.00")
per ogni prezzo in carrello fai
    totale = totale.piu(prezzo)
fine
stampa("Totale carrello: {totale}")
stampa("Con lo sconto del 20%: {totale.per(0.8)}")
Con i decimali normali: 0.30000000000000004
Totale carrello: 18,39 €
Con lo sconto del 20%: 14,71 €

Dove Leo è più comodo

soldi(...) è nel linguaggio di serie, senza import: calcola al centesimo esatto e stampa all'italiana (virgola e simbolo €). In Python per evitare lo 0.30000000000000004 devi ricordarti di from decimal import Decimal.

Dove Python resta avanti

Il modulo decimal di Python è uno standard maturo, con arrotondamenti configurabili, contesti e precisione arbitraria usati in ambito bancario da anni. La base su cui Leo poggia è proprio quella.

Caso 5 di 11

Errori leggibili

Gestire un dato sbagliato e mostrare un messaggio comprensibile.

Python
eta.py
def eta_da_testo(testo):
    return int(testo)

try:
    eta = eta_da_testo("trenta")
    print(f"Hai {eta} anni")
except ValueError as e:
    print("Non è un numero valido.")
    print(f"Python dice: {e}")
Leo
eta.leo
funzione eta_da_testo(testo)
    restituisci intero(testo)
fine

inizio
    prova
        variabile eta = eta_da_testo("trenta")
        stampa("Hai {eta} anni")
    cattura ErroreValore come e
        stampa("Non è un numero valido.")
        stampa("Leo dice: {e.messaggio}")
        stampa("Alla riga {e.riga}")
    fine
fine
Non è un numero valido.
Leo dice: Non posso trasformare il testo "trenta" in un intero.
Alla riga 2

Dove Leo è più comodo

Il messaggio è in italiano e discorsivo, e l'errore porta con sé .messaggio, .riga e .colonna: puoi leggerli dal programma. Anche a terminale Leo mostra la riga col segnalino ^^^ e spesso un suggerimento.

Dove Python resta avanti

Il messaggio di Python (invalid literal for int() with base 10: 'trenta') è in inglese, ma la gerarchia delle eccezioni è ampia e documentatissima, e il traceback completo con tutte le chiamate è uno strumento di debug potente.

Caso 6 di 11

Leggere un CSV

Aprire un file CSV della spesa e calcolare il totale.

Python
spesa.py
import csv

with open("spesa.csv", encoding="utf-8") as f:
    righe = list(csv.DictReader(f))

totale = 0
for r in righe:
    costo = float(r["prezzo"]) * float(r["quantita"])
    print(f"{r['prodotto']}: {costo:.2f} EUR")
    totale += costo
print(f"Spesa totale: {totale:.2f} EUR")
Leo
spesa.leo
inizio
    variabile righe = leggi_tabella("spesa.csv")
    variabile totale = 0
    per ogni r in righe fai
        variabile costo = decimale(r["prezzo"]) * decimale(r["quantita"])
        stampa("{r["prodotto"]}: {costo:migliaia 2} €")
        totale += costo
    fine
    stampa("Spesa totale: {totale:migliaia 2} €")
fine
Mele: 20,00 €
Pane: 3,60 €
Latte: 8,70 €
Spesa totale: 32,30 €

Dove Leo è più comodo

leggi_tabella apre il file, riconosce le intestazioni e restituisce righe già pronte come dizionari, in una riga sola. L'accesso ai file passa dai permessi di Leo, quindi di serie è più sicuro.

Dove Python resta avanti

Il modulo csv di Python gestisce dialetti, separatori strani, virgolette e file enormi in streaming; con pandas l'analisi di tabelle grandi è di un altro livello. Leo copre i casi comuni, non quelli estremi.

Caso 7 di 11

Leggere un JSON

Interpretare una risposta JSON, per esempio quella di un servizio web.

Python
meteo.py
import json

risposta = '{"citta": "Bologna", "gradi": 19, "venti": ["N", "NE"]}'
meteo = json.loads(risposta)
venti = ", ".join(meteo["venti"])
print(f"A {meteo['citta']}: {meteo['gradi']} gradi, venti da {venti}")
Leo
meteo.leo
inizio
    variabile risposta = "\{\"citta\": \"Bologna\", \"gradi\": 19, \"venti\": [\"N\", \"NE\"]}"
    variabile meteo = da_json(risposta)
    stampa("A {meteo["citta"]}: {meteo["gradi"]} gradi, venti da {", ".unisci(meteo["venti"])}")
fine
A Bologna: 19 gradi, venti da N, NE

Dove Leo è più comodo

da_json è globale, senza import, e i dati diventano dizionari e liste come in Python. Se il testo non è valido, l'errore dice dove (riga e colonna) e che cosa manca, in italiano.

Dove Python resta avanti

Il modulo json è nella libreria standard di Python e scrivere una stringa JSON a mano è più pulito: Leo interpola ogni testo tra { }, così le graffe del JSON vanno protette con \{. Da un file o da una risposta web il problema non si pone.

Caso 8 di 11

Un piccolo server web

Esporre due rotte JSON su un server locale e interrogarle.

Python
servizio.py
from http.server import BaseHTTPRequestHandler, HTTPServer
import json, threading, urllib.request

PAGINE = {
    "/api/saluto": {"messaggio": "Ciao da Python!"},
    "/api/ora": {"servizio": "orologio", "attivo": True},
}

class Gestore(BaseHTTPRequestHandler):
    def do_GET(self):
        corpo = json.dumps(PAGINE.get(self.path, {})).encode()
        self.send_response(200 if self.path in PAGINE else 404)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(corpo)
    def log_message(self, *a): pass

server = HTTPServer(("127.0.0.1", 0), Gestore)
threading.Thread(target=server.serve_forever, daemon=True).start()
porta = server.server_address[1]
with urllib.request.urlopen(f"http://127.0.0.1:{porta}/api/saluto") as r:
    print(json.load(r)["messaggio"])
server.shutdown()
Leo
servizio.leo
inizio
    variabile servizio = server_web(0, pagine = {
        "/api/saluto": {"messaggio": "Ciao da Leo!"},
        "/api/ora": {"servizio": "orologio", "attivo": vero}
    })
    servizio.avvia()
    stampa("Server avviato, solo su questo computer (127.0.0.1).")

    variabile r = scarica_json(servizio.indirizzo + "api/saluto")
    stampa("La rotta /api/saluto risponde: {r["messaggio"]}")

    servizio.ferma()
    stampa("Server spento.")
fine
Server avviato, solo su questo computer (127.0.0.1).
La rotta /api/saluto risponde: Ciao da Leo!
Server spento.

Dove Leo è più comodo

server_web trasforma un dizionario di rotte in un servizio JSON: niente classi gestore, header o thread da scrivere a mano. server_web(0, ...) sceglie una porta libera e ascolta solo in locale.

Dove Python resta avanti

Per il web vero Python ha un ecosistema insuperabile: Flask, Django e FastAPI, con rotte, template, autenticazione, database e un server async capace di reggere molte connessioni insieme. server_web di Leo è pensato per demo e strumenti, non per siti in produzione.

Caso 9 di 11

Parallelismo

Avviare un calcolo lungo senza bloccare il resto del programma.

Python
parallelo.py
from concurrent.futures import ThreadPoolExecutor

def somma_fino(n):
    totale = 0
    for i in range(1, n + 1):
        totale += i
    return totale

with ThreadPoolExecutor() as pool:
    lavoro = pool.submit(somma_fino, 1_000_000)
    print("Il calcolo è partito; intanto vado avanti...")
    print(f"Risultato: {lavoro.result():,}")
Leo
parallelo.leo
funzione somma_fino(n)
    variabile totale = 0
    per ogni i in 1..n fai
        totale += i
    fine
    restituisci totale
fine

inizio
    variabile lavoro = in parallelo fai
        restituisci somma_fino(1_000_000)
    fine
    stampa("Il calcolo è partito; intanto vado avanti...")
    stampa("Risultato: {aspetta(lavoro):migliaia}")
fine
Il calcolo è partito; intanto vado avanti...
Risultato: 500.000.500.000

Dove Leo è più comodo

in parallelo ... fine è un costrutto del linguaggio: avvia il blocco di fianco e con aspetta ne raccogli il risultato. Leo controlla anche che il lavoro non modifichi di nascosto le variabili di fuori.

Dove Python resta avanti

Python ha il parallelismo più maturo e vario: threading, concurrent.futures, multiprocessing per usare davvero tutti i core, e asyncio per decine di migliaia di operazioni di rete insieme. Leo non ha ancora async/await.

Caso 10 di 11

Una password sicura

Conservare una password nel modo giusto: mai in chiaro, solo l'impronta.

Python
accesso.py
import hashlib, os

def impronta(password):
    sale = os.urandom(16)
    chiave = hashlib.scrypt(password.encode(), salt=sale,
                            n=16384, r=8, p=1, dklen=32)
    return sale.hex() + ":" + chiave.hex()

def verifica(password, salvata):
    sale_hex, chiave_hex = salvata.split(":")
    sale = bytes.fromhex(sale_hex)
    chiave = hashlib.scrypt(password.encode(), salt=sale,
                            n=16384, r=8, p=1, dklen=32)
    return chiave.hex() == chiave_hex

scelta = "Cavallo-Batteria-Pinzatrice-42"
salvata = impronta(scelta)
print("Login con password giusta:", verifica(scelta, salvata))
print("Login con password sbagliata:", verifica("123456", salvata))
Leo
accesso.leo
inizio
    variabile scelta = "Cavallo-Batteria-Pinzatrice-42"
    variabile forza = misura_forza_password(scelta)
    stampa("Robustezza: {forza["livello"]} (circa {forza["bit"]} bit)")

    variabile salvata = impronta_password(scelta)
    stampa("Salvo solo l'impronta scrypt, irreversibile ({lunghezza(salvata)} caratteri).")
    stampa("Login con password giusta: {verifica_password(scelta, salvata)}")
    stampa("Login con password sbagliata: {verifica_password("123456", salvata)}")
fine
Robustezza: ottima (circa 184.1 bit)
Salvo solo l'impronta scrypt, irreversibile (83 caratteri).
Login con password giusta: vero
Login con password sbagliata: falso

Dove Leo è più comodo

impronta_password e verifica_password usano scrypt con sale casuale e confronto a tempo costante: la cosa giusta, in due righe e senza poterla sbagliare. In più misura_forza_password stima la robustezza senza mandare nulla in rete.

Dove Python resta avanti

In Python gli stessi mattoni ci sono (hashlib.scrypt), ma vanno montati a mano; in cambio hai la scelta fra tanti algoritmi provati (bcrypt, argon2) e librerie di sicurezza mantenute da esperti e aggiornate di continuo.

Caso 11 di 11

Una data e una durata

Calcolare quanto tempo passa fra due momenti e dire che giorno cade.

Python
scadenze.py
from datetime import datetime

avvio = datetime(2026, 3, 2, 9, 0)
consegna = datetime(2026, 3, 20, 17, 30)
giorni = (consegna - avvio).days
giorno = ["lunedì", "martedì", "mercoledì", "giovedì",
          "venerdì", "sabato", "domenica"][consegna.weekday()]
print(f"Dal {avvio:%d/%m/%Y} al {consegna:%d/%m/%Y}")
print(f"Giorni di calendario: {giorni}")
print(f"La consegna cade di {giorno}")
Leo
scadenze.leo
inizio
    variabile avvio = data(2026, 3, 2, ore = 9)
    variabile consegna = data(2026, 3, 20, ore = 17, minuti = 30)
    stampa("Dal {formatta_data(avvio, "medio")} al {formatta_data(consegna, "medio")}")
    stampa("Giorni di calendario: {giorni_tra(avvio, consegna)}")
    stampa("Durata esatta: {differenza_tra(avvio, consegna)}")
    stampa("La consegna cade di {consegna.nome_giorno}")
fine
Dal 2 mar 2026 al 20 mar 2026
Giorni di calendario: 18
Durata esatta: 18 giorni, 8 ore e 30 minuti
La consegna cade di venerdì

Dove Leo è più comodo

giorni_tra, differenza_tra e nome_giorno parlano italiano: la durata esce già scritta in parole (“18 giorni, 8 ore e 30 minuti”) e i nomi dei giorni sono in italiano, senza tabelle da costruire né codici di formato da ricordare.

Dove Python resta avanti

Il modulo datetime di Python, con zoneinfo per i fusi orari e librerie come dateutil, copre fusi, calendari e parsing di date in qualunque formato con una precisione che a Leo oggi manca.

In sintesi

Quando scegliere Leo, quando Python

Nessuno dei due “vince” sempre: dipende da che cosa stai facendo. Ecco la regola pratica.

Scegli Leo se…

  • Vuoi programmare (o insegnare) in italiano, senza la barriera dell'inglese.
  • Ti servono numeri esatti su denaro e misure senza pensarci.
  • Vuoi errori chiari che dicono dove e perché, con un suggerimento.
  • Fai app con finestre, giochi 2D, automazioni, strumenti interni.
  • Vuoi un .exe per Windows o un'app Android con un comando.
  • Vuoi tutto incluso: niente pip install, niente ambienti da configurare.

Scegli Python se…

  • Hai bisogno della massima velocità su calcoli pesanti e numerici.
  • Ti serve l'enorme ecosistema di pacchetti (PyPI): IA, dati, scienza, web.
  • Costruisci servizi web in produzione o molta rete con async/await.
  • Lavori in team dove Python è lo standard e la comunità è sterminata.
  • Ti servono librerie di nicchia mantenute da esperti del settore.
  • Cerchi un linguaggio con vent'anni di documentazione e corsi in ogni lingua.

Leo gira sopra Python e ne eredita la solidità: i concetti sono gli stessi, chi conosce Python legge Leo al primo sguardo e chi impara con Leo passa a Python senza ricominciare da capo.

Il modo migliore per decidere è provarlo

Installa Leo Studio o apri l'app sul telefono, incolla uno di questi esempi e premi Esegui. In trenta secondi hai la tua risposta.

stampa("Ciao, mondo!")