[Python] It's 1999 all over again

Daniele Varrazzo piro a develer.com
Ven 14 Feb 2014 00:34:35 CET


On 2014-02-13 18:50, Dario Bertini wrote:
> On 02/13/2014 05:03 PM, Daniele Varrazzo wrote:
>> - sai che a[n] non è un carattere ma è un byte. La bugia dei 
>> widechar
>> non regge. Neanche quella di unicode in python che però si rompe al 
>> di
>> fuori del BMP (a meno che non lo compili 4 byte per carattere blah 
>> blah)
>
> Forse sono pignolo, ma "la bugia dei widechar non regge" non vuol 
> dire
> quasi nulla visto che:
> - non specifichi cos'è un widechar (è un codeunit a 16 bit, o un
> codepoint memorizzato in 32?)
> - non chiarisci in che modo non regge

wchar_t è compiler dependent: potrebbe essere anche 8 bit. Tanto per 
essere utile.

Se fosse 16 bit, comunque ti servono 2 unità per esprimere un carattere 
fuori BMP (surrogate pairs)

Se fosse 32 bit, comunque ci sono i caratteri combinanti. Non ne esci.

Cosa non regge è l'idea che se hai widechar, non mi interessa quanto 
wide, comunque né accesso casuale né lunghezza siano operazioni 
genericamente utili. Anche in utf32:

     In [3]: s = u'\u0075\u0301'

     In [4]: len(s)
     Out[4]: 2

     In [5]: print s
     ú


> Python da questo punto di vista non ha nessun problema, con Py3.3
> l'astrazione unicode non mi sembra sia leaky e comunque mi risulta 
> che
> tutte le distro linux fornissero da diversi anni solo le wide build 
> di
> python di default

Non lo sapevo. Che spreco. Nessuno usa i caratteri fuori dal BMP <grin> 
Non so come verificarlo ma sembra sia così:

>>> sys.getsizeof(u'aa') - sys.getsizeof(u'a')
4


> insomma: di default le cose funzionano bene da anni... anche se sono
> d'accordo che il fardello cognitivo del ricordarsi di "fallire
> graziosamente" sulle narrow build fosse un deal breaker
>
>> - tipicamente l'i/o non richiede encoding/decoding
>
> Questo vuol dire che se i dati che leggi non sono codificati
> correttamente te ne accorgi proprio nel mezzo dell'elaborazione

Anche in python. Tutto funziona finché non arriva un accento e le cose 
si rompono. Magari si rompono in I/O, mentre tutto quello che occorreva 
era leggere una stringa da un database e scriverla su una pagina web: 
per un programma scritto negli ultimi 10 anni ci sono buone chance che 
entrambi siano utf8 e la codifica/decodifica non era necessaria: un 
accento sarebbe arrivato indenne a destinazione, anche se Python non 
l'avrebbe visto come codepoint singolo.

Il problema sono i dati legacy. Go credo non ne voglia avere a che 
fare, e ne approfitta per fare pulizia. Se scrivi un programma oggi da 
zero sarà bene che faccia tutto con unicode/utf8. Il problema delle 
codifiche 8 bit è un problema che hanno i linguaggi colla, e Go ha 
deciso di semplificare su questo punto e guardare al presente/futuro 
invece che al passato. Anche ora, con tutte le interfacce che gestisco 
(database, web, email, file...) non uso praticamente nessun encoding che 
non sia utf8. Se proprio c'è un input latin1 ok, ci sarà un decoder 
sull'interfaccia che lo converte: non cambia rispetto a Python.


> penso sarebbe stato meglio fare di len("whatever") un compile error 
> in
> Go, e fornire una funzione size() allo scopo... size si presta di 
> meno
> ad essere fraintesa come "lunghezza di un testo"

se uno non sa che cosa sta facendo, a livello di usare una stringa 
senza sapere se sono codepoint o una codifica, non vedo la necessità di 
venirgli incontro. Meglio faccia un altro lavoro. Altrimenti rischia di 
centrare male la stringa in cinese nello schermo.

Se per questo vorrei una macchina del tempo per dare una martellata 
sulle dita di chi ha implementato str.encode() e unicode.decode(), 
perpretando sempre di più l'idea che str e unicode siano 
intercambiabili. Ho bestemmiato i miei santi migliori dietro alle 
librerie che chiamano x.decode() qualunque cosa sia x, e siccome va bene 
agli americani va bene a tutti. O meglio magari funziona nella shell ma 
si rompe in crontab perchè una variabile d'ambiente è diversa [1]. Dare 
ad unicode l'interfaccia non di una lista ma di un iterabile avrebbe 
fatto notare che len(unicode) non è un'operazione poi così utile - e se 
proprio ti serve fai len(list(u)). Invece che ha fatto in python3? Ha 
azzoppato bytes rimuovendo l'operatore %, quindi tutto *deve* essere 
[de]codificato. A questo punto ecco il bastone, lì ci sono i cuccioli di 
foca... Ok fine rant :)


>> È un linguaggio opinionato
>
> Anche Python è un linguaggio opinionato: solo perchè a te non 
> piacciono
> i bytes literals (così mi sembra), non vuol dire che "paghi sempre
> l'overhead necessario" :P

Perché non mi dovrebbero piacere i byte literal? :)

E sì, paghi overhead in codifica (input), elaborazione (4 volte la 
memoria e quindi il tempo per processarla) e decodifica (output) anche 
dove non sarebbe stato necessario: è un fatto che non vedo come si possa 
negare. Usare unicode ovunque mi va benissimo: è giusto ed è il presente 
e il futuro. È la scelta di rappresentarlo internamente come array di 
codepoint che crea delle strozzature. Go non ha queste chicane, il che 
lo rende più efficiente sull'I/O di qualunque magia possa fare Python. 
Penso gli dia più questo che il fatto di essere compilato.


-- Daniele


[1] ok, questo è un problema diverso, ma deriva dal fatto che in python 
tutti gli encoding hanno la stessa importanza, che è una panzana 
colossale visto che solo uno gestisce il dominio completo, mentre gli 
altri esplodono su diversi sottoinsiemi.

     piro a bagheera:~$ python -c 'print u"\u20ac"'
     €
     piro a bagheera:~$ LC_CTYPE=C python -c 'print u"\u20ac"'
     Traceback (most recent call last):
       File "<string>", line 1, in <module>
     UnicodeEncodeError: 'ascii' codec can't encode character u'\u20ac' 
in position 0: ordinal not in range(128)

Cosa vorrei da un linguaggio moderno? Che se chiedo di scrivere 
dell'unicode in un file i dati, grazie, per favore, convertili 
nell'unico encoding che può gestire tutto il dominio unicode, no, non 
voglio fare una partita alla roulette degli encoding. Se in quel file 
strano e curioso voglio scriverci in LATIN15 ti passerò dei byte 
codificati da me. Sì esistono terminali che non gestiscono utf8. Sì, 
sono io il coglione se ti chiedo di stamparci sopra €, non è colpa tua: 
un risultato indeterminato va bene. Ma ti prego, non crashare alle 3 di 
mattina per un print.



Maggiori informazioni sulla lista Python