[Python] 0 in (False,) // 0 == False

Enrico Franchi enrico.franchi a gmail.com
Mer 10 Feb 2010 22:48:36 CET


On Feb 6, 2010, at 11:46 PM, Pietro Battiston wrote:

> Il punto che forse non è chiaro è cosa sia l'aritmetica.

Sarebbe da capire a chi non è chiaro cosa sia. Sono abbastanza sicuro
di non essere io quello con dubbi o lacune. Qui si sta parlando di aritmetica
mista fra interi e booleani. Che a te da fastidio, a me e a Guido no.

Quello che io ho scritto è semplicemente che *se* vuoi aritmetica che 
coinvolga anche i booleani
di fatto la cosa più naturale è che False sia equivalente a 0 e True ad 1,
al di la del fatto che bool is-a int per l'implementazione corrente.

Se poi bandisci tale aritmetica hai semplicemente un linguaggio
decisamente meno comodo senza essenzialmente nessun vantaggio
concreto.


> Sia dal punto di vista matematico che da quello del Python, "==" è
> qualcosa che non ha _niente_ a che fare con l'aritmetica: dal punto di
> vista logico-matematico perché l'eguaglianza non è (necessariamente) un
> operatore, dal punto di vista di come ragiona il Python perché "==" può
> confrontare due oggetti qualsiasi, non solo numerici. Diventa
> l'uguaglianza tra numeri... esattamente quando gli argomenti sono due
> numeri.

1. Da un punto di vista matematico il predicato = (che da pythonisti chiamiamo ==)
ha *tantissimo* a che fare con l'aritmetica stessa. Non so se hai fatto un minimo
di fondamenti, ma fra gli assiomi di peano ci sono precisamente quelli che
riguardano =, senza non fondi l'aritmetica sulla set theory. Puoi fondarla sulla
logica combinatoria, ma ovviamente stiamo spostandoci alquanto.

2. Dal punto di vista "matematico" un operatore come il == è una bestiaccia. 
Immagino tu non abbia lavorato più di tanto (suppongo nulla) sulla semantica
formale dei linguaggi di programmazione. Formalizzare un linguaggio ad oggetti
è abbastanza un bagno di sangue (fidati, è un bagno di sangue anche avere
un linguaggetto come C). Formalizzare qualcosa come Python è essenzialmente
un suicidio. La semantica denotazionale è essenzialmente inadatta. Io avevo
lavorato parecchio con structured operational semantics, che funzionerebbe
anche decentemente... ma il dinamismo di Python sarebbe una iattura.

 Quindi, tiriamo fuori la matematica dal discorso. O le cose si fanno bene, e
ti garantisco che quando si va sul formale divento *davvero* sgradevolmente
pignolo, oppure la tiriamo indietro. Ma fino in fondo.

Quindi rimaniamo al problema fondamentale: stiamo cercando di avere
booleani che si comportano come gli interi. Se no, devi proibire le commistioni.
Ed essenzialmente hai un altro linguaggio, che non è Python, che è più scomodo
di Python e che, per coerenza, deve andare contro molte convenzioni ed idiomi
pythonici. 

Già... se ci troviamo 

if 0: print "ciao" -> ciao

mi aspetto anche 

if []: print "ciao" -> ciao

e per risolvere la cosa... if bool([])? Fa schifo. Allora tanto vale 
prendersi

if len([]) == 0:


che è un tipico idioma anti-pythonico, sconsigliato a tutti i niubbi.

> Stai scherzando?! Questo punto è stato chiarito 5 mail fa.
> _Evidentemente_ bool(0) == False.

Nope. Tu parti dal presupposto che i bool e gli int debbano essere tipi distinti.
E guarda caso concludi che debbano essere tipi distinti.

Il che porta un po' a quello che veramente è stato detto alcune mail fa.
Ovvero che di per se puoi benissimo tirare un'eccezione ogni volta che
mischi interi e booleani. 

Ma *non* è quello che vuole Guido. Per inciso continuo a non vedere 
il vantaggio di farlo in Python. E a quel punto mi aspetto anche che l'inferno
si scateni quando mischio float e interi. 

