Home

Ключевые различия между Python 2.7.x и Python 3.x

Многие начинающие пользователи Python интересуются с какой версии Python они должны начать. Мой ответ на этот вопрос, как правило, что-то вроде «просто начните с версии о которой написан ваш любимый учебник, а потом просто проверьте различия».

Но что делать, если вы начинаете новый проект и у вас есть выбор? Я бы сказал, что в настоящее время нет «правильных» или «неправильных» решений до тех пор, как и Python 2.7.x и Python 3.x поддерживают библиотеки, которые вы планируете использовать. Тем не менее стоит взглянуть на главные различия между этими двумя наиболее популярными версиями Python, чтобы избежать распространенных ошибок при написании кода для одной из них, или если вы планируете портировать ваш проект.

Модуль __future__

Python 3.x внес некоторые ключевые слова и функции несовместимые с Python 2, которые могут быть импортированы в Python 2 с помощью встроенного модуля __future__. Рекомендуется использовать __future__, если вы планируете поддержку Python 3.x для вашего кода. Например, если вы хотите, чтобы целочисленное деление в Python 2 осуществлялось как в Python 3.x, вы можете импортировать его через

from __future__ import division

 

Дополнительные функции, которые могут быть импортированы из модуля __future__ приведены в таблице ниже. В столбике Версия1 указаны :

 

feature

Выборочно в

Обязательно в

Эффект

nested_scopes

2.1.0b1

2.2

PEP 227: Статистически вложенные области видимости

generators

2.2.0a1

2.3

PEP 255: Простые генераторы

division

2.2.0a2

3.0

PEP 238: Изменение оператора деления

absolute_import

2.5.0a1

3.0

PEP 328: Импортирует: Multi-Line и Absolute/Relative

with_statement

2.5.0a1

2.6

PEP 343: Выражение “with”

print_function

2.6.0a2

3.0

PEP 3105Делает print функцией

unicode_literals

2.6.0a2

3.0

PEP 3112: Байтовые литералы в Python 3000

 

In [1]:

from platform import python_version

 

Python 2

In [3]:

print'Python', python_version()

print'Hello, World!'

print('Hello, World!')

print"text",;print'print more text on the same line'

Python 2.7.6

Hello, World!

Hello, World!

text print more text on the same line

 

Python 3

In [2]:

print('Python', python_version())

print('Hello, World!')

print("some text,", end="")

print(' print more text on the same line')

Python 3.4.1

Hello, World!

some text,print more text on the same line

In [3]:

print'Hello, World!'

  File "<ipython-input-3-139a7c5835bd>", line 1

    print'Hello, World!'

                        ^

SyntaxError: invalid syntax

Замечание

Вывод «Hello, World» в Python 2 выглядел вполне «нормально». Однако, если у нас есть несколько объектов внутри скобок, мы создадим кортеж, тогда как выражение print в Python 2 не является вызовом функции.

In [2]:

print'Python', python_version()

print('a','b')

print'a','b'

Python 2.7.7

('a','b')

a b

Целочисленное деление

Это изменение особенно опасно, если вы портируете кода, или, если вы выполняете код Python 3 в Python 2, так как изменения в поведении целочисленного деления часто может остаться незамеченными (не вызвать SyntaxError).

Так что, я все еще склонен использовать float(3)/2 или 3/2.0 вместо 3/2 в моих скриптах Python 3, чтобы сохранить разработчикам Python 2 некоторые проблемы (и наоборот, я рекомендую from __future__ import division в ваш Python 2 скрипт).

Python 2

In [4]:

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

In [4]:

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 есть типы ASCII str(), отдельный Unicode (), но не byte тип.

Теперь, в Python 3, наконец есть строки Unicode (UTF-8) и 2 байтовых класса: byte и bytearray.

Python 2

In [2]:

print'Python', python_version()

Python 2.7.6

In [3]:

print type(unicode('this is like a python3 str type'))

<type 'unicode'>

In [4]:

print type(b'byte type does not exist')

<type 'str'>

In [5]:

print'they are really'+b' the same'

they are really the same

In [7]:

