Python 3
фичи и проблемы портирования


        Михаил Коробов,
     EKBPY, 10 февраля 2012
Немного истории
● апрель 2006 - PEP 3000
● декабрь 2008 - Python 3.0
● июль 2010 - последний релиз с новыми
  фичами в ветке 2.х

● май 2011 - закончилась поддержка 2.5
  (больше нет даже обновлений
  безопасности)
● 2013 - закончится поддержка 2.6
● 2016 (?) - закончится поддержка 2.7
Зачем переходить на 3.x?
Когда переходить на 3.x?
 Переходить ли на 3.х?
    Как переходить?
Нет и не будет в Python 2.x
Улучшения в стандартной
библиотеке


    В 2.x с лета 2010 года (выход 2.7)
улучшений в стандартной библиотеке нет.
            И не будет больше.
      Только исправление ошибок.
Unicode
● 2.x: неявное преобразование между
  юникодными и байтовыми строками
  иногда вызывает исключение.

● 3.х: неявное преобразование между
  юникодными и байтовыми строками
  всегда вызывает исключение.

см. также: https://github.com/mitsuhiko/unicode-nazi
Unicode
● вся стандартная библиотека работает с
  юникодом (см., например, csv в 2.x)

● # --* coding: utf-8 *--

● def вася(параметр1, параметр2): pass

3.3 (выйдет этим летом): более
эффективное хранение unicode-строк
Ленивость по умолчанию
xrange() -> range()
.iteritems() -> .items()
izip() -> zip()

и т.д.
Классы
● Больше нет разделения на old-style и
  new-style;

● другой синтаксис для метаклассов (плюс
  поддержка kwargs в объявлении классов);

● super

cм. https://github.com/rfk/magicsuper и http://pypi.python.
org/pypi/newsuper
Классы: 2.x
class Foo(object):
    def do(self):
        return 41

class Bar(Foo):
    __metaclass__ = Meta

    def do(self):
        old = super(Bar, self).do()
        return old + 1
Классы: 3.x
class Foo:
    def do(self):
        return 41

class Bar(Foo, metaclass=Meta):
    def do(self):
        old = super().do()
        return old + 1
Аннотации
def foo(x: int, y: int, *args: str)-> int:
    pass

def bar(x: "широта в градусах"=60):
    pass

foo.__annotations__
bar.__annotations__
nonlocal
Javascript:

  function foo(){
    for (i=0; i<10; i++) {}
    // OMG, меняется window.i
  }
nonlocal
Python:

  x = 5
  def foo():
      x = 1
  foo()
  # x все еще 1
nonlocal
 def bar():
     x = 5
     def foo():
         x += 1
     foo()
     return x
 bar()
 # упадет с UnboundLocalError: local
 variable 'x' referenced before assignment
nonlocal (2.x)
  def bar():
      x = {'val': 5} # хак?
      def foo():
          x['val'] += 1
      foo()
      return x['val']
  bar()
  # вернет 6
nonlocal (3.x)
  def bar():
      x = 5
      def foo():
          nonlocal x
          x += 1
      foo()
      return x
  bar()
  # вернет 6
Функции без позиционных
аргументов
def foo(*, x):
    pass

foo(x=1) # работает
foo(1) # упадет с TypeError
Исключения
try:
    foo()
except ExcClass as e:
    print(e.__traceback__)
    print(e.__context__)
    print(e.__cause__)
    raise NewExcClass from e
Присваивание
>>> a, *rest, b = range(5)
>>> rest
[1, 2, 3]
Потоки (threads)

● Более быстрый GIL (Python 3.2);

● Исправлены некоторые недочеты
  реализации в 2.х - например, в 2.x поток,
  застрявший в deadlock'е, нельзя убить по
  SIGINT (Ctrl-C), а в 3.2 - можно.
yield from
# python 2.x

def gen1():
    yield 'foo'
    yield 'bar'

def gen2():
    for x in gen():
        yield x
yield from
# python 3.3
def gen1():
    yield 'foo'
    yield 'bar'
    return 42

def gen2():
    return yield from gen1()