>> Comunque davvero, la cosa più naturale è vedere True e False come numeri a 1-bit.
> 
> Non dubito che per te possa essere (ormai?) naturale...

Ormai? Non so. Non vedo perchè un numero di un bit non debba essere un numero.
Ah, tu dici che non è un numero. Ok. Un numero di due bit è un numero? No?
Uno di 31? No? uno di 32? Si. Ah, ok.

>> Non ci formalizziamo perchè 0 == 0L, ma ci da fastidio False. 
> 
> ... ma renditi conto che questo è un argomento che non c'entra niente.

Come non centra niente? 0L è lo 0 dei long, 0 degli int, False dei bool.
Caso che mai non ti fosse sfuggito, long e int sono tipi *diversi* in Python.
Eppure non ti stupisci che lo 0 dei long sia diverso dallo 0 degli interi.
Per non parlare dello 0 dei float e quello degli interi.


> Tutti i tipi numerici hanno somma, sottrazione, moltiplicazione,
> divisione (questa, con qualche distinguo), che definiscono su di essi
> delle immersioni che sono più che canoniche, sono _ovvie_: i naturali
> (chiusi solo per la somma) sono degli interi (chiusi per somma,
> sottrazione, prodotto), che sono dei razionali (OK, questi l'informatica
> per quanto ne so ce li risparmia), che sono dei reali (chiusi per tutte
> le operazioni - tralasciando la rappresentazione approssimata), che
> volendo sono dei complessi.

Ti mancano un sacco di pezzi, mi dispiace. Di fatto ti aspetti erroneamente
che i tipi numerici in macchina si comportino come quelli della matematica.

In primo luogo "l'informatica" non ci risparmia affatto i razionali. In scheme 
per esempio sono abbastanza standard (anzi, sarebbero nello standard).
Python stesso li implementa in una libreria standard, sebbene da poco.

Il tuo discorso purtroppo cade in quanto tu hai l'erronea convinzione che 
quello che hai studiato in algebra elementare debba valere su un computer.

I reali non ci sono. E non ci possono essere. La dimostrazione è pure banale,
quindi dimenticali. Quella che tu definisci "approssimazione" ha un piccolo
problema: le operazioni che ci sono definite sopra *non* hanno le proprietà
delle operazioni che hai sui reali. Tipicamente non hai nemmeno la proprietà
associativa. Non sei manco in un semigruppo. 

Idem per gli interi. Gli interi (di C) *non* hanno proprietà associativa sull'addizione.
Sono "fatti meglio" gli unsigned che hanno una buona aritmetica modulare. Ma
finisce li la storia.

In Python invece la somma non è nemmeno un operazione? Come come?
Se sommi due interi, puoi ottenere un long. Ergo quella, matematicamente,
non è un'operazione. Certo, è comodo. Ma appunto, abbandona quello che
"dovrebbe essere".


> In tutto ciò i booleani, che sono nati come logica a due valori in cui
> le due operazioni "standard" erano or (l'equivalente intero è "max", non
> una delle operazioni aritmetiche) e and ("min"), sono un caso a parte.
> Ma non c'è solo problema storico: c'è che seppure l'addizione (così come
> la sottrazione) assomiglia ad un'operazione logica, lo xor, purtroppo
> l'insieme dei booleani _non è chiuso_ rispetto ad essa (dato che anche
> assumendo che i booleani siano numeri, True + True non sarebbe un
> booleano): ovvero non si comporta affatto come lo xor. Quindi _comunque_
> quando parliamo di addizione di booleani è piuttosto ipocrita spacciarla
> per un'operazione "nativa": è, come tutti hanno ammesso, una comodissima
> eredità del C.

Non è un'operazione infatti. Esattamente come in Python non è un'operazione
la somma. Come come?

Dal momento che int è un tipo *limitato* quando fai somma di due interi, puoi sforare
e quando succede:
1. dai eccezione
2. cambi tipo