print type(bytearray(b'bytearray oddly does exist though'))

<type 'bytearray'>

 

Python 3

In [6]:

print('Python', python_version())

print('strings are now utf-8 u03BCnicou0394é!')

Python 3.4.1

strings are now utf-8 μnicoΔé!

In [8]:

print('Python', python_version(), end="")

print(' has', type(b' bytes for storing data'))

Python 3.4.1 has <class'bytes'>

In [11]:

print('and Python', python_version(), end="")

print(' also has', type(bytearray(b'bytearrays')))

and Python 3.4.1 also has <class'bytearray'>

In [13]:

'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

Использование xrange () очень популярно в Python 2.x для создания перечисляемых объектов, например, в цикле for или list/set-dictionary.

Поведение очень похоже на генератор («ленивая оценка»), но здесь xrange-итератор не исчерпаем, то есть вы можете проходить бесконечно.

Благодаря такой «ленивой оценке», преимущество обычного range() заключается в том, что xrange (), как правило, быстрее, если необходимо перебрать его только один раз (например, в цикле for). Однако не рекомендуется повторять итерации несколько раз, так как генерация происходит каждый раз, с нуля!

В Python 3, range() была реализована как функция xrange (), так что функция xrange () больше не существует (xrange () вызывает NameError в Python 3).

In [5]:

import timeit

n =10000

deftest_range(n):

    returnfor i in range(n):

        pass

deftest_xrange(n):

    for i in xrange(n):

        pass   

 

Python 2

In [6]:

print'Python', python_version()

print' timing range()'

%timeit test_range(n)

print' timing xrange()'

%timeit test_xrange(n)

Python 2.7.6

timing range()

1000 loops, best of 3:433 µs per loop

timing xrange()

1000 loops, best of 3:350 µs per loop

 

Python 3

In [7]:

print('Python', python_version())

print(' timing range()')

%timeit test_range(n)

Python 3.4.1

timing range()

1000 loops, best of 3:520 µs per loop

In [5]:

print(xrange(10))

---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-5-5d8f9b79ea70>in<module>()

---->1print(xrange(10))

NameError: name 'xrange'isnot defined

Метод __contains__ для объектов range в Python 3

Еще одна вещь, которую стоит отметить, что range получил «новый» метод __contains__ в Python 3.x (благодаря YuChen Ying). Метод __contains__ может значительно ускорять «look up» в Python 3.x для целых и булевых типов.

In [3]:

x =10000000

In [4]:

defval_in_range(x, val):

    return val in range(x)

In [5]:

defval_in_xrange(x, val):

    return val in xrange(x)

In [7]:

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.1

1 loops, best of 3:742 ms per loop

1000000 loops, best of 3:1.19 µs per loop

Основываясь на результатах timeit выше, вы видите, что выполнение «look up» было примерно в 60 000 быстрее, когда он был целого типа, float. Однако, поскольку в Python 2.xу range или xrange нет метода __contains__, скорость «look up» не будет сильно отличаться для integer или float:

In [6]:

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.7

1 loops, best of 3:285 ms per loop

1 loops, best of 3:179 ms per loop

1 loops, best of 3:658 ms per loop

1 loops, best of 3:556 ms per loop

Ниже приведены «доказательства», что метод __contain__ не был пока что добавлен в Python 2.x:

In [8]:

print('Python', python_version())

range.__contains__

Python 3.4.1

Out[8]:

<slot wrapper '__contains__' of 'range' objects>

In [7]:

print'Python', python_version()

range.__contains__

Python 2.7.7

-----------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-7-05327350dafb>in<module>()

      1print'Python', python_version()

---->2 range.__contains__

AttributeError:'builtin_function_or_method' object has no attribute '__contains__'

In [8]:

print'Python', python_version()

xrange.__contains__

Python 2.7.7

-----------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-8-7d1a71bfee8e>in<module>()

      1print'Python', python_version()

---->2 xrange.__contains__

AttributeError: type object 'xrange' has no attribute '__contains__'

Примечание о различиях скорости в Python 2 и 3