правильная обработка исключений, возврат
значений
yield from: зачем?
С помощью yield можно писать на Python
асинхронный код в синхронном стиле (без
вороха callback'ов); yield from делает это
проще.

См. также:
● twisted.internet.defer.deferredGenerator;
● tornado.gen;
● https://launchpad.net/adisp
Есть в Python 2.x:
С помощью __future__
●   absolute_import
●   print_function
●   unicode_literals
●   division
Стандартная библиотека
● Большинство изменений из 3.0 и 3.1
● часть изменений из 3.2.

Со сторонними пакетами:
● futures
● distutils2
Синтаксис (2.7)
● dict comprehensions

{i: foo(i) for i in range(10)}

● set lterals

{1, 2, 3}
Библиотеки
январь 2012 г., количество библиотек на
pypi:

● 2.x: >18000
● 3.x: <1000
Стратегии перехода
Просто взять и переписать на 3.х
● (+) лучшая стратегия перевода "своего"
  кода;
● (-) нужно переписывать весь код на 3.х;
● (-) нельзя использовать сторонний код,
  написанный для 2.х;
● (-) не подходит для open-source
  библиотек, которым важна обратная
  совместимость (привет dateutil)
Библиотеки
● Чтоб на практике использовать Python 3,
  нужны библиотеки, поддерживающие
  Python 3;
● переписать библиотеки с 2.х на 3.х
  нельзя, т.к. большая часть кода пишется
  на 2.х и поэтому поддержка 2.х важна.
● Чтобы писать код на Python 3 и
  использовать сторонние наработки, нужно
  уметь добавлять поддержку 3.х в код на 2.
  х.
Проблема не в портировании
своего кода (взял да переписал),
   проблема в портировании
          библиотек
Экзотические способы
● писать на Cython (пример: lxml - большая
  часть написана на Cython и поэтому
  поддерживает и 2.x, и 3.х)

● пожертвовать 100k$ pypy и, возможно,
  через год-другой получить интерпретатор,
  который умеет как 2.х, так и 3.х
  одновременно.
Практические способы
● 2to3

● 2+3
2to3
Берет код на 2.х и преобразует его в код на
3.х автоматически.

● Можно применить 1 раз и работать потом
  только с 3.х кодом;

● можно вести разработку на 2.х и получать
  версию 3.х при необходимости (setup.py).
2to3: кто использует
●   tornado;
●   jinja2;
●   SQLAlchemy;
●   celery;
●   mako;
●   nose;
●   ipython;
●   Sphinx;
●   numpy;
●   psycopg2;
●   ...
2to3: плюсы
● Входит в стандартную поставку и
  рекомендуется в PEP-3000;
● автоматически генерирует 3.х код из 2.х
  кода;
● просто интегрируется с setup.py.
2to3: минусы
● работает не всегда корректно, требует
  ручного вмешательства или написания
  своих "фиксеров";
● медленно работает;
● затруднена отладка при ошибках в 3.х
  коде;
● при использовании в авто-режиме
  разработка все еще ведется на 2.х
3to2
2to3 наоборот: код пишется на 3.х,
автоматически генерируется версия для 2.х

● не входит в стандартную поставку;
● код пишется на 3.х, а не на 2.х;
● мало кто использует на практике.
Можно писать код, который
работает как на 2.х, так и на 3.х
Код, который работает как на 2.х,
так и на 3.х
●   pyramid (1.3);
●   django (trunk сразу после релиза 1.4);
●   pip;
●   virtualenv;
●   WebOb;
●   coverage;
●   requests;
●   python-код из lxml;
●   whoosh;
●   pytz;
●   ...
Код, который работает как на 2.х,
так и на 3.х
● разработка ведется с использованием
  подмножества python 2.x, совместимого с
  3.х.

● отсутствует шаг с автоматической
  генерацией кода, поэтому проще отладка
  и разработка;

● поддержка python 2.5 требует хаков;
Код, который работает как на 2.х,
так и на 3.х
● Библиотека six (около 300 строк):

if PY3:
    text_type = str
else:
    text_type = unicode
# ...

● вся six целиком редко нужна (пример:
  pymorphy.py3k - это 6 строк кода из six)
Необходимые условия для
портирования
● Автоматизированные тесты;

● по возможности, современные версии
  языковых конструкций (желательно 2.6+);

● четкая стратегия работы с unicode;
Один из возможных алгоритмов
1.   Удостовериться, что тестов достаточно;
2.   настроить tox;
3.   перестать поддерживать python 2.5;
4.   заставить весь код работать с
from __future__ import (absolute_import,
unicode_literals, print_function, division)
5. "внутри" использовать только unicode-
строки;
6. чинить ошибки в 3.х с помощью six, пока
все не заработать и под 2, и под 3.
Больше ссылок
●   http://wiki.python.org/moin/PortingPythonToPy3k
●   https://code.djangoproject.com/wiki/PortingNotesFor2To3
●   http://python3porting.com/
●   http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/
●   http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/
●   http://docs.pythonsprints.com/python3_porting/index.html
●   http://docs.python.org/release/3.0.1/whatsnew/3.0.html (там внизу -
    способ портирования на python3 от Guido van Rossum)
Спасибо.

