====== Porting da Python 2 a Python 3 ======
===== 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 =====
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 =====
0644 # Python 2 only
0o644 # Python 2 and 3
===== Backtick repr =====
`x` # Python 2 only
repr(x) # Python 2 and 3
===== 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'))
>>
print type(b'il tipo byte non esiste')
>>
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'))
>>
==== 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
print('e Python', python_version(), end="")
print(' ha anche ', type(bytearray(b'bytearrays')))
>> e Python 3.7.3 ha anche
'Ora cercare di sommare una stringa' + b' ed un bytes provoca errore.'
>> Traceback (most recent call last):
File "", line 1, in
'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 "", line 1, in
raise IOError, "file Error"
IOError: file Error
raise IOError("file error")
>> Traceback (most recent call last):
File "", line 1, in
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 "", line 1, in
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 "", line 1, in
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 "", line 1, in
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 "", line 1, in
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 "", line 1, in
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)
>>
my_input = raw_input('enter a number: ')
>> enter a number: 123
type(my_input)
>>
==== Python 3 ====
my_input = input('enter a number: ')
>> enter a number: 123
type(my_input)
>>
===== 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))
>>
==== Python 3 ====
print(range(3))
>> range(0, 3)
print(type(range(3)))
>>
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
===== Custom class behaviour =====
===== Dictionaries =====
heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
==== Scorrere le chiavi / i valori / gli elementi di un dizionario ====
=== Le chiavi ===
== Python 2 ==
for key in heights.iterkeys():
...
== Python 2 e 3 ==
for key in heights:
...
=== I Valori ===
== Python 2 ==
for value in heights.itervalues():
...
== Python 2 e 3 ==
#Opzione 0: Sconsigliata
for value in heights.values(): # Maggiore consumo di memoria in Py2
...
# Opzione 1
from builtins import dict
heights = dict(Fred=175, Anne=166, Joe=192)
for key in heights.values(): # efficiente in Py2 e Py3
# Opzione 2
from builtins import itervalues
for key in itervalues(heights):
...
=== Gli Elementi ===
== Python 2 ==
for (key, value) in heights.iteritems():
...
== Python 2 e 3 ==
# Opzione 1
for (key, value) in heights.items(): # inefficiente in Py2
...
# Opzione 2
from future.utils import viewitems
for (key, value) in viewitems(heights): # si compporta come un set
...
# Opzione 3
from future.utils import iteritems
for (key, value) in iteritems(heights):
...
==== Chiavi/Valori/Elementi di un Dizionario come Lista ====
=== Chiavi ===
== Python 2 ==
keylist = heights.keys()
assert isinstance(keylist, list)
== Python 2 e 3 ==
keylist = list(heights)
assert isinstance(keylist, list)
=== Valori ===
== Python 2 ==
heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}
valuelist = heights.values()
assert isinstance(valuelist, list)
== Python 2 e 3 ==
# Opzione 1
valuelist = list(heights.values()) # inefficiente in Py2
# Opzione 2
from builtins import dict
heights = dict(Fred=175, Anne=166, Joe=192)
valuelist = list(heights.values())
# Opzione 3
from future.utils import listvalues
valuelist = listvalues(heights)
# Opzione 4
from future.utils import itervalues
valuelist = list(itervalues(heights))
=== Elementi ===
== Python 2 e 3 ==
# Opzione 1
itemlist = list(heights.items()) # inefficiente in Py2
# Opzione 2
from future.utils import listitems
itemlist = listitems(heights)
# Opzione 3
from future.utils import iteritems
itemlist = list(iteritems(heights))
===== Imports relativi ad un package =====
supponiamo che il package abbia struttura:
mypackage/
__init__.py
submodule1.py
submodule2.py
dove il modulo //submodule1.py// contenga il seguente codice:
# Python 2 only:
import submodule2
# Python 2 and 3:
from . import submodule2
# Python 2 and 3:
# To make Py2 code safer (more like Py3) by preventing
# implicit relative imports, you can also add this to the top:
from __future__ import absolute_import
===== Altre variazioni ai builtins =====
==== File IO con open() ====
# Python 2 only
f = open('myfile.txt')
data = f.read() # as a byte string
text = data.decode('utf-8')
# Python 2 and 3: alternative 1
from io import open
f = open('myfile.txt', 'rb')
data = f.read() # as bytes
text = data.decode('utf-8') # unicode, not bytes
# Python 2 and 3: alternative 2
from io import open
f = open('myfile.txt', encoding='utf-8')
text = f.read() # unicode, not bytes
==== reduce() ====
# Python 2 only:
assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
# Python 2 and 3:
from functools import reduce
assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5
==== file() ====
# Python 2 only:
f = file(pathname)
# Python 2 and 3:
f = open(pathname)
# But preferably, use this:
from io import open
f = open(pathname, 'rb') # if f.read() should return bytes
# or
f = open(pathname, 'rt') # if f.read() should return unicode text
==== exec ====
# Python 2 only:
exec 'x = 10'
# Python 2 and 3:
exec('x = 10')
# Python 2 only:
g = globals()
exec 'x = 10' in g
# Python 2 and 3:
g = globals()
exec('x = 10', g)
# Python 2 only:
l = locals()
exec 'x = 10' in g, l
# Python 2 and 3:
exec('x = 10', g, l)
==== execfile() ====
# Python 2 only:
execfile('myfile.py')
# Python 2 and 3: alternative 1
from past.builtins import execfile
execfile('myfile.py')
# Python 2 and 3: alternative 2
exec(compile(open('myfile.py').read()))
# This can sometimes cause this:
# SyntaxError: function ... uses import * and bare exec ...
# See https://github.com/PythonCharmers/python-future/issues/37
==== unichr() ====
# Python 2 only:
assert unichr(8364) == '€'
# Python 3 only:
assert chr(8364) == '€'
# Python 2 and 3:
from builtins import chr
assert chr(8364) == '€'
==== intern() ====
# Python 2 only:
intern('mystring')
# Python 3 only:
from sys import intern
intern('mystring')
# Python 2 and 3: alternative 1
from past.builtins import intern
intern('mystring')
# Python 2 and 3: alternative 2
from six.moves import intern
intern('mystring')
# Python 2 and 3: alternative 3
from future.standard_library import install_aliases
install_aliases()
from sys import intern
intern('mystring')
# Python 2 and 3: alternative 4
try:
from sys import intern
except ImportError:
pass
intern('mystring')
==== apply() ====
args = ('a', 'b')
kwargs = {'kwarg1': True}
# Python 2 only:
apply(f, args, kwargs)
# Python 2 and 3: alternative 1
f(*args, **kwargs)
# Python 2 and 3: alternative 2
from past.builtins import apply
apply(f, args, kwargs)
==== chr() ====
# Python 2 only:
assert chr(64) == b'@'
assert chr(200) == b'\xc8'
# Python 3 only: option 1
assert chr(64).encode('latin-1') == b'@'
assert chr(0xc8).encode('latin-1') == b'\xc8'
# Python 2 and 3: option 1
from builtins import chr
assert chr(64).encode('latin-1') == b'@'
assert chr(0xc8).encode('latin-1') == b'\xc8'
# Python 3 only: option 2
assert bytes([64]) == b'@'
assert bytes([0xc8]) == b'\xc8'
# Python 2 and 3: option 2
from builtins import bytes
assert bytes([64]) == b'@'
assert bytes([0xc8]) == b'\xc8'
==== cmp() ====
# Python 2 only:
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
# Python 2 and 3: alternative 1
from past.builtins import cmp
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
# Python 2 and 3: alternative 2
cmp = lambda(x, y): (x > y) - (x < y)
assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0
==== reload() ====
# Python 2 only:
reload(mymodule)
# Python 2 and 3
from imp import reload
reload(mymodule)
===== Standard library =====
==== dbm modules ====
# Python 2 only
import anydbm
import whichdb
import dbm
import dumbdbm
import gdbm
# Python 2 and 3: alternative 1
from future import standard_library
standard_library.install_aliases()
import dbm
import dbm.ndbm
import dbm.dumb
import dbm.gnu
# Python 2 and 3: alternative 2
from future.moves import dbm
from future.moves.dbm import dumb
from future.moves.dbm import ndbm
from future.moves.dbm import gnu
# Python 2 and 3: alternative 3
from six.moves import dbm_gnu
# (others not supported)
==== commands / subprocess modules ====
# Python 2 only
from commands import getoutput, getstatusoutput
# Python 2 and 3
from future import standard_library
standard_library.install_aliases()
from subprocess import getoutput, getstatusoutput
==== subprocess.check_output() ====
# Python 2.7 and above
from subprocess import check_output
# Python 2.6 and above: alternative 1
from future.moves.subprocess import check_output
# Python 2.6 and above: alternative 2
from future import standard_library
standard_library.install_aliases()
from subprocess import check_output
==== Collections: Counter and OrderedDict ====
# Python 2.7 and above
from collections import Counter, OrderedDict
# Python 2.6 and above: alternative 1
from future.moves.collections import Counter, OrderedDict
# Python 2.6 and above: alternative 2
from future import standard_library
standard_library.install_aliases()
from collections import Counter, OrderedDict
==== StringIO module ====
# Python 2 only
from StringIO import StringIO
from cStringIO import StringIO
# Python 2 and 3
from io import BytesIO
# and refactor StringIO() calls to BytesIO() if passing byte-strings
==== http module ====
# Python 2 only:
import httplib
import Cookie
import cookielib
import BaseHTTPServer
import SimpleHTTPServer
import CGIHttpServer
# Python 2 and 3 (after ``pip install future``):
import http.client
import http.cookies
import http.cookiejar
import http.server
==== xmlrpc module ====
# Python 2 only:
import DocXMLRPCServer
import SimpleXMLRPCServer
# Python 2 and 3 (after ``pip install future``):
import xmlrpc.server
# Python 2 only:
import xmlrpclib
# Python 2 and 3 (after ``pip install future``):
import xmlrpc.client
==== html escaping and entities ====
# Python 2 and 3:
from cgi import escape
# Safer (Python 2 and 3, after ``pip install future``):
from html import escape
# Python 2 only:
from htmlentitydefs import codepoint2name, entitydefs, name2codepoint
# Python 2 and 3 (after ``pip install future``):
from html.entities import codepoint2name, entitydefs, name2codepoint
==== html parsing ====
# Python 2 only:
from HTMLParser import HTMLParser
# Python 2 and 3 (after ``pip install future``)
from html.parser import HTMLParser
# Python 2 and 3 (alternative 2):
from future.moves.html.parser import HTMLParser
==== urllib module ====
''urllib'' è uno dei moduli più complessi da usare in compatibilità fra 2 e 3.
# Python 2 only:
from urlparse import urlparse
from urllib import urlencode
from urllib2 import urlopen, Request, HTTPError
# Python 3 only:
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
# Python 2 and 3: easiest option
from future.standard_library import install_aliases
install_aliases()
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
# Python 2 and 3: alternative 2
from future.standard_library import hooks
with hooks():
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
# Python 2 and 3: alternative 3
from future.moves.urllib.parse import urlparse, urlencode
from future.moves.urllib.request import urlopen, Request
from future.moves.urllib.error import HTTPError
# Python 2 and 3: alternative 4
try:
from urllib.parse import urlparse, urlencode
from urllib.request import urlopen, Request
from urllib.error import HTTPError
except ImportError:
from urlparse import urlparse
from urllib import urlencode
from urllib2 import urlopen, Request, HTTPError
==== Tkinter ====
# Python 2 only:
import Tkinter
import Dialog
import FileDialog
import ScrolledText
import SimpleDialog
import Tix
import Tkconstants
import Tkdnd
import tkColorChooser
import tkCommonDialog
import tkFileDialog
import tkFont
import tkMessageBox
import tkSimpleDialog
import ttk
# Python 2 and 3 (after ``pip install future``):
import tkinter
import tkinter.dialog
import tkinter.filedialog
import tkinter.scrolledtext
import tkinter.simpledialog
import tkinter.tix
import tkinter.constants
import tkinter.dnd
import tkinter.colorchooser
import tkinter.commondialog
import tkinter.filedialog
import tkinter.font
import tkinter.messagebox
import tkinter.simpledialog
import tkinter.ttk
==== socketserver ====
# Python 2 only:
import SocketServer
# Python 2 and 3 (after ``pip install future``):
import socketserver
==== copy_reg, copyreg ====
# Python 2 only:
import copy_reg
# Python 2 and 3 (after ``pip install future``):
import copyreg
==== configparser ====
# Python 2 only:
from ConfigParser import ConfigParser
# Python 2 and 3 (after ``pip install configparser``):
from configparser import ConfigParser
==== queue ====
# Python 2 only:
from Queue import Queue, heapq, deque
# Python 2 and 3 (after ``pip install future``):
from queue import Queue, heapq, deque
==== repr, reprlib ====
# Python 2 only:
from repr import aRepr, repr
# Python 2 and 3 (after ``pip install future``):
from reprlib import aRepr, repr
==== UserDict, UserList, UserString ====
# Python 2 only:
from UserDict import UserDict
from UserList import UserList
from UserString import UserString
# Python 3 only:
from collections import UserDict, UserList, UserString
# Python 2 and 3: alternative 1
from future.moves.collections import UserDict, UserList, UserString
# Python 2 and 3: alternative 2
from six.moves import UserDict, UserList, UserString
# Python 2 and 3: alternative 3
from future.standard_library import install_aliases
install_aliases()
from collections import UserDict, UserList, UserString
==== itertools: filterfalse, zip_longest ====
# Python 2 only:
from itertools import ifilterfalse, izip_longest
# Python 3 only:
from itertools import filterfalse, zip_longest
# Python 2 and 3: alternative 1
from future.moves.itertools import filterfalse, zip_longest
# Python 2 and 3: alternative 2
from six.moves import filterfalse, zip_longest
# Python 2 and 3: alternative 3
from future.standard_library import install_aliases
install_aliases()
from itertools import filterfalse, zip_longest
===== Python-Modernize =====
==== Scopo del progetto ====
Questa libreria è un wrapper attorno a ''lib2to3'' che serve a rendere il codice Python 2 più moderno in ottica di portarlo su Python 3.
Il comando python-modernize funziona come 2to3. Ecco come riscrivere un singolo file:
python-modernize -w example.py
Il sito Web del progetto è disponibile su [[https://github.com/python-modernize/python-modernize|GitHub]] e il nome del progetto PyPI è [[https://pypi.python.org/pypi/modernize|modernize]]
==== Una nota sulla gestione delle stringhe ====
* Di default modernize non cambia le stringhe Unicode. \\ Questa è l'opzione più semplice se vuoi supportare Python 3.x e versioni successive insieme a Python 2.
* In alternativa, c'è il flag ''**--six-unicode**'' che avvolgerà le stringhe Unicode con la funzione helper **six.u()** usando il fixer contenuto in ''libmodernize.fixes.fix_unicode''.\\ Ciò è utile se si desidera supportare Python 3.1 e Python 3.2 senza grandi cambiamenti.
* L'ultima alternativa è il flag ''**--future-unicode**'' che importa gli ''unicode_literals'' dal modulo **__future__** usando il fixer contenuto in ''libmodernize.fixes.fix_unicode_future''.\\ Ciò richiede Python 2.6 e versioni successive e richiederà di contrassegnare le bytestring con b '' e le stringhe native in str('') o qualcosa di simile che sopravviva alla trasformazione.
==== Preparazione ====
Dal prompt dei comandi lanciare
pip --install modernize
Al termine dell'installazione avrete a disposizione l'eseguibile **python-modernize**
==== Batch di conversione ====
set mydatetime=%date:~-4%%date:~-7,2%%date:~-10,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set logpath=.\Logs
set logfile=%logpath%\Migrate_%Application%_2to3_%mydatetime%.log
set RepoPath=path\to\your \web2py\applications
set Application=Your_Folder_App_Name
IF NOT EXIST %logpath% (
MKDIR %logpath%
)
(FOR /f "delims=" %%a IN ('DIR %RepoPath%\%Application%\*.pyc /b /s') do (del %%a))
(FOR /f "delims=" %%a IN ('DIR %RepoPath%\%Application%\*.py /b /s') do (python-modernize -w %%a >> %LogFile%))
(FOR /f "delims=" %%a IN ('DIR %RepoPath%\%Application%\*.bak /b /s') do (del %%a))
===== PyCharm e Cross-compatibilità =====
{{:python:annotazione_2020-01-12_230840.png?400|}}
===== Fonti =====
- [[http://python-future.org/compatible_idioms.html]]
- [[https://www.geeksforgeeks.org/important-differences-between-python-2-x-and-python-3-x-with-examples/]]
- [[https://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html]]
- [[https://python-modernize.readthedocs.io/en/latest/]]