Некоторые отметили разницу в скорости между range () в Python 3 и xrange() в Python 2. Так как они реализованы одинаковым способом, можно было бы ожидать, что они будут работать с одинаковой скоростью. Однако разница здесь возникает потому, что Python 3, как правило, имеет тенденцию работать чуть медленнее, чем Python 2.

In [3]:

deftest_while():

    i =0

    while i <20000:

        i +=1

    return

In [4]:

print('Python', python_version())

%timeit test_while()

Python 3.4.1

100 loops, best of 3:2.68 ms per loop

In [6]:

print'Python', python_version()

%timeit test_while()

Python 2.7.6

1000 loops, best of 3:1.72 ms per loop

Инициализация исключений

В то время как Python 2 принимает обе нотации, «старого» и «нового» синтаксиса, Python 3 вызывает SyntaxError, если мы не заключили аргумент исключения в скобках:

Python 2

In [7]:

print'Python', python_version()

Python 2.7.6

In [8]:

raise IOError,"file error"

-----------------------------------------------------------------

IOError                         Traceback (most recent call last)

<ipython-input-8-25f049caebb0>in<module>()

---->1raise IOError,"file error"

IOError: file error

In [9]:

raise IOError("file error")

-----------------------------------------------------------------

IOError                         Traceback (most recent call last)

<ipython-input-9-6f1c43f525b2>in<module>()

---->1raise IOError("file error")

IOError: file error

 

Python 3

In [9]:

print('Python', python_version())

Python 3.4.1

In [10]:

raise IOError,"file error"

  File "<ipython-input-10-25f049caebb0>", line 1

    raise IOError,"file error"

                 ^

SyntaxError: invalid syntax

The proper way to raise an exception in Python 3:

In [11]:

print('Python', python_version())

raise IOError("file error")

Python 3.4.1

-----------------------------------------------------------------

OSError                          Traceback (most recent call last)

<ipython-input-11-c350544d15da>in<module>()

      1print('Python', python_version())

---->2raise IOError("file error")

OSError: file error

Обработка исключений

Также обработка исключений сильно изменилась в Python 3. В Python 3 теперь необходимо использовать ключевое слово «as»

Python 2

In [10]:

print'Python', python_version()

try:

    let_us_cause_a_NameError

except NameError, err:

    print err,'--> our error message'

Python 2.7.6

name 'let_us_cause_a_NameError'isnot defined --> our error message

  

Python 3

In [12]:

print('Python', python_version())

try:

    let_us_cause_a_NameError

except NameError as err:

    print(err,'--> our error message')

Python 3.4.1

name 'let_us_cause_a_NameError'isnot defined --> our error message

Функция next() и метод .next()

Так как next () (.next ()) часто используемая функция (метод), это еще одно изменение синтаксиса (или, скорее, изменение в реализации), которое стоит упомянуть: тогда как в Python 2.7.5 можете использовать синтаксис как функции так и метода, в Python 3 осталась только функция next () (вызов метода .next () вызывает AttributeError).

Python 2

In [11]:

print'Python', python_version()

my_generator =(letter for letter in'abcdefg')

next(my_generator)

my_generator.next()

Python 2.7.6

Out[11]:

'b'

Python 3

In [13]:

print('Python', python_version())

my_generator =(letter for letter in'abcdefg')

next(my_generator)

Python 3.4.1

Out[13]:

'a'

In [14]:

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 и утечка глобального пространства имен

Хорошие новости: в Python 3.x переменные цикла for больше не просачиваются в глобальное пространство имен!

Это относится к изменениям, которые были сделаны в Python 3.x и описаны в What’s New In Python 3.0 следующим образом:

«Для описаний списков больше не будет поддерживать синтаксическая форма [... for var in item1, item2, ...]. Используйте [... for var in (item1, item2, ...)] вместо этого. Также обратите внимание, что списки имеют различную семантику: они ближе к синтаксису для выражения генератора внутри конструктора list(), и, в частности, цикл контролирует  переменные, которые больше не просачиваются в окружающее пространство имен».

Python 2

In [12]:

print'Python', python_version()

i =1

print'before: i =', i

print'comprehension: ',[i for i in range(5)]

print'after: i =', i

