Porównanie wersji 2 i 3 języka Pythona

Python jest językiem programowania, który jest aktywnie rozwijany. Oznacza to, że wciąż wydawane są nowe, usprawnione wersje Pythona. Po Pythonie 2.5 wydano Pythona 2.6. Po 2.6 wydano 2.7 itd.

Regułą jest, że kod działający pod starszą wersją Pythona będzie dalej działał po przejściu na nowszego Pythona. Na przykład kod napisany pod Pythona 2.3 będzie uruchomi się także w Pythonie 2.7. Nazywane jest to kompatybilnością wsteczną.

Wymóg zachowania takiej kompatybilności oznacza, że do języka można tylko dodawać nowe elementy, ale nie można nic usunąć. Np. nie można zerwać z konwencją, że 5/2 == 2 i zastąpić ją intuicyjną zasadą, że 5/2 == 2.5. Nie można wycofać się ze złych decyzji projektowych. Raz wprowadzonej do biblioteki standardowej funkcji nie można później usunąć. Powoduje to ciągłe komplikowanie języka.

Dlatego zdecydowano, że następcą Pythona 2.7 będzie wersja 3.0, która jest niekompatybilna wstecz, ale za to znacząco usprawnia Pythona. Kolejne wersje: 3.1, 3.2, 3.3 itd. są już kompatybilne z każdą wcześniejszą wersją 3.X.

Różnice między Pythonem 2 i 3 nie są diametralne - jest to wciąż ten sam język z niemal identyczną składnią i podobną biblioteką standardową. Wiele prostych programów napisanych pod Pythona 2 można, po niewielkich zmianach, uruchomić także pod Pythonem 3.

Decydując się na wersję Pythona, warto zastanowić się, jakie biblioteki będą potrzebne i sprawdzić, czy zostały one przeportowane do Pythona 3. Jeżeli tak jest, to rekomendowany jest Python 3. W przeciwnym razie należy użyć Pythona 2.

Funkcja print

Najbardziej widoczną różnicą jest fakt, że print przestało być słowem kluczowym i stało się funkcją. Oznacza to, że w Pythonie 3 konieczne jest użycie nawiasów, w przeciwnym razie rezultatem jest błąd:

>>> print("Hello world")
Hello world
>>> print "Hello world"
  File "<ipython-input-4-3c090b498326>", line 1
    print "Hello world"
                      ^
SyntaxError: Missing parentheses in call to 'print'

Dzielenie liczb

Zadbano także o to, aby w Pythonie 3 dzielenie dwóch liczb całkowitych zawsze dawało matematycznie poprawny wynik:

>>> 3 / 2
1.5

Pod Pythonem 2 rezultat jest następujący:

>>> 3 / 2
1

range i xrange

Pod Pythonem 2, dostępne są dwie funkcje do generowania liczb z podanego zakresu:

  • range, które zwraca listę oraz

  • xrange, które zwraca generator.

W przypadku wywołania range(1000000) w pamięci przetrzymywana jest lista zawierająca milion liczb, co zajmuje dużo pamięci.

xrange jest usprawnieniem range i ma tę przewagę, że zachowuje się podobnie do listy, ale generuje kolejne liczby na żądanie.

Pod Pythonem 3 istnieje tylko jedna funkcja range, która zachowuje się jak xrange w Pythonie 2.

Porównywanie obiektów

W Pythonie 2 możliwe jest porównywanie obiektów różnych typów:

>>> (1, 2) > 'foo'
True

Zazwyczaj jednak tego rodzaju porównania świadczą o błędzie w aplikacji, dlatego w Pythonie 3 takie porównanie kończy się błędem:

>>> (1, 2) > 'foo'
Traceback (most recent call last):
  File "<ipython-input-5-29c7ccd4206b>", line 1, in <module>
    (1, 2) > 'foo'
TypeError: unorderable types: tuple() > str()

Pozostałe różnice

Pozostałe ważne różnice między Pythonem 2 i 3 to:

  • Python 3 lepiej wspiera napisy zawierające nietypowe znaki, takie jak np. polskie ogonki.

  • Zmieniono nazwę funkcji raw_input na input. Z kolei starą funkcję input usunięto.

  • Wiele funkcji i metod, które do tej pory zwracały listy i posiadały odpowiedniki zwracające generatory, zostały zastąpione wersjami zwracającymi generatory. Na przykład, w Pythonie 2 funkcja zip zwraca listę, z kolei itertools.izip zwraca generator. W Pythonie 3, dostępna jest tylko jedna funkcja: zip, która zwraca generator.

  • W Pythonie 3, zmienne w wyrażeniach listowych nie mogą nadpisać innych zmiennych. W Pythonie 2 możliwe było przypadkowe nadpisanie zmiennej:

    >>> var = 1
    >>> print([var for var in [7, 2, 4]])
    [7, 2, 4]
    >>> print(var)
    4
    

Powyższa lista nie wyczerpuje wszystkich różnic.

Co nowego w Pythonie 3.1

Pola w funkcji format() dla ciągów znaków są automatycznie numerowane:

>>> 'Sir {} of {}'.format('Gallahad', 'Camelot')
'Sir Gallahad of Camelot'

Wyrażenie with dopuszcza wiele menadżerów kontekstu:

>>> with open('mylog.txt') as infile, open('a.out', 'w') as outfile:
...     for line in infile:
...         if '<critical>' in line:
...             outfile.write(line)

Co nowego w Pythonie 3.2

Nowy moduł - concurrent.futures.

Wraca wbudowana funkcja callable().

>>> callable(max)
True
>>> callable(20)
False

Co nowego w Pythonie 3.3

Nowe wyrażenie yeld from

>>> def g(x):
...     yield from range(x, 0, -1)
...     yield from range(x)
...
>>> list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

Co nowego w Pythonie 3.4

Bez zmian w składni.

Nowe biblioteki, w szczególności asyncio i enum

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

Co nowego w Pythonie 3.5

Nowy operator mnożenia macierzy: a @ b

Podpowiedzi typów (type hints):

def greeting(name: str) -> str:

return «Hello « + name

Co nowego w Pythonie 3.6

Formatowane ciągi znaków (Formatted string literals):

>>> name = "Fred"
>>> f"He said his name is {name}."
'He said his name is Fred.'
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

Co nowego w Pythonie 3.7

Słowa kluczowe async i await.

Nowy dekorator dataclass do tworzenia klas przechowujących dane.

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0

p = Point(1.5, 2.5)
print(p)   # produces "Point(x=1.5, y=2.5, z=0.0)"

Co nowego w Pythonie 3.8

Nowy operator := „the walrus operator”. Przydatny w porównaniach i przypisaniach.

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

Co nowego w Pythonie 3.9

Głownie zmiany w bibliotekach.