Questa è una vecchia versione del documento!
Indice
Porting da Python 2 a Python 3
*WIP*: Quello seguente è un articolo in divenire
Differenze con impatto maggiore
Python nell'evoluzione dalla versione 2 alla versione 3 ha radicalmente cambiato molte parti del linguaggio.
Fra le modifiche con un impatto piú rilevante troviamo:
- la scrittura su console print
- Sollevare e Gestire eccezioni
- la divisione intera
- Unicode
- xrange
Nelle seguenti sezioni vedremo come scrivere codice che sia compatibile su entrambe le versioni.
Print è ora una funzione e per tanto vuole le parentesi tonde.
# python 2.x print "Ciao" # python 3.x print("Ciao")
Per una semplice print, potete mettere le parentesi anche su Python 2 ed il risultato sarà identico.
Varia invece se usate una print con parti concatenate da virgola
# python 2.x print "Ciao", " Come stati?" >> Ciao Come stai? # python 3.x print ("Ciao", " Come stati?") >> Ciao Come stai?
In questo caso, aggingendo le parentesi se python 2 otteniamo un risultato che è quello desiderato
# python 2.x print ("Ciao", " Come stati?") >> ("Ciao", "Come stai?")
Per ovviare a questo spiacevole risultato, ci viene in aiuto il modulo __future__
# python 2.x from __future__ import print_function print ("Ciao", " Come stati?") >> Ciao Come stai?
Divisione Intera
Questa modifica è particolarmente pericolosa poiché può spesso passare inosservata (non genera un SyntaxError). Quindi si consiglia l'importazione del modulo __future__ in tutti gli script Python 2 ove sia prevista una divisione).
Python 2
print 'Python', python_version() print '3 / 2 =', 3 / 2 print '3 // 2 =', 3 // 2 print '3 / 2.0 =', 3 / 2.0 print '3 // 2.0 =', 3 // 2.0
>> Python 2.7.6 3 / 2 = 1 3 // 2 = 1 3 / 2.0 = 1.5 3 // 2.0 = 1.0
Python 3
print('Python', python_version()) print('3 / 2 =', 3 / 2) print('3 // 2 =', 3 // 2) print('3 / 2.0 =', 3 / 2.0) print('3 // 2.0 =', 3 // 2.0)
>> Python 3.4.1 3 / 2 = 1.5 3 // 2 = 1 3 / 2.0 = 1.5 3 // 2.0 = 1.0
Python 2 e 3
# Python 2 and 3 from __future__ import division assert 3 / 2 == 1.5
Long integers
In Python 3, il tipo int corrisponde come implementazione al vecchio tipo long, quindi int e long diventano equivalenti.
Python 2
bigint = 1L if isinstance(bigint, (int, long)): ...
Python 3
bigint = 1 if isinstance(bigint, int): ...
Python 2 e 3
ATTENZIONE: A Python 3 da fastidio la 'L' quindi in Python 2 sarà necessario modificare il codice come mostrato di seguito.
from builtins import int bigint = int(1) if isinstance(bigint, int): # matches both int and long on Py2 ...
Octal constants
Backtick repr
Unicode (text) string literals e Byte-string literals
In Python 2 esistono due diversi tipi stringa:
- ASCII str()
- UNICODE unicode()
Ma non esiste la stringa di byte.
Ora, in Python 3, tutte le stringhe sono Unicode (utf-8), in aggiunta abbiamo due nuove classi: byte e bytearrays.
Python 2
print 'Python', python_version() >> Python 2.7.6 print type(unicode('Questa è come una stringa di Python3')) >> <type 'unicode'> print type(b'il tipo byte non esiste') >> <type 'str'> print 'Queste due stringe sono' + b' dello stesso tipo' >> Queste due stringe sono dello stesso tipo print type(bytearray(b'Stranamente però esiste il bytearray')) >> <type 'bytearray'>
Python 3
print('Python', python_version()) print('ora le stringhe sono utf-8 \u03BCnico\u0394é!') >> Python 3.7.3 strings are now utf-8 μnicoΔé! print('Python', python_version(), end="") print(' ha', type(b' il tipo bytes')) >> Python 3.7.3 ha <class 'bytes'> print('e Python', python_version(), end="") print(' ha anche ', type(bytearray(b'bytearrays'))) >> e Python 3.7.3 ha anche <class 'bytearray'> 'Ora cercare di sommare una stringa' + b' ed un bytes provoca errore.' >> Traceback (most recent call last): File "<pyshell#8>", line 1, in <module> 'Ora cercare di sommare una stringa' + b' ed un bytes provoca errore.' TypeError: can only concatenate str (not "bytes") to str
Python 2 e 3
# Byte-String # Python 2 and 3: byte-strings as strings. from builtins import str a = u'abc' b = b'def' c = b.decode() assert isinstance(a, str) and isinstance(c, str) # ... # Unicode: Alternative 1 # Python 2 and 3 s1 = u'The Zen of Python' s2 = u'きたないのよりきれいな方がいい\n' # Unicode: Alternative 2 # Python 2 and 3 from __future__ import unicode_literals # rende tutte le stringhe unicode s1 = 'The Zen of Python' s2 = 'きたないのよりきれいな方がいい\n'
xrange
Poichè xrange() è generalmente più veloce di range(), in Python 3 range() è implementata come xrange() mentre quest'ultima è stata rimossa.
Questo lo rende efficientissimo su iterazioni 1-time, mentre su iterazioni multiple perde un po' di efficienza perchè, ogni volta, la generazione viene rifatta da zero.
In Python 3, la chiamata alla funzione xrange() genera un NameError.
Sollevare Eccezioni
Mentre Python 2 accetta sia la sintassi “vecchia” che la “nuova”, Python 3 genera un SyntaxError se non l'argomento non viene messo tra parentesi.
Python 2
raise IOError, "file error" >> Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> raise IOError, "file Error" IOError: file Error raise IOError("file error") >> Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> raise IOError("file Error") IOError: file Error
Python 3
raise IOError, "file error" >> SyntaxError: invalid syntax raise IOError("file error") >> Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> raise IOError("file error") OSError: file error
Python 2 e 3
Poichè Python 2 supporta la sintassi con le parentesi è buona cosa scrivere le raise con tale sintassi anche in Python 2.
Sollevare Eccezioni (TraceBack)
Python 2
traceback = sys.exc_info()[2] raise ValueError, "dodgy value", traceback
Python 3
raise ValueError("dodgy value").with_traceback()
Python 2 e 3
# Python 2 e 3: Alternativa 1 from future.utils import raise_ traceback = sys.exc_info()[2] raise_(ValueError, "dodgy value", traceback) #... #... # Python 2 e 3: Alternativa 2 from future.utils import raise_with_traceback raise_with_traceback(ValueError("dodgy value"))
Gestire Eccezioni
In Python 3 ora è obbligatoria la keyword as.
Python 2
try: ... except ValueError, e: ...
Python 3
try: ... except ValueError as e: ...
Python 2 e 3
Python 2 supporta la sintassi di Python 3.
try: ... except ValueError as e: ...
La funzione next() ed il metodo .next()
Mentre in Python 2.7 per ottenere l'elemento successivo di un generatore è possibile usare sia la funzione next() che il metodo .next(), il Python 3 è presente solo la funzione ed un eventuale chiamata al metodo genera un'eccezione di tipo AttributeError.
Python 2
my_generator = (letter for letter in 'abcdefg') next(my_generator) >> 'a' my_generator.next() >> 'b'
Python 3
my_generator = (letter for letter in 'abcdefg') next(my_generator) >> 'a' my_generator.next() >> Traceback (most recent call last): File "<pyshell#3>", line 1, in <module> my_generator.next() AttributeError: 'generator' object has no attribute 'next'
Python 2 e 3
La soluzione è utilizzare sempre la funzione next().
Variabili For-loop e perdita del namespace globale
In Python 3 non avviene più la perdita del namespace globale.
Python 2
i = 1 print 'before: i =', i >> before: i = 1 print 'comprehension: ', [i for i in range(5)] >> comprehension: [0, 1, 2, 3, 4] print 'after: i =', i >> after: i = 4
Python 3
i = 1 print('before: i =', i) >> before: i = 1 print('comprehension:', [i for i in range(5)]) >> comprehension: [0, 1, 2, 3, 4] print('after: i =', i) >> after: i = 1
Comparazione fra tipi unorderable
InPython 3 il tentativo di comparare due entità non ordinabili restituisce un TypeError.
Python 2
print "[1, 2] > 'foo' = ", [1, 2] > 'foo' >> [1, 2] > 'foo' = False print "(1, 2) > 'foo' = ", (1, 2) > 'foo' >> (1, 2) > 'foo' = True print "[1, 2] > (1, 2) = ", [1, 2] > (1, 2) >> [1, 2] > (1, 2) = False
Python 3
print("[1, 2] > 'foo' = ", [1, 2] > 'foo') >> Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> print("[1, 2] > 'foo' = ", [1, 2] > 'foo') TypeError: '>' not supported between instances of 'list' and 'str' print("(1, 2) > 'foo' = ", (1, 2) > 'foo') >> Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> print("(1, 2) > 'foo' = ", (1, 2) > 'foo') TypeError: '>' not supported between instances of 'tuple' and 'str' print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2)) >> Traceback (most recent call last): File "<pyshell#5>", line 1, in <module> print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2)) TypeError: '>' not supported between instances of 'list' and 'tuple'
Analisi degli input dell'utente tramite input()
In Python 2 la funzione input() cercava di determinare il tipo dell'input inserito (a volte causando problemi perchè gestiva la stringa di input come se fosse codice Python).
In Python 3 questo comportamento è stato corretto ed ora input() restituisce sempre una stringa.
Per lo stesso comportamento in Python 2 è necessario chiamare la funzione raw_input().
Python 2
my_input = input('enter a number: ') >> enter a number: 123 type(my_input) >> <type 'int'> my_input = raw_input('enter a number: ') >> enter a number: 123 type(my_input) >> <type 'str'>
Python 3
my_input = input('enter a number: ') >> enter a number: 123 type(my_input) >> <class 'str'>
Restituire Oggetti Iterabili al posto si Liste
Come abbiamo già visto nella sezione xrange, alcune funzioni e metodi restituiscono oggetti iterabili in Python 3, anziché liste come in Python 2.
Poichè solitamente vengono ciclati una sola volta, questo cambiamento consente di salvare memoria.
Tuttavia, con una perdita di efficienza, è anche possibile, a differenza dei generatori, scorrere più volte se necessario.
E per quei casi in cui abbiamo davvero bisogno degli oggetti list, possiamo semplicemente convertire l'oggetto iterabile in una lista tramite la funzione list().
Python 2
print range(3) >> [0, 1, 2] print type(range(3)) >> <type 'list'>
Python 3
print(range(3)) >> range(0, 3) print(type(range(3))) >> <class 'range'> print(list(range(3))) >> [0, 1, 2]
Funzioni e metodi comunemente usati che non restituiscono più elenchi in Python 3:
zip()map()filter()- Metodo
.keys()dei dizionari - Metodo
.values()dei dizionari - Metodo
.items()dei dizionari
Variazione del comportamento della funzione Round
Python 3 ha adottato il modo ormai standard di arrotondare i decimali quando si ottiene un pareggio (.5) alle ultime cifre significative.
Ora, in Python 3, i decimali vengono arrotondati al numero pari più vicino.
Purtroppo questo comportamento non è workaround-abile … il che significa che bisogna tenerne conto.
Python 2
round(15.5) >> 16.0 round(16.5) >> 17.0
Python 3
round(15.5) >> 16 round(16.5) >> 16


