Если вы еще не начали переходить на Python3 то сейчас самое время!
Различий очень много. Как внутренние архитектурные решения, невидимые рядовому программисту, так и явные изменения, которые приходится использовать каждый день.
Для тех, кто только начинает знакомиться с 3й версией я собрал самые, на мой взгляд, заметные отличия 3й версии Python.
🔸Unicode по умолчанию
Для тех, кто пишет на Python2, всегда была актуальная проблема использования любых символов вне ASCII таблицы. Это относилось как к строкам в данных, так и просто комментариям по коду.
В Python3 по умолчанию строки это UNICODE, кодировка по умолчанию UTF-8. Теперь можно где угодно в строках использовать символы из кириллицы, таблицы Unicode, иероглифы, смайлы и эмодзи.
Конечно же это не относится к именам переменных и файлов! Только строки и коменты.
🔸Функция print()
Не смотря на то, что теперь приходится писать лишние скобочки, мы получили серьезный апгрейд такой простой, но полезной функции. Если вкратце, то print "объединился" с sys.stdout. Как это работает?
Как не переносить курсор на новую строку после печати?
PY2:
Логично предположить что вместо пустой строки можно подставить любые символы.
Форсировать вывод в stdout во время блокирующей операции в консольном приложении.
PY2:
Кроме того, с помощью аргумента file можно печатать в файл или другой подходящий интерфейс.
🔸Оператор деления
Теперь деление целых чисел не обязательно в результате будет иметь целое число. То есть, если числа int не делятся без остатка то результатом будет float
Как разделить два int и получить float?
PY2:
Между тем, чтобы работало как раньше, то есть деление без остатка, просто нужно использовать другой оператор:
В Python3 изменили принципы относительного импорта. Теперь стало проще и более предсказуемо. Но об этом лучше подробней и отдельно.
Все перечисленные выше фишки были доступны и в Python2 как бекпорт. То есть можно было активировать этот функционал через модуль __future__. Но когда оно по умолчанию, это сильно удобней)))
🔸 Асинхронное программирование
Теперь в Python3 удобно писать асинхронный код с помощью библиотеки asyncio и аналогичных.
🔸 F-String
Да, в Python добавился новый способ форматирования строки (если не ошибаюсь, уже 5й), но он офигенный!
PY2:
Нет, я не про OrderedDict, я про обычный dict. Теперь порядок ключей сохраняется! В каком порядке добавляли, в таком порядке они и итерируются. Мелочь, но приятно.
Это далеко не всё, но для начала достаточно 😉
#2to3
Различий очень много. Как внутренние архитектурные решения, невидимые рядовому программисту, так и явные изменения, которые приходится использовать каждый день.
Для тех, кто только начинает знакомиться с 3й версией я собрал самые, на мой взгляд, заметные отличия 3й версии Python.
🔸Unicode по умолчанию
Для тех, кто пишет на Python2, всегда была актуальная проблема использования любых символов вне ASCII таблицы. Это относилось как к строкам в данных, так и просто комментариям по коду.
В Python3 по умолчанию строки это UNICODE, кодировка по умолчанию UTF-8. Теперь можно где угодно в строках использовать символы из кириллицы, таблицы Unicode, иероглифы, смайлы и эмодзи.
Конечно же это не относится к именам переменных и файлов! Только строки и коменты.
🔸Функция print()
Не смотря на то, что теперь приходится писать лишние скобочки, мы получили серьезный апгрейд такой простой, но полезной функции. Если вкратце, то print "объединился" с sys.stdout. Как это работает?
Как не переносить курсор на новую строку после печати?
PY2:
print text,PY3:
print(text, end='')
Логично предположить что вместо пустой строки можно подставить любые символы.
Форсировать вывод в stdout во время блокирующей операции в консольном приложении.
PY2:
import sysPY3:
print text # или sys.stdout.write(text)
sys.stdout.flush()
print(text, flush=True)
Кроме того, с помощью аргумента file можно печатать в файл или другой подходящий интерфейс.
🔸Оператор деления
Теперь деление целых чисел не обязательно в результате будет иметь целое число. То есть, если числа int не делятся без остатка то результатом будет float
Как разделить два int и получить float?
PY2:
12/5PY3:
>>> 2 # ужасный результат!
12/float(5) # приходится делать так
>>> 2.4
12/5
>>> 2.4
Между тем, чтобы работало как раньше, то есть деление без остатка, просто нужно использовать другой оператор:
12//5🔸Относительный импорт
>>> 2
В Python3 изменили принципы относительного импорта. Теперь стало проще и более предсказуемо. Но об этом лучше подробней и отдельно.
Все перечисленные выше фишки были доступны и в Python2 как бекпорт. То есть можно было активировать этот функционал через модуль __future__. Но когда оно по умолчанию, это сильно удобней)))
🔸 Асинхронное программирование
Теперь в Python3 удобно писать асинхронный код с помощью библиотеки asyncio и аналогичных.
🔸 F-String
Да, в Python добавился новый способ форматирования строки (если не ошибаюсь, уже 5й), но он офигенный!
PY2:
value = 123PY3:
string = "Value = {}".format(value)
value = 123🔸Упорядоченный словарь
string = f"Value = {value}"
Нет, я не про OrderedDict, я про обычный dict. Теперь порядок ключей сохраняется! В каком порядке добавляли, в таком порядке они и итерируются. Мелочь, но приятно.
Это далеко не всё, но для начала достаточно 😉
#2to3
Поздно писать код, совместимый с Python2 и Python3 одновременно. Если вы матёрый программист, то, возможно, использовали библиотеку six для создания совместимого кода. Или использовали различные модули автоматизации для преобразования кода из Python2 в совместимый для Python2 и Python3. Например sixer или python-future.
Но это время прошло! Теперь пишем только на 3м Python. Совместимость с версией 2 скоро вовсе не потребуется (кроме отдельных тяжелых случаев поддержки legacy систем, но я надеюсь таких у вас нет или совсем мало)
Скорее всего, у многих появляется вопрос, а что делать с кодом который написан под Python2? Садиться и переписывать? Или хотя бы сделать совместимым с Python3?
Всё зависит от конкретной ситуации. Убедитесь что ваша инфраструктура готова под Python3 прежде чем начать.
Скорее всего пора переписывать и не оглядываться назад!
Но не всё так безнадёжно, в стандартной поставке Python уже давно есть готовый инструмент для автоматизации процесса. Это библиотека lib2to3 и прилагаемый скрипт 2to3.py
С чего начать?
1. Документация
Прежде всего почитайте документацию
https://docs.python.org/3.8/library/2to3.html
А также встроенная справка
для Windows
2. Тест
Прежде чем приступать к ожесточенным конвертациям, обязательно протестируйте весь процесс! Например, чтобы просто посмотреть какие будут изменения в скрипте, запустите команду с флагом --list-fixes
Нашел развёрнутую статью про тему 2to3, советую почитать.
https://python3porting.com/2to3.html
3. Конвертация
Не забываем что в качестве источника для конвертации можно указать директорию. И ВСЕГДА указывайте путь сохранения результата.
Пример конвертации пакета
#2to3
Но это время прошло! Теперь пишем только на 3м Python. Совместимость с версией 2 скоро вовсе не потребуется (кроме отдельных тяжелых случаев поддержки legacy систем, но я надеюсь таких у вас нет или совсем мало)
Скорее всего, у многих появляется вопрос, а что делать с кодом который написан под Python2? Садиться и переписывать? Или хотя бы сделать совместимым с Python3?
Всё зависит от конкретной ситуации. Убедитесь что ваша инфраструктура готова под Python3 прежде чем начать.
Скорее всего пора переписывать и не оглядываться назад!
Но не всё так безнадёжно, в стандартной поставке Python уже давно есть готовый инструмент для автоматизации процесса. Это библиотека lib2to3 и прилагаемый скрипт 2to3.py
С чего начать?
1. Документация
Прежде всего почитайте документацию
https://docs.python.org/3.8/library/2to3.html
А также встроенная справка
для Windows
python C:\python37\Tools\scripts\2to3.py --helpдля Linux
2to3-3.7 --help(имена зависят от установленных версий Python)
2to3-2.7 --help
2. Тест
Прежде чем приступать к ожесточенным конвертациям, обязательно протестируйте весь процесс! Например, чтобы просто посмотреть какие будут изменения в скрипте, запустите команду с флагом --list-fixes
2to3 --list-fixes my_script.py3. Полезные советы
Нашел развёрнутую статью про тему 2to3, советую почитать.
https://python3porting.com/2to3.html
3. Конвертация
Не забываем что в качестве источника для конвертации можно указать директорию. И ВСЕГДА указывайте путь сохранения результата.
Пример конвертации пакета
2to3 --output-dir=./py3/package_name -W -n ./package_name
#2to3
Одно из незаметных но приятных изменений в Python3, которое не часто упоминается, это проблема Leak of variables.
Относится она к конструкции List Comprehensions, он же генератор списков.
Лучше всего данный фикс показать на примере.
Python2
Но стоит помнить, что к обычным циклам это не относится.
Python3
#2to3
Относится она к конструкции List Comprehensions, он же генератор списков.
Лучше всего данный фикс показать на примере.
Python2
x = 'some_value'
my_list = [x for x in range(10)]
print x
>>> 9
Python3x = 'some_value'
my_list = [x for x in range(10)]
print(x)
>>> some_value
Переменная "х" в генераторе списка не затирает глобальную переменную с таким же именем. То есть внутри генератора действуют локальные переменные.Но стоит помнить, что к обычным циклам это не относится.
Python3
x = 'some_value'
for x in range(10):
pass
print(x)>>>9
#2to3
Ранее, в посте о главных новшествах Python3, я упоминал про полный переход на unicode. А в конце было сказано:
"Конечно же это НЕ относится к именам переменных и файлов! Только строки и комменты."
На самом деле это было лишь предостережение. Можно создавать имена переменных и модулей на unicode!
То есть мы вполне можем сделать так:
Bash:
// создали файл с именем на кириллице
Или так
Или так
Или так
Надеюсь не нужно объяснять что смысла в этом ноль и так делать не стоит)))
Кстати, это была секретная информация, так что никому! Чтобы и в мыслях не было! 🤐
PS. я поддерживаю русский язык только в комментариях по коду. Конечно, если только вы уверены, что команда разработчиков будет русскоязычной и никак иначе! Это очень помогает разбираться в чужом коде (или в своём через время) Но это крайний случай.
#tricks
"Конечно же это НЕ относится к именам переменных и файлов! Только строки и комменты."
На самом деле это было лишь предостережение. Можно создавать имена переменных и модулей на unicode!
То есть мы вполне можем сделать так:
Bash:
$ echo "print('Приветы')" > моймодуль.py// создали файл с именем на кириллице
>>> import моймодуль
Приветы
Или так
>>> Василий = "Василий"
>>> Петрович = "Петрович"
>>> фулнейм = ' '.join([Василий, Петрович])
>>> print(фулнейм)
Василий Петрович
Или так
def сделать_красиво(было):
более_красиво = 100500
стало = было * более_красиво
return стало
Или так
>>> Ψ = 100
>>> Σ = -100
>>> смысл = Ψ + Σ
>>> print(смысл)
0
Надеюсь не нужно объяснять что смысла в этом ноль и так делать не стоит)))
Кстати, это была секретная информация, так что никому! Чтобы и в мыслях не было! 🤐
PS. я поддерживаю русский язык только в комментариях по коду. Конечно, если только вы уверены, что команда разработчиков будет русскоязычной и никак иначе! Это очень помогает разбираться в чужом коде (или в своём через время) Но это крайний случай.
#tricks
Иногда бывает ситуация когда dev-сервер по какой-либо причине не закрылся и висит в процессах, занимая порт.
Это может быть из-за падения IDE или просто сам забыл погасить и закрыл терминал.
Для таких случаев я набросал простую функцию с командой:
Если во время старта dev-сервера получаете ошибку что порт уже занят, просто выполните команду, подставив свой порт.
Bash
Имя команды можете изменить на любое другое.
#linux
Это может быть из-за падения IDE или просто сам забыл погасить и закрыл терминал.
Для таких случаев я набросал простую функцию с командой:
kill_on_port() {
port=$(lsof -t -i:$1)
echo "KILL PROCESS:" $port
sudo kill -9 $port
}
alias killonport="kill_on_port $@"
Код поместить в ~/.bashrc и рестартнуть систему.Если во время старта dev-сервера получаете ошибку что порт уже занят, просто выполните команду, подставив свой порт.
Bash
kill_on_port 8000Скорее всего бесполезно, если другой процесс назначен на перезапуск вашего dev-сервера в случае падения.
Имя команды можете изменить на любое другое.
#linux
Все знают как красиво написать дату и время с помощью библиотеки datetime:
1. Удобно писать более комплексные шаблоны
Чтобы не повторять аргумент dt делаем форматирование по индексу
Если используем разные даты, то можно сделать форматирование по имени
3. Зная про эту фишку, и что реализуется она с помощью magic-метода
Я набросал простой пример с таким поведением, смотрим в гистах 🔗.
>>> from datetime import datetimeНо мало кто знает, что тоже самое можно сделать и другим способом:
>>>
>>> dt = datetime.now()
>>> dt.strftime('%Y.%m.%d %H:%I')
'2020.01.08 12:00'
>>> "{:%Y.%m.%d %H:%I}".format(dt)
'2020.01.08 12:00'
Если паттерн хранится отдельно, то можно записать так>>> date_frmt = '%Y.%m.%d %H:%I'Хм, а чем это лучше, спросите вы?
>>> '{:{}}'.format(dt, date_frmt)
'2020.01.08 12:00'
1. Удобно писать более комплексные шаблоны
>>> event_name = 'Python-вебинар'(ну да, такие паттерны можно писать и в strftime, если что. Хотя, можете напороться на UnicodeEncodeError даже в Python3!)
>>> "Сегодня, {:%Y.%m.%d}, ровно в {:%H:%I} мы начинаем {}!".format(dt, dt, event_name)
'Сегодня, 2020.01.08, ровно в 12:00 мы начинаем Python-вебинар!'
Чтобы не повторять аргумент dt делаем форматирование по индексу
"{0:%Y} {0:%H.%I}".format(dt)Если используем разные даты, то можно сделать форматирование по имени
"{date1:%Y}-{date2:%Y} (current {date1:%Y})".format(date1=dt1, date2=dt2)
2. Форматирование даты находится непосредственно в тексте а не в отдельной переменной, что бывает удобней в некоторых случаях. Например, если текст хранится в базе данных то не нужно гдето отдельно хранить формат дат.3. Зная про эту фишку, и что реализуется она с помощью magic-метода
__format__можно начать писать свои классы с подобным функционалом! Собственно, в классе datetime этот метод просто вызывает уже знакомый метод strftime.
Я набросал простой пример с таким поведением, смотрим в гистах 🔗.
GitHub
python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.
Есть такая запись форматирования
Есть аналог для перемещения влево и по центру, но не об этом сейчас.
Что будет, если подать в такую строку кастомный класс?
Чтобы "конвертнуть" свой класс с помощью ˍˍstrˍˍ пишем так:
Только учтите, если не определить методы ˍˍstrˍˍ и ˍˍreprˍˍ то получится что-то типа:
PS. А если определить метод ˍˍformatˍˍ, то можно и другие буковки использовать для специфичных преобразований!
#tricks
>>> "{0:>10}".format(42)
" 42"
Что означает: сделать строку шириной 10 символов и переданный аргумент выровнять по правую сторону.Есть аналог для перемещения влево и по центру, но не об этом сейчас.
Что будет, если подать в такую строку кастомный класс?
>>> class MyClass:Упс, ошибка форматирования! То есть Python говорит что у нашего класса нет метода ˍˍformatˍˍ. Даже ˍˍstrˍˍ и ˍˍreprˍˍ не помогут.
>>> pass
>>> c = MyClass()
>>> "{0:>10}".format(c)
TypeError: unsupported format string passed to MyClass.__format__
>>> class MyClass:Что ж, это легко решается добавлением метода ˍˍformatˍˍ. Но а что если это не ваши классы и изменить исходники никак нельзя? Можно сделать небольшое преобразование, например такими способами:
>>> def __str__(self):
>>> return 'My Class'
>>> def __repr__(self):
>>> return '<My Class>'
>>> c = MyClass()
>>> "{0:>20}".format(c)
TypeError: unsupported format string passed to MyClass.__format__
>>> "{0:>20}".format(str(c))
" My Class"
>>> "{0:>20}".format(repr(c))
" <My Class>"
Но лучше использовать явный конвертор, то есть механизмы самого форматирования. В таком случае необходимое действия будет жёстко указано в самом паттерне.Чтобы "конвертнуть" свой класс с помощью ˍˍstrˍˍ пишем так:
"{0!s:>20}".format(c)
А чтобы преобразование сделалось с помощью ˍˍreprˍˍ, пишем так:"{0!r:>20}".format(c)
Теперь не важно какой класс прилетит, всё сработает!Только учтите, если не определить методы ˍˍstrˍˍ и ˍˍreprˍˍ то получится что-то типа:
"<MyClass object at 0x7f27a62ed278>"Но хотя бы не будет ошибки.
PS. А если определить метод ˍˍformatˍˍ, то можно и другие буковки использовать для специфичных преобразований!
#tricks
На заметку начинающим.
Оператор for..in имеет необязательный блок else.
Как это работает?
Этот блок выполняется только если итерация завершилась успешно, пройдя все элементы.
Чтобы блок else не выполнился, итерация должна прерваться с помощью break.
Зачем это нужно?
Предполагается, что такая конструкция нужна для определения значения по умолчанию, в случае если мы ищем нужное значение в цикле. Как только необходимые данные найдены, выходим из цикла с помощью break. Если ничего не нашли, то выполняется блок else, в котором выполняем альтернативные действия.
Чем полезно?
В целом, пишется более лаконично (читай питонично)
Позволяет сократить время вычислений, если получение дефолта достаточно затратная процедура. Например, есть такой код:
Но что, если функция get_default() занимает слишком много времени или не должна вызываться просто так? Тогда написать можно иначе:
#tricks
Оператор for..in имеет необязательный блок else.
Как это работает?
Этот блок выполняется только если итерация завершилась успешно, пройдя все элементы.
Чтобы блок else не выполнился, итерация должна прерваться с помощью break.
Зачем это нужно?
Предполагается, что такая конструкция нужна для определения значения по умолчанию, в случае если мы ищем нужное значение в цикле. Как только необходимые данные найдены, выходим из цикла с помощью break. Если ничего не нашли, то выполняется блок else, в котором выполняем альтернативные действия.
Чем полезно?
В целом, пишется более лаконично (читай питонично)
Позволяет сократить время вычислений, если получение дефолта достаточно затратная процедура. Например, есть такой код:
value = get_default()В этом примере мы сначала создаем значение по умолчанию, потом в итерации получаем нужный параметр. Если get_new_value() не вернула новое значение, блок if никогда не сработает и остается значение по умолчанию.
for i in array:
res = get_new_value(i)
if res:
value = res
break
Но что, если функция get_default() занимает слишком много времени или не должна вызываться просто так? Тогда написать можно иначе:
value = NoneА с блоком else можно записать короче
for i in array:
res = get_new_value(i)
if res:
value = res
break
if value is None:
value = get_default()
for i in array:А тех кто на Python3.8+, еще короче:
res = get_new_value(i)
if res:
value = res
break
else:
value = get_default()
for i in array:Вероятно, можно такую логику записать еще короче, но это уже другая история
if (res:=get_new_value(i)):
value = res
break
else:
value = get_default()
#tricks