Em breve, mais cedo ou mais tarde, teremos que migrar uma aplicação feita em Python 2.x para Python 3, não tem jeito é o nosso destino e porque migrar?
Como está escrito na wiki oficial do Python:
Python 2.7 é legado e o Python 3 é o presente e o futuro da linguagem.
Python 3 foi lançado em 2008, já a versão final do Python 2, a versão 2.7, saiu por volta de 2010, mas já com a decisão que a família 2.x não teria grandes releases. E desde então a versão 3.x vem sendo melhorada e conta com versões estáveis desde então, como por exemplo as versões 3.3 em 2012, 3.4 em 2014 e 3.5 em 2015.
Uma das primeiras mudanças que vemos no Python 3 é na função print, que agora realmente é uma função:
Python 2:
|
>>> print "o valor eh: ", 2*15 # o valor eh: 30 |
Python 3:
|
>>> print ("o valor eh: ", 2*15) # o valor eh: 30 |
Podemos também usar alguns argumentos novos na função print como por exemplo o end e o sep:
|
>>> print ("o valor eh", 2*15, sep=": ") o valor eh: 30 >>> print ("o valor eh", 2*15, sep=": ", end="!\n") # o \n garante uma nova linha o valor eh: 30! |
Divisão entre números inteiros mudou um pouco:
No Python 2.x a divisão entre 3 / 2 sempre retorna 1, mas no Python 3 o resultado é 1,5 (que bom né, rs), porque agora a divisão não depende mais do tipo do denominador e do numerador.
Se for necessário utilizar o comportamento antigo utilize duas barras, assim: 3 // 2
True e False agora são palavras reservadas:
No Python 2 podiamos fazer algumas coisas estranhas como:
|
>>> True = "Ai, ai, ai" >>> False = "Ai, ai, ai" >>> True == False True # -> viiixi |
No Python 3 finalmente isso não é mais possível:
|
>>> True = "Ai, ai, ai" File "<ipython-input-47-7885763f8a8f>", line 1 True = "Ai, ai, ai" ^ SyntaxError: can't assign to keyword |
Raising exceptions:
No Python 2.x podiamos usar a velha e nova sintaxe. No Python 3 podemos usar apenas a nova:
Python 2:
|
>>> raise IOError, "file error" # -> velha sintaxe --------------------------------------------------------------------------- IOError Traceback (most recent call last) in () ----> 1 raise IOError, "file error" IOError: file error >>> raise IOError("file error") # -> nova sintaxe # mesmo error de cima |
Python 3:
Só assim:
|
>>> raise IOError("file error") |
Manipulando exceções:
No Python 3 temos que usar obrigatoriamente a palavra chave “as”
Python 2:
|
>>> try: forçar um erro aqui exception ErrorName, err: print err |
Python 3:
|
>>> try: forçar um erro aqui exception ErrorName as err: print (err) |
Unpacking avançado:
Agora podemos fazer coisas do tipo:
|
>>> a, *resto, b = range(10) # colocar o meio da sequência na variável resto >>> a, resto, b (0, [1, 2, 3, 4, 5, 6, 7, 8], 9) >>> a, b, *resto = ["x", "p", "t", "o"] >>> a "x" >>> resto ['t', 'o'] >>> b "p" |
“keyword-only” arguments:
|
>>> def fn(a, b, option=True): >>> .... |
Com a função acima em mente vamos imaginar que você precisa que o parâmetro option seja usado da forma como está no contrato da função, ou seja, deve ser passado como no exemplo abaixo:
>>> fn(1, 2, option=False)
e nunca
>>> fn(1, 2, 3) # -> desse modo, o parâmetro option teria o valor 3 e não é o que queremos.
Felizmente no Python 3 isso é possivel, basta adicionar o argumento de quantidade indefinida de parâmetros posicionais, aqui representado por *args, como no exemplo abaixo:
|
>>> def fn(a, b, *args, option=True): >>> .... |
E assim dessa forma podemos chamar a função fn desse jeito:
>>> fn(1, 2, 3): # -> o parâmetro de valor 3 foi enviado para o argumento *args.
E o parâmetro option continua com o valor True.
Poderiamos utilizar dessa forma, que também funcionaria:
>>> fn(1, 2, 3, 4, option=False) # -> os parâmetro 3 e 4 foram enviados para o argumento *args.
Tudo agora é iterator
Se você fizer no Python 2.x:
|
>>> def my_sum(n): ret = 0 for i in range(n+1): ret += i return ret >>> my_sun(100000000) |
Tem grandes chances do seu computador travar, bem travado. Por que? Porque a função range() tenta alocar neste caso um espaço na memória com 100000000 posições, detonando a memória da máquina. Já no Python 3 isso não acontece porque a função range() retorna uma espécie de iterator que vai liberando a memória a cada loop.
Com isso a função xrange() do Python 2.x foi eleminada sem dó, pois o range() agora faz o papel que ela fazia.
Por isso em Python 3, range, zip, map, list, etc, são todos iterators.
Se você precisa de uma lista basta fazer isto: list(range(10))
Comparar tudo com tudo, não pode mais:
Python 2:
|
>>> '3' > 2 True >>> 'dois' > 1 # essa foi fo@$%! True |
No Python 3 isso não pode mais, melhor assim né:
|
>>> '3' > 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unorderable types: str() > int() |
O método .next() dos iterators foi para o /dev/null
No Python 2.x podemos utilizar a função built-in next() e também o método .next() de um iterator. Já no Python 3 isso mudou, ficamos apenas com a função next().
Python 2:
|
>>> my_generator = (letter for letter in 'abcdefg') >>> my_generator.next() a >>> next(my_generator ) b |
Python 3:
|
>>> my_generator = (letter for letter in 'abcdefg') >>> my_generator.next() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-36-125f388bb61b> in <module>() ----> 1 my_generator.next() AttributeError: 'generator' object has no attribute 'next' >>> next(my_generator ) a |
O método has_key(), aquele dos dicionários, não existe mais:
Em Python 2, poderia ser feito:
|
>>> d = {"a": 1, "b": 2} >>> d.has_key("a") >>> True |
No Python 3, temos que usar o operado in:
Variáveis de controle em List comprehensions não mais afetam o escopo a sua volta:
Python 2:
|
>>> i = 1 >>> print 'before: i =', i >>> print 'comprehension: ', [i for i in range(5)] >>> print 'after: i =', i |
|
before: i = 1 comprehension: [0, 1, 2, 3, 4] after: i = 4 # repare aqui o valor 4. O valor de i após o loop |
Python 3:
|
>>> i = 1 >>> print('before: i =', i) >>> print('comprehension:', [i for i in range(5)]) >>> print('after: i =', i) |
|
before: i = 1 comprehension: [0, 1, 2, 3, 4] after: i = 1 # repare aqui o valor 1, o mesmo antes de rodar o loop |
Finalmente o tipo enumerado faz parte da biblioteca padrão do Python:
No Python 3.4, agora temos:
|
>>> from enum import Enum >>> class Animal(Enum): DOG = 1 CAT = 2 >>> Animal.DOG <Animal.DOG: 1> >>> Animal.DOG.value 1 |
O raw_input não existe mais:
No Python 2.x temos o raw_input e o input para fazer a leitura dos dados do teclado. O raw_input lê strings em geral e o input lê apenas números.
No python 3 existe apenas o input que lê tudo como strings, então se você precisa de uma entrada numérica basta converter assim:
|
>>> dia = int(input('Digite o dia do seu nascimento:')) >>> Digite o dia do seu nascimento: >>> type(dia) >>> <class 'int'> |
Nome de variáveis unicode:
|
>>> évariavel = “será que funciona” >>> évariavel “será que funciona” # -> funciona >>> import math >>> π = math.pi >>> π 3.141592653589793 |
Valeuuuu,
Referências que usei para criar este post:
https://docs.python.org/3/whatsnew/3.0.html
https://asmeurer.github.io/python3-presentation/slides.html#1
https://pythonhelp.wordpress.com/2013/09/01/o-que-mudou-no-python-3/
http://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html#future_module