Il comportamento in Python è il secondo. Secondo le definizioni dell'aritmetica
e dell'algebra elementare che io non dovrei sapere (rotfl) questa *non* è un'operazione.

Un'operazione in senso matematico è la somma sugli unsigned (che infatti hanno
esattamente il comportamento dell'aritmetica modulare). Non è un'operazione una
cosa che ti tira un integer overflow e non è un'operazione una 

AxA -> B dove B non e' incluso in A.

Infatti ti sei ben guardato da definire la sottrazione un'operazione sui naturali e la divisione
un'operazione sugli interi. 

Ecco... la somma nemmeno lei è un'operazione. Questo se non vogliamo
considerare l'estensione automatica del tipo.

Apparentemente sommare due interi e ottenere automaticamente un long non ti
infastidisce, sommare due booleani e ottenere un int si. Boh.

BTW, non tirare in ballo xor e compagnia, al momento non hanno nulla a che vedere.

Una volta ammessa sta somma fra interi che da un long, per quando mi riguarda ammetto
anche la somma su booleani che mi da un int.

Per inciso, le operazioni booleane sono facilmente estensibili agli interi
come operazioni bit a bit. Quindi continuo a non vedere il problema.


> D'altronde, vedila in un altro modo: se per te è perfettamente naturale
> che i booleani siano semplicemente (un piccolo sottoinsieme degli)
> interi, e ti sembra quindi naturale che siano _identici_ in tutto e per
> tutto rispettivamente a 0 e 1, non ti sembrerebbe naturale anche che
> 
> In [1]: isinstance(1,bool)
> Out[1]: True
> 
> ?!

Solo se non avessi capito una pura fava di niente di progettazione ad oggetti
e avessi invertito il vincolo di ereditarietà nella mia testa.

Poi essere "identici" è una proprietà forte. In genere non mi interessa.
Quello che mi interessa (e che è alla base della programmazione con 
linguaggi dinamici) è *si comporta come*.

Oltretutto, quello che probabilmente ti sfugge è che se un animale è un cane,
un cane non è un intero. Ergo se un booleano è un intero, un interno non è
un booleano.

E, in conclusione, quella dell'ereditarietà è solo una scorciatoia implementativa.
Quello che Guido vuole e che i Pythonisti si aspettano è che i booleani si
possano usare dove si possono usare gli interi. Punto e stop. Sarebbe meglio
che un booleano non fosse un intero ma si comportasse come tale. Amen.

> La scomoda verità è che se volessimo una cosa "pulita" con False != 0,
> il modo giusto sarebbe che, come in C, False e True fossero
> semplicemente _definiti_ individualmente come 0 e 1. Solo che il tipo
> "bool" ci fa comodo... mentre la riga di codice che ho scritto sopra è
> assolutamente inutile.

No, la riga di sopra è utilissima a mostrare la confusione che hai in testa
e che spero di riuscire a sciogliere nei prossimi post. 

Nella tua crociata contro if 0 non ti sei accorto di una cosa ben più drammatica.
True e False, in Python, non sono letterali. Sono *variabili*. Come come?

>>> True = False
>>> if True: print "buh!"
... 
>>> 

Questo si che fa un po' cacare. Certo un pervertito che va a rinominare True
si merita tutto il male che può capitargli. E siccome i Pythonisti sono tutti 
adulti e vaccinati, si fa conto che quella cosa lassù non venga mai fatta e che
tanto se uno vuole fottere il sistema ci sono anche metodi più creativi.

Poi, tanto per divertitci... la sotto secondo me il vero True viene semplicemente
nascosto e non succede nulla di grave, tanto che un del riporta tutto a post.

> Ciò detto, ho già scritto che in fondo me ne frega assai: è evidente
> che, posto che Guido si è già espresso, tutto il thread è puramente
> teorico.

Considera anche questo... in Ruby false e nil sono gli unici valori veramente
falsi. Ovviamente Ruby funziona e tutto... ma viene spesso elencato fra le cose
contro-intuitive di Ruby. 

Non sono quindi convinto che la praticità di Python gioverebbe di questa scelta.





Maggiori informazioni sulla lista Python