ekbpy'2012 - Михаил Коробов - Python 3

  • 1.
    Python 3 фичи ипроблемы портирования Михаил Коробов, EKBPY, 10 февраля 2012
  • 2.
    Немного истории ● апрель2006 - PEP 3000 ● декабрь 2008 - Python 3.0 ● июль 2010 - последний релиз с новыми фичами в ветке 2.х ● май 2011 - закончилась поддержка 2.5 (больше нет даже обновлений безопасности) ● 2013 - закончится поддержка 2.6 ● 2016 (?) - закончится поддержка 2.7
  • 3.
    Зачем переходить на3.x? Когда переходить на 3.x? Переходить ли на 3.х? Как переходить?
  • 4.
    Нет и небудет в Python 2.x
  • 5.
    Улучшения в стандартной библиотеке В 2.x с лета 2010 года (выход 2.7) улучшений в стандартной библиотеке нет. И не будет больше. Только исправление ошибок.
  • 6.
    Unicode ● 2.x: неявноепреобразование между юникодными и байтовыми строками иногда вызывает исключение. ● 3.х: неявное преобразование между юникодными и байтовыми строками всегда вызывает исключение. см. также: https://github.com/mitsuhiko/unicode-nazi
  • 7.
    Unicode ● вся стандартнаябиблиотека работает с юникодом (см., например, csv в 2.x) ● # --* coding: utf-8 *-- ● def вася(параметр1, параметр2): pass 3.3 (выйдет этим летом): более эффективное хранение unicode-строк
  • 8.
    Ленивость по умолчанию xrange()-> range() .iteritems() -> .items() izip() -> zip() и т.д.
  • 9.
    Классы ● Больше нетразделения на old-style и new-style; ● другой синтаксис для метаклассов (плюс поддержка kwargs в объявлении классов); ● super cм. https://github.com/rfk/magicsuper и http://pypi.python. org/pypi/newsuper
  • 10.
    Классы: 2.x class Foo(object): def do(self): return 41 class Bar(Foo): __metaclass__ = Meta def do(self): old = super(Bar, self).do() return old + 1
  • 11.
    Классы: 3.x class Foo: def do(self): return 41 class Bar(Foo, metaclass=Meta): def do(self): old = super().do() return old + 1
  • 12.
    Аннотации def foo(x: int,y: int, *args: str)-> int: pass def bar(x: "широта в градусах"=60): pass foo.__annotations__ bar.__annotations__
  • 13.
    nonlocal Javascript: functionfoo(){ for (i=0; i<10; i++) {} // OMG, меняется window.i }
  • 14.
    nonlocal Python: x= 5 def foo(): x = 1 foo() # x все еще 1
  • 15.
    nonlocal def bar(): x = 5 def foo(): x += 1 foo() return x bar() # упадет с UnboundLocalError: local variable 'x' referenced before assignment
  • 16.
    nonlocal (2.x) def bar(): x = {'val': 5} # хак? def foo(): x['val'] += 1 foo() return x['val'] bar() # вернет 6
  • 17.
    nonlocal (3.x) def bar(): x = 5 def foo(): nonlocal x x += 1 foo() return x bar() # вернет 6
  • 18.
    Функции без позиционных аргументов deffoo(*, x): pass foo(x=1) # работает foo(1) # упадет с TypeError
  • 19.
    Исключения try: foo() except ExcClass as e: print(e.__traceback__) print(e.__context__) print(e.__cause__) raise NewExcClass from e
  • 20.
    Присваивание >>> a, *rest,b = range(5) >>> rest [1, 2, 3]
  • 21.
    Потоки (threads) ● Болеебыстрый GIL (Python 3.2); ● Исправлены некоторые недочеты реализации в 2.х - например, в 2.x поток, застрявший в deadlock'е, нельзя убить по SIGINT (Ctrl-C), а в 3.2 - можно.
  • 22.
    yield from # python2.x def gen1(): yield 'foo' yield 'bar' def gen2(): for x in gen(): yield x
  • 23.
    yield from # python3.3 def gen1(): yield 'foo' yield 'bar' return 42 def gen2(): return yield from gen1() правильная обработка исключений, возврат значений
  • 24.
    yield from: зачем? Спомощью yield можно писать на Python асинхронный код в синхронном стиле (без вороха callback'ов); yield from делает это проще. См. также: ● twisted.internet.defer.deferredGenerator; ● tornado.gen; ● https://launchpad.net/adisp
  • 25.
  • 26.
    С помощью __future__ ● absolute_import ● print_function ● unicode_literals ● division
  • 27.
    Стандартная библиотека ● Большинствоизменений из 3.0 и 3.1 ● часть изменений из 3.2. Со сторонними пакетами: ● futures ● distutils2
  • 28.
    Синтаксис (2.7) ● dictcomprehensions {i: foo(i) for i in range(10)} ● set lterals {1, 2, 3}
  • 29.
    Библиотеки январь 2012 г.,количество библиотек на pypi: ● 2.x: >18000 ● 3.x: <1000
  • 30.
  • 31.
    Просто взять ипереписать на 3.х ● (+) лучшая стратегия перевода "своего" кода; ● (-) нужно переписывать весь код на 3.х; ● (-) нельзя использовать сторонний код, написанный для 2.х; ● (-) не подходит для open-source библиотек, которым важна обратная совместимость (привет dateutil)
  • 32.
    Библиотеки ● Чтоб напрактике использовать Python 3, нужны библиотеки, поддерживающие Python 3; ● переписать библиотеки с 2.х на 3.х нельзя, т.к. большая часть кода пишется на 2.х и поэтому поддержка 2.х важна. ● Чтобы писать код на Python 3 и использовать сторонние наработки, нужно уметь добавлять поддержку 3.х в код на 2. х.
  • 33.
    Проблема не впортировании своего кода (взял да переписал), проблема в портировании библиотек
  • 34.
    Экзотические способы ● писатьна Cython (пример: lxml - большая часть написана на Cython и поэтому поддерживает и 2.x, и 3.х) ● пожертвовать 100k$ pypy и, возможно, через год-другой получить интерпретатор, который умеет как 2.х, так и 3.х одновременно.
  • 35.
  • 36.
    2to3 Берет код на2.х и преобразует его в код на 3.х автоматически. ● Можно применить 1 раз и работать потом только с 3.х кодом; ● можно вести разработку на 2.х и получать версию 3.х при необходимости (setup.py).
  • 37.
    2to3: кто использует ● tornado; ● jinja2; ● SQLAlchemy; ● celery; ● mako; ● nose; ● ipython; ● Sphinx; ● numpy; ● psycopg2; ● ...
  • 38.
    2to3: плюсы ● Входитв стандартную поставку и рекомендуется в PEP-3000; ● автоматически генерирует 3.х код из 2.х кода; ● просто интегрируется с setup.py.
  • 39.
    2to3: минусы ● работаетне всегда корректно, требует ручного вмешательства или написания своих "фиксеров"; ● медленно работает; ● затруднена отладка при ошибках в 3.х коде; ● при использовании в авто-режиме разработка все еще ведется на 2.х
  • 40.
    3to2 2to3 наоборот: кодпишется на 3.х, автоматически генерируется версия для 2.х ● не входит в стандартную поставку; ● код пишется на 3.х, а не на 2.х; ● мало кто использует на практике.
  • 41.
    Можно писать код,который работает как на 2.х, так и на 3.х
  • 42.
    Код, который работаеткак на 2.х, так и на 3.х ● pyramid (1.3); ● django (trunk сразу после релиза 1.4); ● pip; ● virtualenv; ● WebOb; ● coverage; ● requests; ● python-код из lxml; ● whoosh; ● pytz; ● ...
  • 43.
    Код, который работаеткак на 2.х, так и на 3.х ● разработка ведется с использованием подмножества python 2.x, совместимого с 3.х. ● отсутствует шаг с автоматической генерацией кода, поэтому проще отладка и разработка; ● поддержка python 2.5 требует хаков;
  • 44.
    Код, который работаеткак на 2.х, так и на 3.х ● Библиотека six (около 300 строк): if PY3: text_type = str else: text_type = unicode # ... ● вся six целиком редко нужна (пример: pymorphy.py3k - это 6 строк кода из six)
  • 45.
    Необходимые условия для портирования ●Автоматизированные тесты; ● по возможности, современные версии языковых конструкций (желательно 2.6+); ● четкая стратегия работы с unicode;
  • 46.
    Один из возможныхалгоритмов 1. Удостовериться, что тестов достаточно; 2. настроить tox; 3. перестать поддерживать python 2.5; 4. заставить весь код работать с from __future__ import (absolute_import, unicode_literals, print_function, division) 5. "внутри" использовать только unicode- строки; 6. чинить ошибки в 3.х с помощью six, пока все не заработать и под 2, и под 3.
  • 47.
    Больше ссылок ● http://wiki.python.org/moin/PortingPythonToPy3k ● https://code.djangoproject.com/wiki/PortingNotesFor2To3 ● http://python3porting.com/ ● http://lucumr.pocoo.org/2010/2/11/porting-to-python-3-a-guide/ ● http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/ ● http://docs.pythonsprints.com/python3_porting/index.html ● http://docs.python.org/release/3.0.1/whatsnew/3.0.html (там внизу - способ портирования на python3 от Guido van Rossum)
  • 48.