Python 2.7.6

before: i =1

comprehension:  [0,1,2,3,4]

after: i =4

Python 3

In [15]:

print('Python', python_version())

i =1

print('before: i =', i)

print('comprehension:',[i for i in range(5)])

print('after: i =', i)

Python 3.4.1

before: i =1

comprehension:[0,1,2,3,4]

after: i =1

Сравнение несортируемых типов

Другое хорошее изменение в Python 3, это то, что TypeError появляется как предупреждение если мы пытаемся сравнить несортируемые типы.

Python 2

In [2]:

print'Python', python_version()

print"[1, 2] > 'foo' = ",[1,2]>'foo'

print"(1, 2) > 'foo' = ",(1,2)>'foo'

print"[1, 2] > (1, 2) = ",[1,2]>(1,2)

Python 2.7.6

[1,2]>'foo'=  False

(1,2)>'foo'=  True

[1,2]>(1,2)=  False

Python 3

In [16]:

print('Python', python_version())

print("[1, 2] > 'foo' = ",[1,2]>'foo')

print("(1, 2) > 'foo' = ",(1,2)>'foo')

print("[1, 2] > (1, 2) = ",[1,2]>(1,2))

Python 3.4.1

---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-16-a9031729f4a0>in<module>()

      1print('Python', python_version())

---->2print("[1, 2] > 'foo' = ",[1,2]>'foo')

      3print("(1, 2) > 'foo' = ",(1,2)>'foo')

      4print("[1, 2] > (1, 2) = ",[1,2]>(1,2))

TypeError: unorderable types: list()> str()

Парсинг пользовательского ввода с помощью input()

К счастью, функция input() была фиксирована в Python 3, так что она всегда хранит данных, вводимых пользователем, как объект str. Для того, чтобы избежать опасного поведения в Python 2 при чтении других типов, кроме строк, мы должны использовать raw_input ().

Python 2

Python 2.7.6

[GCC 4.0.1(Apple Inc. build 5493)] on darwin

Type "help","copyright","credits"or"license"for more information.

>>> 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

Python 3.4.1

[GCC 4.2.1(Apple Inc. build 5577)] on darwin

Type "help","copyright","credits"or"license"for more information.

>>> my_input = input('enter a number: ')

enter a number:123

>>> type(my_input)

<class'str'>

Возврат перечисляемых объектов вместо списков

Как мы уже видели в разделе xrange, некоторые функции и методы сейчас возвращают перечисляемые объекты в Python 3 — вместо списков в Python 2.

Так как мы, как правило, перебираем их только один раз в любом случае, я думаю, что это изменение имеет много смысла, чтобы сохранить память. Тем не менее, также возможно — в отличие от генераторов — перебирать несколько раз, если это необходимо, только это не так эффективно.

А для тех случаев, когда действительно необходим объект list, мы можем просто конвертировать перечисляемый объект в список с помощью функции list().

Python 2

In [2]:

print'Python', python_version()

print range(3)

print type(range(3))

Python 2.7.6

[0,1,2]

<type 'list'>

 

Python 3

In [7]:

print('Python', python_version())

print(range(3))

print(type(range(3)))

print(list(range(3)))

Python 3.4.1

range(0,3)

<class'range'>

[0,1,2]

Функция вывода print

Изменение в синтаксисе print, вероятно, наиболее широко известное изменение, но все-таки это стоит отметить: выражение print в Python 2 было заменено функцией print (), это означает, что мы должны взять в скобки объект, который мы хотим вывести.

В Python 2 нет проблем с дополнительными скобками, но в Python 3 возникает SyntaxError если мы вызвали функцию print без скобок.

Некоторые наиболее часто используемых функции и методы, которые больше не возвращают списки в Python 3:

  •         zip()
  •         map()
  •         filter()
  •         dictionary's .keys() method
  •         dictionary's .values() method
  •         dictionary's .items() method

 

Еще статьи о Python 2 и Python 3

Вот список некоторых хороших статей по поводу Python 2 и Python 3, которые я хотел бы порекомендовать.

// Porting to Python 3

// Pro and anti Python 3

Комментарии