Как стать автором
Обновить

Комментарии 75

Эти нововведения только лишь мне одному кажутся сомнительными ?


Аргументирую.


  1. Моржовый оператор открывает дорогу в ад. Будет та же ситуация, что и в Си — поди догадайся в каком порядке вычисляются все подвыражения. Нужна однозначность, а она обеспечивается кодом в "старом" стиле. Если же мы говорим про эффективность интерпретации, ну, так интерпретатор сделайте умным — с JIT, blackjack и куртизанками.


  2. format v2.0 — очень сомнительно. Стало меньше писанины? Ну, немного. Стоило? Нет.


  3. continue в finally — выглядит как возможность создавать недостижимые участки кода. Зачем? В проекте в принципе не должно быть недостижимого кода.


  4. Позиционные аргументы. Ну, оч.странно. Реально у кого-то от этого болело? Проблем с передачей **kwargs между функциями не будет ?


  5. И только пп.4 и 5 выглядят более-менее полезными


По-моему markdown сломал вам нумерацию

format v2.0 — очень сомнительно. Стало меньше писанины? Ну, немного. Стоило? Нет.

Стоило чего? Разработки? Ну, python это open source, так что чуваку, который законтрибутил ок, наверное. А какие минусы?


continue в finally — выглядит как возможность создавать недостижимые участки кода. Зачем? В проекте в принципе не должно быть недостижимого кода.

Аналогичное же раньше делалось через bool переменную, так что какая разница?


Позиционные аргументы. Ну, оч.странно. Реально у кого-то от этого болело? Проблем с передачей **kwargs между функциями не будет ?

Это стандартизация такая. В CPython методах такое было, а в обычном python нет. Вот исправили.

А какие минусы?

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

Первый пункт — это наоборот счастье. Сколько приходится сталкиваться с


b = myfunc(a)
while b > 0:
    a += 1
    b = myfunc(a)

А теперь я могу написать


while b := myfunc(a) > 0:
    a += 1

И кто бы что не говорил про оптимизацию, про костыли вида функции возвращающей значение И изменяющей его же в самой функции, это хорошо. А вот остальное — да, не могу сказать, что прям что-то кардинальное. Форматирование в строках я честно говоря вообще не использую. Дебагпринты? Сомнительный резон.

не могу согласиться, к сожалению, богатый опыт написания кода на С/C++ говорит о том, что это потенциальное UB. В Вашем же примере — первый сниппет четко понятен. Второй — все равно будет внутри преобразован в первый пример, но при этом и выглядит ужасно, и вводит новую сущность, и когнитивная нагрузка на разработчика выше.
Единственное, где бы, ВОЗМОЖНО, я одобрил применение такого приема — это list comprehension и lambda, да и то не факт

Я в таких случаях делаю:

while True:
    b = myfunc(a)
    if b <= 0: break
    a += 1
Чем такое вот писать, лучше сразу расчехлить goto. Попробуйте, вам же понятнее будет.

Не сильно лучше, чем через :=. Точнее имхо хуже

Если уж за красоту бороться, то можно пойти дальше (хоть это и явно медленнее):


import sys

for a in range(sys.maxint):  # Python 3 range
  b = myfunc(a)
  if b<=0: break

Кстати, я всегда жалею, что в Python нет обратного if, он так хорошо читается, как раз в таких коротких примерах.

Лучше бы они do-while сделали

do:
   b = myfunc(a)
   a += 1
while b <= 0
Форматирование в строках я честно говоря вообще не использую. Дебагпринты? Сомнительный резон.

История эволюции форматирования строк в Python полна боли и отчаяния. Интерполяция, которую завезли в 3.6 — это вершина этой эволюции и в большинстве случаев лучший и к тому же самый быстрый способ (для него сделали отдельную инструкцию в байткоде) форматировать строки. Попробуйте его использовать. Это правда круто и после него не хочется использовать format и %. :)


Касательно новой "отладочной фичи", совсем недавно это использовал в примерно таком коде с pytest:


...
sizes = ...
ndims = ...
...
ids = [f'{size=}|{ndim=}' for size, ndim in zip(sizes, ndims)]
...
metafunc.parametrize('size, ndim', [sizes, ndims], ids=ids)

collected 83 items

test_foo[size=100|ndim=2] .....
test_foo[size=100|ndim=3] .....
...

Вот только нужно понимать, что format и % можно использовать для "отложенной" интерполяции, а f-строки — нет, всегда вычисляются сразу на месте. Отложенная интерполяция полезна, чтобы иметь возможность сохранить шаблон, передать, писать лог с параметрами — в последнем случае особенно хорошо экономится на бесполезной интерполяции, если уровень логгирования окажется ниже минимального.

Я не предлагаю использовать f-строки там где нужно отложенное форматирование, для логгирования или шаблонов. Это, очевидно, специфические случаи.


logger.info('hello, %s', username)  # logging
logger.info('hello, {}', username)  # loguru

Но в случае прямого форматирования строк нет никаких причин отказываться от f-строк.


'hello, %s' % username
'hello, {}'.format(username)
f'hello, {username}'

Код получается более чистый, лаконичный и понятный (если, не писать в строках длиннющие выражения, конечно).

Данный случай и правда выглядит неплохо, но вот самый востребованный кейс из статьи Python 3.8: Что нового и как этим пользоваться

Это:
if (match1 := pattern1.match(data)):
    result = match1.group(1)
elif (match2 := pattern2.match(data)):
    result = match2.group(2)
else:
    result = None


Вместо:
match1 = pattern1.match(data)
match2 = pattern2.match(data)
if match1:
    result = match1.group(1)
elif match2:
    result = match2.group(2)
else:
    result = None


Выглядит как попытка добавления синтаксического мусора в язык. Количество логики в данном случае не уменьшилось, зато теперь надо парсить глазами содержимое между скобок.

В первом варианте pattern2.match не будет вызываться лишний раз. Во втором варианте он будет вызываться всегда, что потенциально может замедлить программу (иногда сильно)

  1. Думаю нагляднее был бы пример с условием в finally

if i > 0: 
    continue
print('test')

Да, хороший пример, спасибо.

Согласен с вами, странные плюшки.
1. Новый оператор требует, как минимум, тщательного объяснения его применимости. Даже приведенный пример с массивом, сколько людей способно понять, почему в этом случае он не сработает? Извращенный ум и шаловливые рученки могут породить еще массу вариантов, которые нужно будет проверять, прежде чем вставлять в реальный код. И кто выиграет от такого «улучшения»?
2. Было же простое и понятное правило, сначала идут позиционные, потом именованные аргументы. Зачем и кому потребовалась эта ужесть? Зачем в список параметров функции помещать что — то кроме параметров этой самой функции?
4. Поправьте меня, если я ошибаюсь, но есть просто dict и есть OrderedDict, который помнит порядок вставки. Если для второго — ок, наверное это кому — то нужно, то для первого ценность нововведения сомнительна, на мой взгляд.

П. п. 3 и 5 особой антипатии не вызывают, хотя, лично для меня, и особой ценности не представляют.
2. рекомендую прочитать pep, где приведен вполне понятная (на мой взгляд) мотивация использования только позиционных аргументов. А также пример, как ужасно приходилось это реализовывать раньше вручную — www.python.org/dev/peps/pep-0570
4. начиная с 3.7, ключи в словаре упорядочены по порядку добавления — stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6.
Соответственно, обратный порядок тоже может быть полезен
> 2.…
Ок, кому — то чисто позиционные параметры нужны, я, хоть и не согласился, но понял «зачем». Чего я точно не понимаю, и вообще, слегка в шоке, «почему так»? Ну приспичило вам вам запретить обращение к параметру по имени, ну так возьмите и пометьте каждый такой параметр каким нибудь значком, или даже слово служебное придумайте. Все остальные параметры тусуем как хотим, а этот желаем видеть только на третей позиции, к примеру, и ни как иначе.
> 4.…
То, что в текущей реализации словарь оставляет значения в порядке вставки, это, скорее детали реализации, нежели дизайна языка. На мой взгляд, плохая идея — использовать эту особенность в чем — то, что будет запускаться не только вами и не один — два раза.
моржовый оператор — это попытка сделать дешевый аналог with для примитивных типов например
формат — довольно полезная ИМХО фича, особенно если при переименовывании переменной (нет дублирования текста, нет дублирования работы)
continue — это в примере нет условия, с условием сразу понятна полезность
Я правильно понял, что первый оператор позволяет создавать рекурсии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!

Рекурсию и без него можно создать же...

Я правильно понял, что первый оператор позволяет создавать рекурссии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!
Я правильно понял, что первый оператор позволяет создавать рекурсии, вызывая в себе свою же функцию? Никаких запретов на это нет? Как этот ад отлаживать?!

3 коммента для наглядности?

Всё нормально, он ушел в рекурсию…

> Будет та же ситуация, что и в Си — поди догадайся в каком порядке вычисляются все подвыражения.

Зато, если порядок выражений чётко определён (через зависимости в and или or), получается колоссальное сокращение в коде вида

if (a:=f1()) and (b:=a.f2()) and (c:=f3(b,a,444)) > 10 or (c:=f4(x,y,z)) > 0:
   ....


А такие конструкции очень часты при операциях, например, со сложными структурами данных и опциональными параметрами в них.

И в такой конструкции (абстрагируемся от Питона) мы гарантированно получаем два поведения в зависимости (как в сях) от опции компилятора — оптимизировать вычисление логического выражения, если его результат уже ясен, или высчитывать все равно каждое его подвыражение. Чегой-то я не хочу в таком мире жить...

Простите, откуда это про «от опции компилятора»?
В C всегда и для всех, для && и ||, если результат выражения однозначно ясен по левой части, правая не вычисляется.
В Python аналогично для and и or. Цитата из документации:

> The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.
> The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

> абстрагируемся от Питона

Зачем нам абстрагироваться от Питона? Мы пишем на вполне конкретном Питоне.
> Окей, это родовая травма от Паскаля
Ясно.
Ada, Erlang переделали это — есть отдельно and, or, а есть andthen и orelse.

> Но в любом случае — такой код я не считаю best practice
Почему?
С точки зрения обычного рубиста это выглядит так:
1) Ну наконец-то догадались сделать, и даже оператор присвоения из Паскаля — не слишком плохой вариант
2) Выглядит как ересь, или призыв сотоны однострочником. Весь мир двигается к именованным аргументам
3) Странная у вас интерполяция, явно js покусал
4) А этого не было?!
5)… все равно мета возможности рубей сломают мозг с большей вероятность (читай: руби тут мощнее)
6) Ну и хорошо

> Весь мир двигается к именованным аргументам

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

Однозначно, чем дальше — тем хуже!

Можно аргументов? Я например, за 4 и 5 пункт, а остальное надеюсь станет deprecated в ближайшее время.

Пункт 1 это победа

Вопрос тогда только один — а куда, на какой язык перекатываться? Пайтон привлекал и привлекает тем, что у него батарейки включены. И он очень хорошо заменяет баш для задач администрирования. Ну, и весь ML на нем.

НЛО прилетело и опубликовало эту надпись здесь

автоматизация (сисопс, девопс, гитопс....)
ML
написание простых http сервисов

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

А в чем преимущества писать http-сервисы на Haskell?

НЛО прилетело и опубликовало эту надпись здесь

Если хочется простые http сервисы и язык без bells and whistles, то вроде Go на такое упирает, там как раз не любят без особой нужды новые фичи включать.

НЛО прилетело и опубликовало эту надпись здесь

Нет, уж, спасибо. Для руби надо быть каким-то одаренным....

Что ж, теперь можно и взяться за изучение Python
С моей точки зрения, самое интересное нововведение, это multiprocessing.shared_memory.

P.S. Сам, правда, еще не пробовал ;)

Спасибо за обзор. В чужом коде буду узнавать, если увижу. У меня самого энтузиазма новые фичи не вызвали.

def my_func(a, b, /, c, d, *, e, f):
    return a+b+c+d+e+f  
my_func(1, 2, 3, 4, 5, 6)
Это вообще ужасно выглядит. Как будто у функции 8 параметров, но указывать надо 6.

# так делать НЕ НАДО
a = 5
d = [b := a+1, a := b-1, a := a*2]
А вот и задачки для горе-собеседований подъехали.

А когда порядок элементов словаря стал определённым, что понадобился reversed() или это для OrderedDict() ?

С версии 3.7 Python'ий dict помнит порядок вставки своих элементов.


Dictionaries preserve insertion order.
Словари сохраняют порядок вставки элементов.

На самом деле… порядок вставки стал запоминаться ещё с версии Python 3.5 или 3.6, в с версии Python 3.7 его сделали обязательной частью спецификации языка.

А ещё в PyPy даже для 2-й версии (по крайней мере в 5.6.0) точно так же insertion order — видимо, обобщили реализацию. У нас от этого пошло немного интересных эффектов:) там, где народ невольно завязался на стандартный для 2.x порядок.
а при чем там определенность порядка? Порядок элементов даже может быть случайным — и тогда reversed тоже будет случайным (только в обратном порядке).

При том, что вы вставляете в одном, а получаете в обратном. Этот порядок не случаен, а как раз вполне себе определён. При последовательных вызовах reversed() на одном и том же словаре вы будете получать один и тот же результат. А раньше вообще никакой порядок не гарантировался.

я не про «с версии 3.7», я про логику — что
reversed([a for a in dict]) равен [a for a in reversed(dict)]

А, мне просто показалось, что вы мне отвечали (у меня были проблемы с приходом уведомлений об ответах на почту и я в попытках исправить проблему поставил лишние галочки в настройках, в результате чего на почту стали приходить уведомления обо всех комментариях в теме, а не только о тех, которые являются ответом мне. Но сразу я этого не заметил и получилось так, что я написал комментарий, а минуту спустя на него прилетел «ответ»). Так-то, конечно, вы целиком и полностью правы.

вроде как начиная с 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.
Зачем спорить, какие фичи нужны или не нужны языку? Мне кажется более правильным стратегия, когда язык гибкий и поддерживает много всего, а уже корректное применение инструментов — на совести программистов. Кому-то действительно понадобится присваивание в условиях (на C/C++ я вообще постоянно эти пользовался), а кому-то будет невлом написать лишнюю строчку кода для большей наглядности. Кто-то пишет для себя, кто-то — для сообщества, кто-то — для заказчика. Везде разные исходные условия, разные стили программирования и разные задачи. Язык — он ведь и есть язык, должен быть живым. Как мы с вами по-русски говорим, но все со своими особенностями, выговором, любимыми поговорками и т.д., так и на языке программирования. Какие проблемы? Компилятор все равно все оптимизирует и переварит в почти один байт-кот… ))

Чой-то хабр сгрыз мой ответ.
Поэтому дублирую.


Только вот не задача. Ядро языка должно быть минимальным. Так сказать — минимальный набор возможностей для решения максимального количества задач. Потому что если мы ядро языка раздуваем, то как выше коллега заметил — компилятор/интерпретатор языка становится сложнее и действительно нужные фичи становится сложнее добавлять. + Скорость интерпретации/компиляции падает (отличный пример — скорость компиляции с++, можно успеть состариться, пока напичанный на нем проект соберётся) + сложность автоматической кодогенерации (а кто сказал, что она не нужна?)
А самое неприятное, что каждая не очень продуманная фишка языка приводит к новым неочевидным эффектам.
Вот в сообщении ttpss://habr.com/ru/post/483276/#comment_21108246 очень хороший пример. И не надо говорить, что адекватные люди такой код писать не будут, ок?


Я уж не говорю, что сравнивать естественные языки, на которых мы говорим, и языки программирования немного странно. Первые действительно развиваются, но и стоимость изменений в них не такая большая, как в ЯП. Лучше тогда уж сравнивать с искусственными языками. Ну, тем же эсперанто. И сильно ли он популярен? Была на него мода… Но вроде все уже пришли к тому, что язык интернационального общения — ломаный английский.


И смотрите какая интересная штука. Принципиально — за последние лет 40 ничего нового не придумали. Циклы? Так они и тогда были. Функции? Классы? Ввод-вывод? Ну, разве что ФП пошло в массы, но это как-то очень совпало с распространением действительно распределенных систем. Ну, и какие тогда реальные фичи внедрять, кроме синтаксического сахара?

Ну, и какие тогда реальные фичи внедрять, кроме синтаксического сахара?

Касательно CPython, думаю, преодоление проблем/наследия GIL и отсутствия полноценной многопоточности, реализация JIT с опциональной статической типизацией. В общем, двигаться в сторону повышения производительности, потому как с удобством и скоростью программирования на Python уже сейчас всё очень хорошо.


А в синтаксическом сахаре я не вижу ничего плохого, если он улучшает читабельность кода и повышает продуктивность разработчика.

Наши точки зрения аналогичны вкусам людей, одни из которых предпочитают стиль минимализма, а другие — романтизма или барокко. Одни будут стоять за чистоту и практичность ("вот зачем в эти смартфоны добавили сканер биометрии, когда можно пароли руками вводить"), тогда как другие будут отстаивать удобство и красоту ("а давайте ещё добавим в новую модель смартфона элегантный отсек для наушников / щеточку для ногтей / алкотестер / микрофаллоимитатор и т.п") Между этими крайностями всегда найдутся аргументы у каждой стороны. И нет, принципиальной разницы между естественными языками и ЯП я не вижу. И те и другие суть системы знаков.

Итерироваться по словарям теперь можно в обратном порядке с помощью reversed().

Вот такой код в бетах и 3.8.0 вызывал сегфолт:


list(reversed({}))  # or next(reversed({}))

В 3.8.1 поправили. Если используете 3.8.0 и reversed dict, лучше обновиться. :)

Зачем же ограничивать гибкость типов аргументов? В некоторых случаях использование имени параметра вместо его позиции будет бесполезным и неуместным. Ограничение так же позволит избежать проблем, если вы планируете изменить имена позиционных аргументов в будущем.


Некоторые ф-ции из стандартной библиотеки имеют такую сигнатуру, например bool.

Changed in version 3.7: x is now a positional-only parameter.


Что выглядит вполне логичным, действительно, какие имя дать этому параметру? `o`, `x`, `a`?

И вполне логично запретить вызов

bool(a=x)

Ну, это же чушь? Повторюсь, а если я **kwargs передаю через цепочку вызовов? То мне делать дополнительные конвертации?


bool(a=x)


а это пускай просто проверяет единственный аргумент на то True или False. Если передали больше одного аргумента — ошибка выполнения.

Давайте с самого начала: зачем вы вообще передаёте **kwargs без *args?
Ну, это же чушь? Повторюсь, а если я **kwargs передаю через цепочку вызовов? То мне делать дополнительные конвертации?

Ну если вам не надо, то никому не надо, это очевидно.

а это пускай просто проверяет единственный аргумент на то True или False. Если передали больше одного аргумента — ошибка выполнения.


Там более весёлый механизм.

И вы не перепутали тело ф-ции и сигнатуру?
a = 6

# Код ниже присваивает b значение a ** 2
# и проверяет, если b > 0
if (b := a ** 2) > 0: 
    print(f'Квадрат {a} это {b}.') # Квадрат 6 это 36.

Тут либо условие, либо print неправильные.

А что именно тут неправильно?

continue в finally как-то странно. Во-первых, не очень понятно зачем, во-вторых, если я правильно понял как оно работает, то логичнее там был бы break…
Не все же библиотеки 3.8 поддерживают.

Можно примеры таких библиотек? Если библиотека поддерживает 3.5/3.6 и особенно 3.7, то и 3.8 она будет поддерживать в большинстве случаев. 3.8 же ничего не ломает. В каких-то редких случаях, когда что-то сломалось, завязанное на низкий уровень, байткод, CPython API/Cython и т.п., обычно чинится авторами достаточно оперативно.


А библиотеками, которые до сих пор не поддерживают Python 3, не уверен, что вообще нужно пользоваться. Сейчас многие библиотеки в новых версиях уже 3.5 перестают поддерживать, потому что современный Python, если можно так выразиться, начинается с версии 3.6, какой смысл держаться за 2-ветку?


Мне вообще нравится, что сообщество раскачалось и перестало цепляться за старьё. Ну и кардинально ломать что-то в питоне перестали. В 3.6 устоялись аннотации и появились f-strings, в 3.7 async/await стали ключевыми словами (у некоторых это вызвало боль, например, Celery, TensorFlow), но с тех времен уже всё устаканилось и починилось.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории