Наступил 2020 год, а значит, Python 2 перестал поддерживаться. Если быть совсем точным, то основные разработчики уже перестали заниматься веткой, а выход релиза 2.7.18, приуроченный к PyCon US в апреле 2020 года, ознаменует полное прекращение любой активности, связанной с Python 2.
С другой стороны, совсем недавно состоялся релиз Python 3.8, добавивший немного синтаксического сахара в язык. Python 3.9 же ожидается ещё нескоро, да и пока не похоже что добавит в язык что-то интересное.
Так что если вы вдруг ещё не отказались от Python 2, то дальше тянуть смысла просто нет: поддержка второй версии уже прекратилась, а переход сразу на 3.8 позволит использовать язык в его самом актуальном состоянии ещё долгое время.
Ну а, чтобы решиться было проще, ниже приведён обзор главных нововведений Python 3.8, которые пригодятся каждому питонисту.
1. Выражения присваивания (Assignment expressions)
Так же известные как «моржовый оператор» (walrus operator) — новый синтаксис, который позволит присваивать значения переменным внутри другого выражения. Это, наверное, самое известное и обсуждаемое из нововведений версии 3.8.
a = 6
# Код ниже присваивает b значение a ** 2
# и проверяет, если b > 0
if (b := a ** 2) > 0:
print(f'Квадрат {a} это {b}.') # Квадрат 6 это 36.
Целью введения оператора является повышение читаемости кода, поэтому использовать его (как и все остальные операторы) стоит не при первой же возможности, а только тогда, когда это уместно.
# так делать НЕ НАДО
a = 5
d = [b := a+1, a := b-1, a := a*2]
2. Только позиционные аргументы
Функции могут принимать два типа аргументов.
- Позиционные — передаваемые по позиции
- Именованные — передаваемые по имени
В коде ниже, значения обоих аргументов a и b можно передать как по позиции, так и по имени.
def my_func(a, b=1):
return a+b
my_func(5,2) # Оба аргумента позиционные
my_func(a=5, b=2) # Оба аргумента именованные
В новой версии Питона можно явно ограничить, какие из параметров могут быть только позиционными или только именованными с помощью символов / и * в сигнатуре функции (последний, правда, появился ещё до Python 3.8).
В примере ниже, первые два параметра a и b только позиционные, c и d могут быть любыми, а последние два e и f должны быть именованными.
def my_func(a, b, /, c, d, *, e, f):
return a+b+c+d+e+f
my_func(1, 2, 3, 4, 5, 6) # ошибка: e, f должны быть именованными
my_func(a=1, b=2, 3, 4, e=5, f=6) # ошибка: a, b должны быть позиционными
my_func(1, 2, c=3, 4, e=5, f=6) # returns 21
my_unc(1, 2, c=3, d=4, e=5, f=6) # returns 21
Зачем же ограничивать гибкость типов аргументов? В некоторых случаях использование имени параметра вместо его позиции будет бесполезным и неуместным. Ограничение так же позволит избежать проблем, если вы планируете изменить имена позиционных аргументов в будущем.
3. f-строки 2.0
Сложно описать словами, насколько проще и элегантнее сделали форматирование f-строки, появившиеся ещё в Python 3.6.
Сложно поверить, но Python 3.8 удалось сделать их ещё удобнее. Добавив знак = после имени подставляемой переменной, вы сможете вывести её имя и её значение без дублирования имени.
pi = 3 # В военное время может быть и так
print(f'pi={pi}') # так мы делали раньше
print(f'{pi=}') # а так можно делать теперь
Дебаг с помощью print станет ещё удобнее :)
4. Обратный порядок элементов словаря
Итерироваться по словарям теперь можно в обратном порядке с помощью reversed().
5. Получение метаданных из других модулей
Новый модуль importlib.metadata позволит получать метаданные (например, версию) из сторонних пакетов.
6. finally + continue
Раньше нельзя было использовать выражение continue внутри finally из-за сложности в реализации этой фичи. Теперь можно.
for i in range(2):
try:
print(i)
finally:
print('Тест.')
continue
print('Эту строку вы не увидите.')
# Python <= 3.7
>> SyntaxError: 'continue' not supported inside 'finally' clause
# Python 3.8
>> 0
Тест.
1
Тест.
Комментарии (61)
roller
09.01.2020 14:50С точки зрения обычного рубиста это выглядит так:
1) Ну наконец-то догадались сделать, и даже оператор присвоения из Паскаля — не слишком плохой вариант
2) Выглядит как ересь, или призыв сотоны однострочником. Весь мир двигается к именованным аргументам
3) Странная у вас интерполяция, явно js покусал
4) А этого не было?!
5)… все равно мета возможности рубей сломают мозг с большей вероятность (читай: руби тут мощнее)
6) Ну и хорошо
HeaTTheatR
09.01.2020 14:53+1Однозначно, чем дальше — тем хуже!
slavashock
09.01.2020 15:57Можно аргументов? Я например, за 4 и 5 пункт, а остальное надеюсь станет deprecated в ближайшее время.
gecube
09.01.2020 16:53Вопрос тогда только один — а куда, на какой язык перекатываться? Пайтон привлекал и привлекает тем, что у него батарейки включены. И он очень хорошо заменяет баш для задач администрирования. Ну, и весь ML на нем.
0xd34df00d
09.01.2020 18:00Смотря для чего.
gecube
09.01.2020 19:26автоматизация (сисопс, девопс, гитопс....)
ML
написание простых http сервисов0xd34df00d
09.01.2020 20:37Первое и третье я делаю на хаскеле.
Второе вот да, по части клея между библиотеками питон победил. Правда, когда я занимался ML, питонобиблиотек не хватало, и все нужные алгоритмы я делал на плюсах.
DarkGenius
09.01.2020 21:49А в чем преимущества писать http-сервисы на Haskell?
0xd34df00d
09.01.2020 22:52+1Ну, начнём с того, что это один из немногих языков, что я знаю :) Не на C++ же сервисы писать, в самом деле!
А вообще есть очень прикольный фреймворк Servant, где всё в типах описываешь, а дальше можно выключить мозг и просто писать функции, пока они не начнут тайпчекаться.
Ну и вообще весь профит от статически типизированного языка, да.
Falstaff
10.01.2020 02:04Если хочется простые http сервисы и язык без bells and whistles, то вроде Go на такое упирает, там как раз не любят без особой нужды новые фичи включать.
OnYourLips
09.01.2020 23:15Руби, но там значительно больше подобного, да и популярность ниже.
Хотя в руби батареек больше, например YAML есть из коробки.
Andy_U
09.01.2020 16:33+1С моей точки зрения, самое интересное нововведение, это multiprocessing.shared_memory.
P.S. Сам, правда, еще не пробовал ;)
imengineer-org
09.01.2020 16:37+2Спасибо за обзор. В чужом коде буду узнавать, если увижу. У меня самого энтузиазма новые фичи не вызвали.
YuriM1983
09.01.2020 20:48+8
Это вообще ужасно выглядит. Как будто у функции 8 параметров, но указывать надо 6.def my_func(a, b, /, c, d, *, e, f): return a+b+c+d+e+f my_func(1, 2, 3, 4, 5, 6)
А вот и задачки для горе-собеседований подъехали.# так делать НЕ НАДО a = 5 d = [b := a+1, a := b-1, a := a*2]
Arcpool
09.01.2020 23:02А когда порядок элементов словаря стал определённым, что понадобился reversed() или это для OrderedDict() ?
Mingun
10.01.2020 00:01+2С версии 3.7 Python'ий
dict
помнит порядок вставки своих элементов.
Dictionaries preserve insertion order.
Словари сохраняют порядок вставки элементов.ratijas
10.01.2020 14:05На самом деле… порядок вставки стал запоминаться ещё с версии Python 3.5 или 3.6, в с версии Python 3.7 его сделали обязательной частью спецификации языка.
vassabi
10.01.2020 00:02а при чем там определенность порядка? Порядок элементов даже может быть случайным — и тогда reversed тоже будет случайным (только в обратном порядке).
Mingun
10.01.2020 00:08При том, что вы вставляете в одном, а получаете в обратном. Этот порядок не случаен, а как раз вполне себе определён. При последовательных вызовах
reversed()
на одном и том же словаре вы будете получать один и тот же результат. А раньше вообще никакой порядок не гарантировался.vassabi
10.01.2020 00:35я не про «с версии 3.7», я про логику — что
reversed([a for a in dict]) равен [a for a in reversed(dict)]Mingun
10.01.2020 17:04А, мне просто показалось, что вы мне отвечали (у меня были проблемы с приходом уведомлений об ответах на почту и я в попытках исправить проблему поставил лишние галочки в настройках, в результате чего на почту стали приходить уведомления обо всех комментариях в теме, а не только о тех, которые являются ответом мне. Но сразу я этого не заметил и получилось так, что я написал комментарий, а минуту спустя на него прилетел «ответ»). Так-то, конечно, вы целиком и полностью правы.
noth
10.01.2020 00:57+2вроде как начиная с 3.6 в словари начали сохранять порядок (но это вроде была побочная фича)
с 3.7 это гарантируется официально:
What’s New In Python 3.7
the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.
S0mbre
10.01.2020 01:45Зачем спорить, какие фичи нужны или не нужны языку? Мне кажется более правильным стратегия, когда язык гибкий и поддерживает много всего, а уже корректное применение инструментов — на совести программистов. Кому-то действительно понадобится присваивание в условиях (на C/C++ я вообще постоянно эти пользовался), а кому-то будет невлом написать лишнюю строчку кода для большей наглядности. Кто-то пишет для себя, кто-то — для сообщества, кто-то — для заказчика. Везде разные исходные условия, разные стили программирования и разные задачи. Язык — он ведь и есть язык, должен быть живым. Как мы с вами по-русски говорим, но все со своими особенностями, выговором, любимыми поговорками и т.д., так и на языке программирования. Какие проблемы? Компилятор все равно все оптимизирует и переварит в
почтиодин байт-кот… ))gecube
10.01.2020 09:02Чой-то хабр сгрыз мой ответ.
Поэтому дублирую.
Только вот не задача. Ядро языка должно быть минимальным. Так сказать — минимальный набор возможностей для решения максимального количества задач. Потому что если мы ядро языка раздуваем, то как выше коллега заметил — компилятор/интерпретатор языка становится сложнее и действительно нужные фичи становится сложнее добавлять. + Скорость интерпретации/компиляции падает (отличный пример — скорость компиляции с++, можно успеть состариться, пока напичанный на нем проект соберётся) + сложность автоматической кодогенерации (а кто сказал, что она не нужна?)
А самое неприятное, что каждая не очень продуманная фишка языка приводит к новым неочевидным эффектам.
Вот в сообщении ttpss://habr.com/ru/post/483276/#comment_21108246 очень хороший пример. И не надо говорить, что адекватные люди такой код писать не будут, ок?
Я уж не говорю, что сравнивать естественные языки, на которых мы говорим, и языки программирования немного странно. Первые действительно развиваются, но и стоимость изменений в них не такая большая, как в ЯП. Лучше тогда уж сравнивать с искусственными языками. Ну, тем же эсперанто. И сильно ли он популярен? Была на него мода… Но вроде все уже пришли к тому, что язык интернационального общения — ломаный английский.
И смотрите какая интересная штука. Принципиально — за последние лет 40 ничего нового не придумали. Циклы? Так они и тогда были. Функции? Классы? Ввод-вывод? Ну, разве что ФП пошло в массы, но это как-то очень совпало с распространением действительно распределенных систем. Ну, и какие тогда реальные фичи внедрять, кроме синтаксического сахара?
iroln
10.01.2020 09:28Ну, и какие тогда реальные фичи внедрять, кроме синтаксического сахара?
Касательно CPython, думаю, преодоление проблем/наследия GIL и отсутствия полноценной многопоточности, реализация JIT с опциональной статической типизацией. В общем, двигаться в сторону повышения производительности, потому как с удобством и скоростью программирования на Python уже сейчас всё очень хорошо.
А в синтаксическом сахаре я не вижу ничего плохого, если он улучшает читабельность кода и повышает продуктивность разработчика.
LinearLeopard
10.01.2020 11:44+1Зачем же ограничивать гибкость типов аргументов? В некоторых случаях использование имени параметра вместо его позиции будет бесполезным и неуместным. Ограничение так же позволит избежать проблем, если вы планируете изменить имена позиционных аргументов в будущем.
Некоторые ф-ции из стандартной библиотеки имеют такую сигнатуру, например bool.
Changed in version 3.7: x is now a positional-only parameter.
Что выглядит вполне логичным, действительно, какие имя дать этому параметру? `o`, `x`, `a`?
И вполне логично запретить вызов
bool(a=x)
gecube
10.01.2020 12:48Ну, это же чушь? Повторюсь, а если я **kwargs передаю через цепочку вызовов? То мне делать дополнительные конвертации?
bool(a=x)
а это пускай просто проверяет единственный аргумент на то True или False. Если передали больше одного аргумента — ошибка выполнения.
LinearLeopard
10.01.2020 14:53Ну, это же чушь? Повторюсь, а если я **kwargs передаю через цепочку вызовов? То мне делать дополнительные конвертации?
Ну если вам не надо, то никому не надо, это очевидно.
а это пускай просто проверяет единственный аргумент на то True или False. Если передали больше одного аргумента — ошибка выполнения.
Там более весёлый механизм.
И вы не перепутали тело ф-ции и сигнатуру?
olekl
10.01.2020 14:21-1continue в finally как-то странно. Во-первых, не очень понятно зачем, во-вторых, если я правильно понял как оно работает, то логичнее там был бы break…
gecube
Эти нововведения только лишь мне одному кажутся сомнительными ?
Аргументирую.
Моржовый оператор открывает дорогу в ад. Будет та же ситуация, что и в Си — поди догадайся в каком порядке вычисляются все подвыражения. Нужна однозначность, а она обеспечивается кодом в "старом" стиле. Если же мы говорим про эффективность интерпретации, ну, так интерпретатор сделайте умным — с JIT, blackjack и куртизанками.
format v2.0 — очень сомнительно. Стало меньше писанины? Ну, немного. Стоило? Нет.
continue в finally — выглядит как возможность создавать недостижимые участки кода. Зачем? В проекте в принципе не должно быть недостижимого кода.
Позиционные аргументы. Ну, оч.странно. Реально у кого-то от этого болело? Проблем с передачей **kwargs между функциями не будет ?
И только пп.4 и 5 выглядят более-менее полезными
andreymal
По-моему markdown сломал вам нумерацию
SirEdvin
Стоило чего? Разработки? Ну, python это open source, так что чуваку, который законтрибутил ок, наверное. А какие минусы?
Аналогичное же раньше делалось через bool переменную, так что какая разница?
Это стандартизация такая. В CPython методах такое было, а в обычном python нет. Вот исправили.
Regis
Это становится частью стандарта языка. А значит требует поддержки (усложнит добавление нового функционала в связанном коде) и усложняет альтернативные реализации Python.
WhiteBlackGoose
Первый пункт — это наоборот счастье. Сколько приходится сталкиваться с
А теперь я могу написать
И кто бы что не говорил про оптимизацию, про костыли вида функции возвращающей значение И изменяющей его же в самой функции, это хорошо. А вот остальное — да, не могу сказать, что прям что-то кардинальное. Форматирование в строках я честно говоря вообще не использую. Дебагпринты? Сомнительный резон.
gecube
не могу согласиться, к сожалению, богатый опыт написания кода на С/C++ говорит о том, что это потенциальное UB. В Вашем же примере — первый сниппет четко понятен. Второй — все равно будет внутри преобразован в первый пример, но при этом и выглядит ужасно, и вводит новую сущность, и когнитивная нагрузка на разработчика выше.
Единственное, где бы, ВОЗМОЖНО, я одобрил применение такого приема — это list comprehension и lambda, да и то не факт
germn Автор
Я в таких случаях делаю:
vanxant
Чем такое вот писать, лучше сразу расчехлить goto. Попробуйте, вам же понятнее будет.
WhiteBlackGoose
Не сильно лучше, чем через :=. Точнее имхо хуже
acmnu
Если уж за красоту бороться, то можно пойти дальше (хоть это и явно медленнее):
Кстати, я всегда жалею, что в Python нет обратного if, он так хорошо читается, как раз в таких коротких примерах.
iroln
История эволюции форматирования строк в Python полна боли и отчаяния. Интерполяция, которую завезли в 3.6 — это вершина этой эволюции и в большинстве случаев лучший и к тому же самый быстрый способ (для него сделали отдельную инструкцию в байткоде) форматировать строки. Попробуйте его использовать. Это правда круто и после него не хочется использовать
format
и%
. :)Касательно новой "отладочной фичи", совсем недавно это использовал в примерно таком коде с pytest:
ratijas
Вот только нужно понимать, что
format
и%
можно использовать для "отложенной" интерполяции, а f-строки — нет, всегда вычисляются сразу на месте. Отложенная интерполяция полезна, чтобы иметь возможность сохранить шаблон, передать, писать лог с параметрами — в последнем случае особенно хорошо экономится на бесполезной интерполяции, если уровень логгирования окажется ниже минимального.iroln
Я не предлагаю использовать f-строки там где нужно отложенное форматирование, для логгирования или шаблонов. Это, очевидно, специфические случаи.
Но в случае прямого форматирования строк нет никаких причин отказываться от f-строк.
Код получается более чистый, лаконичный и понятный (если, не писать в строках длиннющие выражения, конечно).
john_2013
gecube
Да, хороший пример, спасибо.
Zanak
Согласен с вами, странные плюшки.
1. Новый оператор требует, как минимум, тщательного объяснения его применимости. Даже приведенный пример с массивом, сколько людей способно понять, почему в этом случае он не сработает? Извращенный ум и шаловливые рученки могут породить еще массу вариантов, которые нужно будет проверять, прежде чем вставлять в реальный код. И кто выиграет от такого «улучшения»?
2. Было же простое и понятное правило, сначала идут позиционные, потом именованные аргументы. Зачем и кому потребовалась эта ужесть? Зачем в список параметров функции помещать что — то кроме параметров этой самой функции?
4. Поправьте меня, если я ошибаюсь, но есть просто dict и есть OrderedDict, который помнит порядок вставки. Если для второго — ок, наверное это кому — то нужно, то для первого ценность нововведения сомнительна, на мой взгляд.
П. п. 3 и 5 особой антипатии не вызывают, хотя, лично для меня, и особой ценности не представляют.
bruce-willis
2. рекомендую прочитать pep, где приведен вполне понятная (на мой взгляд) мотивация использования только позиционных аргументов. А также пример, как ужасно приходилось это реализовывать раньше вручную — www.python.org/dev/peps/pep-0570
4. начиная с 3.7, ключи в словаре упорядочены по порядку добавления — stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6.
Соответственно, обратный порядок тоже может быть полезен
vassabi
моржовый оператор — это попытка сделать дешевый аналог with для примитивных типов например
формат — довольно полезная ИМХО фича, особенно если при переименовывании переменной (нет дублирования текста, нет дублирования работы)
continue — это в примере нет условия, с условием сразу понятна полезность
erty
Я правильно понял, что первый оператор позволяет создавать рекурсии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!
mayorovp
Рекурсию и без него можно создать же...
erty
Я правильно понял, что первый оператор позволяет создавать рекурссии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!
erty
Я правильно понял, что первый оператор позволяет создавать рекурсии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!
semen-pro
3 коммента для наглядности?
ratijas
Всё нормально, он ушел в рекурсию…