2009-06-30 11 views
13

pitone confronto decimalepitone confronto decimale

>>> from decimal import Decimal 
>>> Decimal('1.0') > 2.0 
True 

mi aspettavo per convertire 2,0 correttamente, ma dopo aver letto attraverso PEP 327 Capisco che ci sono stati alcuni ragione per non convertire implicitamente pone galleggiante decimale, ma non dovrebbe in quel caso dovrebbe aumentare TypeError come avviene in questo caso

>>> Decimal('1.0') + 2.0 
Traceback (most recent call last): 
    File "<string>", line 1, in <string> 
TypeError: unsupported operand type(s) for +: 'Decimal' and 'float' 

così fa tutti gli altri operatori/-% // etc

quindi il mio QUESTI ons are

  1. è questo comportamento corretto? (non per generare eccezioni in cmp)
  2. Cosa succede se ottengo la mia classe e a destra un convertitore float fondamentalmente Decimale (repr (float_value)), sono ci sono dei problemi? il mio caso d'uso coinvolge solo confronto dei prezzi

dettagli di sistema: Python 2.5.2 su Ubuntu 8.04.1

risposta

24

Re 1, è in effetti il ​​comportamento che abbiamo progettato - giusto o sbagliato come può essere (scusate se che i viaggi vostro caso d'uso, ma stavamo cercando di essere generale!).

In particolare, è stato a lungo il caso che ogni oggetto Python potesse essere soggetto a un confronto di disuguaglianza con tutti gli altri - oggetti di tipi che non sono realmente confronta vengono confrontati in modo arbitrario (coerentemente in una determinata esecuzione, non necessariamente attraverso le esecuzioni) ; il caso d'uso principale era l'ordinamento di un elenco eterogeneo per raggruppare gli elementi in esso per tipo.

Un'eccezione è stato introdotto solo per i numeri complessi, rendendoli non paragonabile a qualsiasi cosa - ma che era ancora molti anni fa, quando eravamo a volte cavalier di rompere perfettamente buon codice utente. Al giorno d'oggi siamo molto più restrittivi sulla compatibilità con le versioni precedenti all'interno di una major release (ad es.lungo la linea 2.*, e separatamente lungo il 3.* uno, anche se le incompatibilità sono ammessi tra il 2 e 3 -, infatti, che è il punto di avere una serie 3.*, lasciando noi correggere decisioni di progettazione del passato, anche in modi incompatibili).

I confronti arbitrari si sono rivelati più problematici di quanto valgano, causando confusione all'utente; e il raggruppamento per tipo può ora essere ottenuto facilmente ad es. con argomento key=lambda x: str(type(x)) su sort; così in Python 3 confronti tra oggetti di tipo diverso, a meno che gli oggetti stessi specificamente permettere che nei metodi di confronto, fa sollevare un'eccezione:

>>> decimal.Decimal('2.0') > 1.2 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: unorderable types: Decimal() > float() 

In altre parole, in Python 3 questo si comporta esattamente come si pensa che dovrebbe ; ma in Python 2 non lo fa (e mai lo sarà in nessun Python 2.*).

Re 2, starai bene, tuttavia, guarda a gmpy per quello che spero sia un modo interessante per convertire i doppi in frazioni di precisione infinita attraverso gli alberi Farey. Se i prezzi hai a che fare con sono precisi a non più di centesimi, utilizzare '%.2f' % x piuttosto che repr(x) -!)

Piuttosto che una sottoclasse di decimale, mi piacerebbe utilizzare una funzione di fabbrica, come

def to_decimal(float_price): 
    return decimal.Decimal('%.2f' % float_price) 

poiché, una volta prodotto, il decimale risultante è perfettamente ordinario.

+1

Questa risposta è un po 'datata: 'decimal.Decimal' e' float' sono numericamente comparabili in 2.7. "Modificato nella versione 2.7: un confronto tra un'istanza float x e un'istanza Decimal y ora restituisce un risultato basato sui valori di x e y. Nelle versioni precedenti x

1

Se è "giusto" è una questione di opinione, ma la logica del perché non v'è alcuna la conversione automatica esiste nel PEP e questa è stata la decisione presa. L'avvertenza è fondamentalmente che non è sempre possibile convertire esattamente tra float e decimale. Pertanto la conversione non dovrebbe essere implicita. Se nella tua applicazione sai che non hai mai abbastanza numeri significativi affinchè questo influiscano su di te, creare classi che permettano questo comportamento implicito non dovrebbe essere un problema.

Inoltre, un argomento principale è che i casi di utilizzo del mondo reale non esistono. È probabile che sia più semplice se utilizzi Decimal ovunque.

+0

Ho modificato la prima domanda un po ', a destra voglio dire non sollevare un'eccezione se non è possibile convertire in confronto –

+0

Aha, vedo. Sì, questo ha una motivazione diversa, e sono d'accordo che sollevare un'eccezione sembra più esplicito. Ma ancora una volta è una questione di gusti. –

2

Il confronto maggiore di funziona perché, per impostazione predefinita, funziona per tutti gli oggetti.

>>> 'abc' > 123 
True 

Decimal ha ragione semplicemente perché segue correttamente le specifiche. Se la specifica fosse l'approccio corretto è una domanda separata. :)

Solo i caveat normali quando si tratta di float, che sono brevemente riassunti sono: attenzione ai casi limite come zero zero, +/- infinito e NaN, non testare per l'uguaglianza (correlato al punto successivo) e conta sul fatto che la matematica sia leggermente imprecisa.

>>> print (1.1 + 2.2 == 3.3) 
False 
+2

'abc'> 123 genera un'eccezione TypeError in Python 3. Qual è la cosa giusta da fare, IMHO. –