Kluczowe różnice między Pythonem 2.7.x a Pythonem 3.x z przykładami

Wielu początkujących użytkowników Pythona zastanawia się, od której wersji Pythona powinni zacząć. Moją odpowiedzią na to pytanie jest zazwyczaj coś w stylu „po prostu idź z wersją, w której został napisany twój ulubiony tutorial i sprawdź różnice później.”

Ale co jeśli zaczynasz nowy projekt i masz wybór? Powiedziałbym, że obecnie nie ma „dobrego” lub „złego” wyboru, o ile Python 2.7.x i Python 3.x obsługują biblioteki, których zamierzasz używać. Warto jednak przyjrzeć się głównym różnicom pomiędzy tymi dwoma najpopularniejszymi wersjami Pythona, aby uniknąć typowych pułapek podczas pisania kodu dla którejś z nich, lub jeśli planujesz przeportować swój projekt.

Sekcje

  • Sekcje
  • Moduł __future__
  • Funkcja print
    • Python 2
    • Python 3
  • Działanie liczb całkowitych.
    • Python 2
    • Python 3
  • Unicode
    • Python 2
    • Python 3
  • xrange
    • Python 2
    • Python 3
    • The __contains__ metoda dla range obiektów w Pythonie 3
      • Uwaga o różnicach w szybkości w Pythonie 2 i 3
  • Podnoszenie wyjątków
    • Python 2
    • Python 3
  • Obsługiwanie wyjątków
    • Python 2
    • Python 3
    • Python 2
    • Python 3
  • Zmienne pętli for-.pętli i wyciek globalnej przestrzeni nazw
    • Python 2
    • Python 3
  • Porównanie typów nieuporządkowanych
    • Python 2
    • Python 3
  • Porównanie typów nieuporządkowanych
  • Python 2
  • Python 3
  • Parsowanie danych wejściowych użytkownika za pomocą input()
    • Python 2
    • Python 3
  • Zwracanie obiektów iterowalnych zamiast obiektów iterowalnych. Zwracanie obiektów iterowalnych zamiast list
    • Python 2
    • Python 3
  • Zaokrąglanie bankiera
    • Python 2
    • Python 3
  • Więcej artykułów o Pythonie 2 i Pythonie 3
  • Moduł __future__

    Python 3.x wprowadził pewne słowa kluczowe i funkcje niekompatybilne z Pythonem 2, które można zaimportować za pomocą wbudowanego modułu __future__ w Pythonie 2. Zaleca się używanie importu __future__, jeśli planujesz wsparcie Pythona 3.x dla swojego kodu. Na przykład, jeśli chcemy, aby Python 3.x’s integer division behavior w Pythonie 2, możemy go zaimportować poprzez

    from __future__ import division

    Więcej funkcji, które można zaimportować z modułu __future__ wymieniono w poniższej tabeli:

    feature optional in mandatory in effect
    nested_scopes 2.1.0b1 2.2 PEP 227:Statically Nested Scopes
    generators 2.2.0a1 2.3 PEP 255:Simple Generators
    division 2.2.0a2 3.0 PEP 238:Changing the Division Operator
    absolute_import 2.5.0a1 3.0 PEP 328:Imports: Multi-Line and Absolute/Relative
    with_statement 2.5.0a1 2.6 PEP 343:The „with” Statement
    print_function 2.6.0a2 3.0 PEP 3105:Make print a function
    unicode_literals 2.6.0a2 3.0 PEP 3112:Bytes literals in Python 3000

    (Źródło: (https://docs.python.org/2/library/__future__.html#module-__future

    from platform import python_version

    Funkcja print

    Bardzo trywialna, a zmiana w składni print jest prawdopodobnie najbardziej znaną zmianą, ale mimo to warto o niej wspomnieć: Instrukcja print w Pythonie 2 została zastąpiona funkcją print(), co oznacza, że musimy zawinąć obiekt, który chcemy wydrukować w parantele.

    Python 2 nie ma problemu z dodatkowymi nawiasami, ale w przeciwieństwie do niego Python 3 podniósłby SyntaxError, gdybyśmy wywołali funkcję print w Pythonie 2 bez nawiasów.

    Python 2

    print 'Python', python_version()print 'Hello, World!'print('Hello, World!')print "text", ; print 'print more text on the same line'
    Python 2.7.6Hello, World!Hello, World!text print more text on the same line

    Python 3

    print('Python', python_version())print('Hello, World!')print("some text,", end="")print(' print more text on the same line')
    Python 3.4.1Hello, World!some text, print more text on the same line
    print 'Hello, World!'
     File "<ipython-input-3-139a7c5835bd>", line 1 print 'Hello, World!' ^SyntaxError: invalid syntax

    Uwaga:

    Wydrukowanie „Hello, World” powyżej za pomocą Pythona 2 wyglądało całkiem „normalnie”. Jednakże, jeśli mamy wiele obiektów wewnątrz paranteli, utworzymy tuple, ponieważ print jest „oświadczeniem” w Pythonie 2, a nie wywołaniem funkcji.

    print 'Python', python_version()print('a', 'b')print 'a', 'b'
    Python 2.7.7('a', 'b')a b

    Działanie liczb całkowitych

    Ta zmiana jest szczególnie niebezpieczna, jeśli portujesz kod, lub jeśli wykonujesz kod Pythona 3 w Pythonie 2, ponieważ zmiana w zachowaniu dzielenia liczb całkowitych może często pozostać niezauważona (nie powoduje wyświetlenia komunikatu SyntaxError).
    Więc nadal mam tendencję do używania float(3)/2 lub 3/2.0 zamiast 3/2 w moich skryptach Pythona 3, aby zaoszczędzić chłopakom z Pythona 2 trochę kłopotów (i vice versa, Polecam from __future__ import division w twoich skryptach Pythona 2).

    Python 2

    print 'Python', python_version()print '3 / 2 =', 3 / 2print '3 // 2 =', 3 // 2print '3 / 2.0 =', 3 / 2.0print '3 // 2.0 =', 3 // 2.0
    Python 2.7.63 / 2 = 13 // 2 = 13 / 2.0 = 1.53 // 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.13 / 2 = 1.53 // 2 = 13 / 2.0 = 1.53 // 2.0 = 1.0

    Unicode

    Python 2 posiada typy ASCII str(), oddzielne typy unicode(), ale nie ma typu byte.

    Teraz, w Pythonie 3, mamy wreszcie Unicode (utf-8) strings, i 2 klasy bajtów: byte i bytearrays.

    Python 2

    print 'Python', python_version()
    Python 2.7.6
    print type(unicode('this is like a python3 str type'))
    <type 'unicode'>
    print type(b'byte type does not exist')
    <type 'str'>
    print 'they are really' + b' the same'
    they are really the same
    print type(bytearray(b'bytearray oddly does exist though'))
    <type 'bytearray'>

    Python 3

    print('Python', python_version())print('strings are now utf-8 \u03BCnico\u0394é!')
    Python 3.4.1strings are now utf-8 μnicoΔé!
    print('Python', python_version(), end="")print(' has', type(b' bytes for storing data'))
    Python 3.4.1 has <class 'bytes'>
    print('and Python', python_version(), end="")print(' also has', type(bytearray(b'bytearrays')))
    and Python 3.4.1 also has <class 'bytearray'>
    'note that we cannot add a string' + b'bytes for data'
    ---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-13-d3e8942ccf81> in <module>()----> 1 'note that we cannot add a string' + b'bytes for data'TypeError: Can't convert 'bytes' object to str implicitly

    xrange

    Użycie xrange() jest bardzo popularne w Pythonie 2.x do tworzenia obiektu iterowalnego, np, w pętli for lub list/set-dictionary-comprehension.
    Zachowanie było dość podobne do generatora (tj, „lazy evaluation”), ale w tym przypadku xrange-iterable nie jest wyczerpywalny – co oznacza, że można nad nim iterować w nieskończoność.

    Dzięki „lazy-evaluation”, zaletą zwykłego range() jest to, że xrange() jest generalnie szybszy, jeśli trzeba nad nim iterować tylko raz (np. w pętli for). Jednak w przeciwieństwie do iteracji 1-krotnych, nie jest to zalecane, jeśli powtarzasz iterację wiele razy, ponieważ generowanie odbywa się za każdym razem od zera!

    W Pythonie 3, funkcja range() została zaimplementowana jak funkcja xrange(), dzięki czemu dedykowana funkcja xrange() już nie istnieje (xrange() podnosi NameError w Pythonie 3).

    import timeitn = 10000def test_range(n): return for i in range(n): passdef test_xrange(n): for i in xrange(n): pass 

    Python 2

    print 'Python', python_version()print '\ntiming range()'%timeit test_range(n)print '\n\ntiming xrange()'%timeit test_xrange(n)
    Python 2.7.6timing range()1000 loops, best of 3: 433 µs per looptiming xrange()1000 loops, best of 3: 350 µs per loop

    Python 3

    print('Python', python_version())print('\ntiming range()')%timeit test_range(n)
    Python 3.4.1timing range()1000 loops, best of 3: 520 µs per loop
    print(xrange(10))
    ---------------------------------------------------------------------------NameError Traceback (most recent call last)<ipython-input-5-5d8f9b79ea70> in <module>()----> 1 print(xrange(10))NameError: name 'xrange' is not defined

    Metoda __contains__ dla obiektów zakresu w Pythonie 3

    Inną rzeczą, o której warto wspomnieć jest to, że range dostał „nową” __contains__ metodę w Pythonie 3.x (dzięki Yuchen Ying, który zwrócił na to uwagę). Metoda __contains__ może przyspieszyć „look-ups” w Pythonie 3.x range znacznie dla typów całkowitych i Boolean.

    x = 10000000
    def val_in_range(x, val): return val in range(x)
    def val_in_xrange(x, val): return val in xrange(x)
    print('Python', python_version())assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_range(x, x/2)%timeit val_in_range(x, x//2)
    Python 3.4.11 loops, best of 3: 742 ms per loop1000000 loops, best of 3: 1.19 µs per loop

    Na podstawie timeit wyników powyżej, widać, że wykonanie dla „look up” było o około 60 000 szybsze, gdy był to typ integer, a nie float. Jednakże, ponieważ Python 2.x’s range lub xrange nie ma metody __contains__, „prędkość wyszukiwania” nie byłaby tak bardzo różna dla liczb całkowitych lub pływaków:

    print 'Python', python_version()assert(val_in_xrange(x, x/2.0) == True)assert(val_in_xrange(x, x/2) == True)assert(val_in_range(x, x/2) == True)assert(val_in_range(x, x//2) == True)%timeit val_in_xrange(x, x/2.0)%timeit val_in_xrange(x, x/2)%timeit val_in_range(x, x/2.0)%timeit val_in_range(x, x/2)
    Python 2.7.71 loops, best of 3: 285 ms per loop1 loops, best of 3: 179 ms per loop1 loops, best of 3: 658 ms per loop1 loops, best of 3: 556 ms per loop

    Poniżej „dowody”, że metoda __contain__ nie została dodana do Pythona 2.x jeszcze:

    print('Python', python_version())range.__contains__
    Python 3.4.1<slot wrapper '__contains__' of 'range' objects>
    print 'Python', python_version()range.__contains__
    Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-7-05327350dafb> in <module>() 1 print 'Python', python_version()----> 2 range.__contains__AttributeError: 'builtin_function_or_method' object has no attribute '__contains__'
    print 'Python', python_version()xrange.__contains__
    print 'Python', python_version()xrange.__contains__
    Python 2.7.7---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-8-7d1a71bfee8e> in <module>() 1 print 'Python', python_version()----> 2 xrange.__contains__AttributeError: type object 'xrange' has no attribute '__contains__'

    Uwaga na temat różnic prędkości w Pythonie 2 i 3

    Niektórzy zwrócili uwagę na różnicę prędkości między Pythonem 3’s range() i Pythonem2’s xrange(). Ponieważ są one zaimplementowane w ten sam sposób, można by się spodziewać tej samej prędkości. Jednak różnica tutaj po prostu pochodzi z faktu, że Python 3 ogólnie ma tendencję do działania wolniej niż Python 2.

    def test_while(): i = 0 while i < 20000: i += 1 return
    print('Python', python_version())%timeit test_while()
    Python 3.4.1100 loops, best of 3: 2.68 ms per loop
    print 'Python', python_version()%timeit test_while()
    Python 2.7.61000 loops, best of 3: 1.72 ms per loop

    Raising exceptions

    Gdzie Python 2 akceptuje obie notacje, starą” i „nową” składnię, Python 3 dławi się (i podnosi z kolei SyntaxError), jeśli nie zawrzemy argumentu wyjątku w nawiasach:

    Python 2

    print 'Python', python_version()
    Python 2.7.6
    raise IOError, "file error"
    ---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-8-25f049caebb0> in <module>()----> 1 raise IOError, "file error"IOError: file error
    raise IOError("file error")
    ---------------------------------------------------------------------------IOError Traceback (most recent call last)<ipython-input-9-6f1c43f525b2> in <module>()----> 1 raise IOError("file error")IOError: file error

    Python 3

    print('Python', python_version())
    Python 3.4.1
    raise IOError, "file error"
     File "<ipython-input-10-25f049caebb0>", line 1 raise IOError, "file error" ^SyntaxError: invalid syntax

    Prawidłowy sposób zgłaszania wyjątków w Pythonie 3:

    print('Python', python_version())raise IOError("file error")
    Python 3.4.1---------------------------------------------------------------------------OSError Traceback (most recent call last)<ipython-input-11-c350544d15da> in <module>() 1 print('Python', python_version())----> 2 raise IOError("file error")OSError: file error

    Obsługa wyjątków

    W Pythonie 3 zmieniła się również nieco obsługa wyjątków. W Pythonie 3 musimy teraz używać słowa kluczowego „as

    Python 2

    print 'Python', python_version()try: let_us_cause_a_NameErrorexcept NameError, err: print err, '--> our error message'
    Python 2.7.6name 'let_us_cause_a_NameError' is not defined --> our error message

    Python 3

    print('Python', python_version())try: let_us_cause_a_NameErrorexcept NameError as err: print(err, '--> our error message')
    Python 3.4.1name 'let_us_cause_a_NameError' is not defined --> our error message

    Ponieważ next().next()) jest tak powszechnie używaną funkcją (metodą), jest to kolejna zmiana składni (a raczej zmiana w implementacji), o której warto wspomnieć: gdzie można używać zarówno składni funkcji, jak i metody w Pythonie 2.7.5, funkcja next() jest wszystkim, co pozostało w Pythonie 3 (wywołanie metody .next() podnosi AttributeError).

    Python 2

    print 'Python', python_version()my_generator = (letter for letter in 'abcdefg')next(my_generator)my_generator.next()
    Python 2.7.6'b'

    Python 3

    print('Python', python_version())my_generator = (letter for letter in 'abcdefg')next(my_generator)
    Python 3.4.1'a'
    my_generator.next()
    ---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-14-125f388bb61b> in <module>()----> 1 my_generator.next()AttributeError: 'generator' object has no attribute 'next'

    For-…loop variables and the global namespace leak

    Dobra wiadomość to: W Pythonie 3.x zmienne for-loop nie wyciekają już do globalnej przestrzeni nazw!

    Wraca to do zmiany, która została wprowadzona w Pythonie 3.x i jest opisana w What’s New In Python 3.0 w następujący sposób:

    „List comprehensions nie obsługują już formy składniowej . Zamiast tego należy użyć . Zauważ też, że list comprehensions mają inną semantykę: są bliższe syntaktycznemu cukrowi dla wyrażenia generatora wewnątrz konstruktora list(), a w szczególności zmienne kontrolne pętli nie są już wyciekane do otaczającego zakresu.”

    Python 2

    print 'Python', python_version()i = 1print 'before: i =', iprint 'comprehension: ', print 'after: i =', i
    Python 2.7.6before: i = 1comprehension: after: i = 4

    Python 3

    print('Python', python_version())i = 1print('before: i =', i)print('comprehension:', )print('after: i =', i)
    Python 3.4.1before: i = 1comprehension: after: i = 1

    Porównywanie typów nieuporządkowanych

    Kolejną miłą zmianą w Pythonie 3 jest to, że TypeError jest podnoszony jako ostrzeżenie, jeśli próbujemy porównać typy nieuporządkowane.

    Python 2

    print 'Python', python_version()print " > 'foo' = ", > 'foo'print "(1, 2) > 'foo' = ", (1, 2) > 'foo'print " > (1, 2) = ", > (1, 2)
    Python 2.7.6 > 'foo' = False(1, 2) > 'foo' = True > (1, 2) = False

    Python 3

    print('Python', python_version())print(" > 'foo' = ", > 'foo')print("(1, 2) > 'foo' = ", (1, 2) > 'foo')print(" > (1, 2) = ", > (1, 2))
    Python 3.4.1---------------------------------------------------------------------------TypeError Traceback (most recent call last)<ipython-input-16-a9031729f4a0> in <module>() 1 print('Python', python_version())----> 2 print(" > 'foo' = ", > 'foo') 3 print("(1, 2) > 'foo' = ", (1, 2) > 'foo') 4 print(" > (1, 2) = ", > (1, 2))TypeError: unorderable types: list() > str()

    Parsing user inputs via input()

    Na szczęście, funkcja input() została naprawiona w Pythonie 3 tak, że zawsze przechowuje dane wejściowe użytkownika jako obiekty str. Aby uniknąć niebezpiecznego zachowania w Pythonie 2, polegającego na wczytywaniu innych typów niż strings, musimy zamiast tego użyć raw_input().

    Python 2

    Python 2.7.6 na darwinType "help", "copyright", "credits" lub "license", aby uzyskać więcej informacji.>>> my_input = input('wpisz liczbę: ')wpisz liczbę: 123>>> type(my_input)<type. 'int'>>>> my_input = raw_input('wprowadź liczbę: ')enter a number: 123>>> type(my_input)<type 'str'>

    Python 3

    Python 3.4.1 na darwinType "help", "copyright", "credits" lub "license", aby uzyskać więcej informacji.>>> my_input = input('wpisz liczbę: ')wpisz liczbę: 123>>> type(my_input)<class 'str'>

    Wracanie obiektów iterowalnych zamiast list

    Jak już widzieliśmy w sekcji xrange, niektóre funkcje i metody zwracają teraz obiekty iterowalne w Pythonie 3 – zamiast list w Pythonie 2.

    Ponieważ zazwyczaj i tak iterujemy po nich tylko raz, myślę, że ta zmiana ma sens, aby zaoszczędzić pamięć. Jednakże, jest również możliwe – w przeciwieństwie do generatorów – wielokrotne iterowanie nad nimi w razie potrzeby, tylko nie jest to tak wydajne.

    A dla tych przypadków, gdy naprawdę potrzebujemy obiektów list, możemy po prostu przekonwertować obiekt iterowalny na list poprzez funkcję list().

    Python 2

    print 'Python', python_version()print range(3)print type(range(3))
    Python 2.7.6<type 'list'>

    Python 3

    print('Python', python_version())print(range(3))print(type(range(3)))print(list(range(3)))
    Python 3.4.1range(0, 3)<class 'range'>

    Kilka częściej używanych funkcji i metod, które nie zwracają już list w Pythonie 3:

    • zip()

    • map()

    • filter()

    • metoda słownika .keys()

    • metoda słownika .values() method

    • dictionary’s .items() method

    Banker’s Rounding

    Python 3 zaadoptował obecnie standardowy sposób zaokrąglania wartości dziesiętnych, gdy skutkuje to remisem (.5) na ostatnich znaczących cyfrach. Teraz, w Pythonie 3, liczby dziesiętne są zaokrąglane do najbliższej liczby parzystej. Chociaż jest to niedogodność dla przenośności kodu, jest to podobno lepszy sposób zaokrąglania w porównaniu do zaokrąglania w górę, ponieważ unika stronniczości w kierunku dużych liczb. Aby uzyskać więcej informacji, zobacz doskonałe artykuły i akapity Wikipedii:

    • https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
    • https://en.wikipedia.org/wiki/IEEE_floating_point#Roundings_to_nearest

    Python 2

    .

    print 'Python', python_version()
    Python 2.7.12
    round(15.5)
    16.0
    round(16.5)
    17.0

    Python 3

    print('Python', python_version())
    Python 3.5.1
    round(15.5)
    16
    round(16.5)
    16

    Więcej artykułów o Pythonie 2 i Pythonie 3

    Oto lista kilku dobrych artykułów dotyczących Pythona 2 i 3, które poleciłbym jako kontynuację.up.

    // Portowanie do Pythona 3

    • Czy powinienem używać Pythona 2 czy Pythona 3 do moich działań programistycznych?

    • Co nowego w Pythonie 3.0

    • Portowanie do Python 3

    • Portowanie kodu Python 2 do Python 3

    • Jak utrzymać Python 3 idzie do przodu

    // Pro i anty Python 3

    • 10 wspaniałych funkcji Pythona, których nie możesz używać ponieważ odmawiasz uaktualnienia do Pythona 3

    • Wszystko, czego nie chciałeś wiedzieć o Unicode w Pythonie 3

    • Wszystko, czego nie chciałeś wiedzieć o Unicode w Pythonie 3

    • Python 3 zabija Pythona

    • Python 3 może ożywić Pythona

    • Python 3 jest w porządku

    Dodaj komentarz

    Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *