Комментарии 77
Удалена поддержка таких браузеров, как Grail, Mosaic, Netscape, Galeon, Skipstone, Iceape, Firebird, и Firefox до версии 36.
Это шутка? Или как вообще браузеры поддерживались в языке?
Удалена поддержка таких браузеров, как…
Каким образом Python поддерживает браузеры?
A "basic JIT compiler for short regions" might be added to CPython 3.12 to compile only tiny sections of specialized code...
Так JIT добавили или нет, или Гвидо обманул? Помню старые новости, где это обещалось.
Нет. Забоялись. Пришлось перетрусить много op-codes (а это само по себе ой-ой-ой изменение), притулить оптимизатор (что тоже ой-ой-ой), и ещё кучу всего. Поэтому всю подготовительную работу, вроде, сделали, а сам JIT добавлять не стали и перенесли на следующий релиз, так как он задержал бы выход этого релиза.
Могут и дальше задерживать, так как там сейчас (наконец-то) очень серьёзная работа по избавлению GIL началась, что требует переписывания кучи кода, подсистем и библиотек. И пока всё идёт к тому, что блокирующие изменения будут просто откладываться до выпуска интерпретатора с режимом без GIL, так потом всё равно всё переписывать нужно будет.
или Гвидо обманул
Гвидо уже несколько лет как не главный...
"Добавлено inline-развёртывание списковых включений (comprehensions). Это нововведение дало возможность ускорить работу со списковыми включениями."
Вот эта штука ускоряет многие часто используемые вещи вроде list(some_generator) вещи в два раза и больше. 5% прироста это они поскромничали, на некоторых задачах там 15% прироста. Особенно если переписать циклы и проверки под новые присвоения переменных в выражениях.
Вместе в новым оптимизатором в сумме очень хороший прирост получается.
А почему о ещё одной киллер-фиче не написали?
while( ( a:=some_data_read_method() ) is not None ):
print(a)
Вместе с новыми дженериками, f-строками (которые больше не кривой велосипед, а именно то, что должно быть) и foward types язык снова стал современным. Не хватает только null-check оператора.
Потому что assignment expressions появились ещё в 3.8?
А коробит от := до сих пор, как и от for...else - лютая же наркомания
В вашем примере очень неочевидный control flow, из-за чего не сразу понятно, что он вообще делает. Мне кажется, вот так и короче, и понятнее:
for list_b in list_a:
if all(isinstance(item, int) for item in list_b):
list_c.append(list_b)
Так что ваш пример получился не в пользу for-else
:)
А можете за один проход, чтобы вот такое тоже работало?
def gen4():
yield from range(4)
list_a = [
(1, 2, 3, 4),
[1, 2, '3', 4],
[1, 2, 3, 4],
gen4()
]
list_c = []
for list_b in list_a:
if all(isinstance(item, int) for item in list_b):
list_c.append(sum(list_b))
print(list_c)
[10, 10, 0] # ,а хотеть [10,10,6]
( Код @Yuribtr -а тоже опустошает генератор )
Я не спорю, что условие задачи можно поменять так, чтобы именно тот синтаксис был уместным. Более читаемым он от этого не станет.
Можно написать так
for iterator in iterators:
total = 0
for item in iterator:
if isinstance(item, int):
total += item
else:
break
else:
result.append(total)
Можно так
for iterator in iterators:
total = 0
for item in iterator:
if not isinstance(item, int):
break
total += item
else:
result.append(total)
Хотя мне больше нравится такой вариант:
for iterator in iterators:
valid = True
total = 0
for item in iterator:
if not isinstance(item, int):
valid = False
break
total += item
if valid:
result.append(total)
В нем одновременно есть и "отрицательное условие" if not ...: break
(потому что именно так выглядит привычный паттерн проверки предусловий), и нет визуальной путаницы, к чему именно относится else
(повторюсь, по другим языкам мне намного привычнее ассоциировать его с ближайшим if, а не с циклом).
Если там ТАК воткнут генератор, то там уже независимо от количества проходов всё плохо. Потому что дальше кто-то другой захочет взять этот же массив, передать его куда-нибудь ещё и... ой, всё сломалось.
Генератор в данном случае будет перебран весь, т.е. смысла от непосредственно yield тоже не особо видно. На этом можно спокойно добавить одну строчку и перестать страдать ради... ачивки самого плохо сопровождаемого кода и "незаменимого специалиста который один знает как это всё классно работает"
for list_b in list_a:
arr = list(list_b)
if all(isinstance(item, int) for item in arr):
list_c.append(sum(arr))
Ну, это может быть библиотечная функция, которая, в частности, обрабатывает файл на проходе посимвольно, еще и фильтруя строки по какому-то принципу.
Так посимвольно или построчно? Там есть нюанс: читать файл побайтово - дико дорого и почти бессмысленно. Читать побайтово по сети - ещё дороже. Из базы данных - вообще безумие. Без практически ценной имплементации (какого-то реального кейса) этих костыликов, которая бы ЯВНО выигрывала у читаемых нормально вариантов всё это бессмысленно
def validate_codes(codes):
for code in codes:
if not isinstance(code, int):
warn(f"wrong code: {code}")
return False
return True
def extract_valid_codes(codes_list):
return [c for c in codes_list if validate_codes(c)]
Ну это вопрос привычки. Привыкаешь к такому синтаксису и потом легко читать.
Я не спорю, что я не привык. Но мне кажется, что это одна из наиболее непривычных фич Python, и я не уверен, что эта непривычность оправдана.
Сокращает, но вот удобнее читать и искать в этой каше что откуда почему - сильно спорный вопрос. Очень не очевидно, почему переменная проинициализированная в блоке проверки видна вне блока. Эта абракадабра - последнее место куда люди будут смотреть в попытках понять, откуда спустилась эта переменная. Давайте тогда вообще забьём на область видимости переменных
Второй пример вообще выглядит как издевательство
for list_b in list_a:
if all(isinstance(item, int) for item in list_b):
list_c.append(list_b)
А если Python не единственный язык разработчика (а возможно и не основной), то всё становится сильно трагичнее
Иронично, что мы написали прям дословно одинаковый код в одновременных комментариях :)
Еще одно подтверждение тому, что такой вариант куда более интуитивен, чем for-else
.
Любая инвертированная логика неинтуитивна, а for..else скорее всего задумывался для прямолинейной логики поиска, которая выглядит вполне логично:
for x in iterable:
if predicate(x):
break
else:
raise NotFoundException()
# work with x
И я бы даже предпочёл её более современному функциональному коду, который в питоне получается отвратителен:
if (x := next(filter(predicate, iterable)), None) is None:
raise NotFoundException()
if (x := next((i for i in iterable if predicate(i)), None) is None:
raise NotFoundException()
try:
x = next(filter(predicate, iterable))
except StopIteration:
raise NotFoundException()
from more_itertools import first_true # лучше, но нужен внешний модуль
if (x := first_true(iterable, pred=predicate)) is None:
raise NotFoundException()
Ваш первый вариант мне не нравится тем, что для меня ключевое слово break
не несет смысл возврата значения. Если бы в вашем примере predicate
не назывался так очевидно, я бы сначала подумал, что эта функция как-то меняет или потребляет значения из iterable
, и возвращает флаг "дайте мне еще данных".
Наверное, это дело привычки. Я не привык писать на Python, поэтому паттерны с for-else
не узнаю сразу.
В идеали, я бы хотел иметь find
в стандартной библиотеке, работающий как more_itertools.first_true
. И писал бы так:
x = find(iterable, predicate)
if x is None:
raise NotFoundException()
А из ваших вариантов мне ни один не нравится, что с for-else
, что другие. Если выбирать только из существующих способов, сделал бы как-то так:
x = filter(predicate, iterable)
x = next(x, None)
if x is None:
raise NotFoundException()
Но все равно некрасиво.
Я бы написал так:
found = any(predicate(x) for x in iterable)
if not found:
raise NotFoundException()
или так
found = any(map(predicate, iterable))
if not found:
raise NotFoundException()
Наверное, из нашей ветки это не очень понятно, но я (и @AMDmi3, думаю, тоже) предполагал, что найденный элемент будет еще где-то потом использоваться. В ваших вариантах только проверяется наличие элемента: any()
возвращает bool
.
Грубо говоря, представьте, что после всех наших отрывков кода стоит return x
.
Даже хотя бы
x = next(filter(predicate, iterable), None)
if x is None:
raise NotFoundException()
будет куда лучше, чем эти канделябры. Хотя в других языках было бы что-то вида
x = next(filter(predicate, iterable))
?? throw NotFoundException()
# в пайтоне ожидаешь что-то вида
x = next(filter(predicate, iterable), None)
or raise NotFoundException()
потому что в одну строку - хуже читается. Почему из пайтона нельзя вместо None бросить исключение в next - куда более интересный вопрос, чем все эти моржекостыли
В C# “out var” работает похожим образом и ничего, удобно и читаемо.
Может потому, что есть ещё «out» за который легко зацепиться глазами.
out var часто не рекомендуют использовать, если есть другие варианты. А с появлением кортежей они есть. А до их появления рекомендовалось вернуть объект, если уж надо прям много и разного вернуть. Плюс в C# протерянная переменная, или объявленная в двух местах или с не тем типом громко упадёт ещё при сборке, т.е. настолько забагованную вещь ты не сможешь отдать в прод даже если тесты продолбаны
Давайте я вас лучше наведу на мысль: почему бы тогда этому коду не быть корректным
for variable in array:
pass
print("Latest: ", variable)
Аргументация должна быть немножко интереснее, чем "это синтаксический сахар, значит надо пользоваться". Я пишу на .NET/C#, TypeScript (по сути JS), немного Kotlin... и потом переключаюсь на какой-нибудь скрипт для отчётов, а там вот такие синтаксические извращения не поддающиеся никакой логике, не переводящиеся на человеческий язык никаким боком. Как это читать? Для всех переменных из массива считаем что-то, иначе... какое иначе? Что иначе? Почему иначе? Иначе это нельзя было назвать хотя бы? Может это finally? Хотя там много других прекрасных слов можно было найти: another, otherwise... нейтив спикеры могли бы подсказать много куда более удачных вариантов
Я уже слишком стар чтобы замыливать глаз на вещи, которые создают больше проблем, чем решают. Иногда лучше написать лишнюю строку и получить более прозрачный алгоритм, который может посмотреть кто угодно (включая специалистов не являющихся программистами, а являющихся физиками, биологами и т.д.) без попытки угадать, какие сайдэффекты привносит сахар
Давайте я вас лучше наведу на мысль: почему бы тогда этому коду не быть корректным
Вам довольно прозрачно намекнули, что в изначальном примере кода c for ... else
нет «переменной, проинициализированной в блоке проверки и видной вне блока». Так что вот и не понятно, что в этом синтаксисе вас не устраивает.
В посте на который я изначально писал что очень не очевидно почему переменная видна вне блока, есть первый пример, про моржа
if not (value := extract_value(message)):
raise ValueError('Value is absent')
и второй, про for...else. И я в ответе достаточно очевидно отделил ответ на второй пример. Написать про второй пример, что он второй пример - это не достаточный для кого-то намёк что он второй? Серьёзно? Тогда `:=` действительно наименьшая из проблем...
:= довольно логичный, а вот for else на мой взгляд не такой.
Всегда кажется что else это если не прошёлся по коллекции.
while( ( a:=some_data_read_method() ) is not None ):
Киллер фичи, которые мы заслужили...
Убраны устаревшие методы в unittest
А там что-то не устаревшее есть? Параметризованные тесты оно не умеет и в целом после Pytest выглядит как что-то максимально бесполезное. Лучше бы Pytest в основную кодовую базу втащили
Ну все. Скоро PHP будет мертв
Ну, вот, а говорят питон простой язык. С каждым годом столько наворотов.
Так наоборот же, в этом релизе многое стало проще:
убрали депрекейтнутые функции (unittest, webbrowser)
добавили более адекватный синтаксис (параметры дженериков, type, override)
убрали неочевидные ограничения, о которые все спотыкались (парсинг f-строк)
увеличили полезность сообщений об ошибках
Мне кажется, что зря они f-строки ещё дальше расширяют. Некоторые коллеги в них начинают творить непотребства, функции вызывать, какую-то логику писать, в общем превращают питон в PHP
Мне кажется, что это стоит решать скорее насаждением стиля кода в команде. А для самого языка лучше быть консистентным - допускать в них любые выражения, без произвольных ограничений.
Ограничения f-строк меня уже давно раздражали, если честно. Особенно невозможность использовать бекслеши, или одни и те же кавычки.
А можете показать какой-нибудь ежедневный пример f-строки?
Я просто, как старовер, не люблю в них писать ничего сложнее условного f"hello {var}"
. Интересно, как ими пользуются коллеги по цеху в более сложных случаях.
Про код стайл согласен. Была бы возможность - настроил бы линтеры по вкусу.
Я не очень много пишу именно на Python, больше на JS/TS, в котором таких ограничений нет. Поэтому спотыкаюсь в простых случаях:
# здесь нужны другие кавычки
f"order id: {order["id"]}"
# здесь нельзя бекслеш
f"""
some header
{'\n'.join(rows)}
"""
Конечно, все это сразу же подсвечивается IDE и правится. А второй случай у меня чаще всего возникает в кустарных одноразовых скриптах, когда я мало забочусь о стиле кода, и нужно просто что-то быстро вывести в нужном формате. Но, тем не менее, немного раздражает.
А разве в первом случае в JS не нужны другие кавычки тоже ?
Обратную косую черту можно обойти дополнительной переменной хотя конечно и не так удобно.
nl = '\n'
print(rf"""
{nl.join(["a", "b"])}
""")"""
А разве в первом случае в JS не нужны другие кавычки тоже ?
В том и дело, что нет:
> var order = { id: 123 }, s = `order: ${order[`id`]}`; s
'order: 123'
Понятное дело, что и случаев таких в JS/TS меньше, потому что кавычки для форматирования - это не обычные одинарные и двойные, и потому что в "объекты-словари" можно ходить просто через точку (order.id
).
Обратную косую черту можно обойти дополнительной переменной хотя конечно и не так удобно.
Понятно, что можно обойти. Но, если честно, в Python мне чаще, чем во многих других языках, приходится искать вот такие обходные пути. В треде выше вот огорчился, что нет встроенного find(iterable, predicate)
, тоже приходится как-то обходить.
(параметры дженериков, type, override)
А кто-то этим пользуется в Питоне?
Вся эта возня с типами — она нужна исключительно для удобства разработки. Попытка превратить Питон в язык с таким же разнообразием средств типизирования, как в строго типизированных языках, мне кажется ошибочной.
Так без всего этого разрабатывать как раз бывает неудобно.
Вот тут выше только что, опять-таки, обсуждали, что в Python нужно либо брать из библиотеки, либо реализовывать самому функцию find(iterable, predicate)
. Ее, например, без дженериков адекватно не типизируешь. И так с очень многими библиотечными, "не-бизнесовыми" функциями.
превратить Питон в язык с таким же разнообразием средств типизирования, как в строго типизированных языках
python - строго типизированный язык
Это всё опционально для тех у кого проект дозрел
" Во внутренней строке можно повторно использовать двойные кавычки, не применяя одинарные. "
меня это прямо пугает, и еще интересно сколько % подсвечевалок синтаксиса это умеет
print(f"This is the playlist: {"\n".join(songs)}")
Решил проверить прибавку производительности новой версии python.
Запустил код на 4х версиях python (3.7, 3.9, 3.11, 3.12)
import time
times = []
def check():
strt = time.time()
sum_: int = 0
for x in range(1_000_000_000):
sum += x
times.append(time.time() — strt)
for x in range(5):
check()
print(times, sum(times) / 5)
Python 3.7.0: 37,76 с
Python 3.9.0: 34,83 с
Python 3.11.0: 32,28 с
Python 3.12.0: 41,22 с
Не смог понять, почему простой цикл на новой более оптимальной версии выполняется медленнее, чем на 3.7
ключ "-OO" указали же? Без него никто ничего ускорять не обещал.
Для таких измерений time.time не очень подходит, лучше взять time.perf_counter или timeit, иначе много стороннего влияния в подсчет, тем более большое кол-во итераций.
Предложен более компактный синтаксис аннотирования типов для обобщенных классов и функций. Вот пример:
def max[T](args: Iterable[T]) -> T:
Я правильно понимаю, что это просто чтобы T = TypeVar('T')
не писать?
Появился новый способ определения псевдонимов типов при помощи выражения type. Еще пример:
type Point = tuple[float, float]
В чем отличие от Point = tuple[float, float]
без type?
TypeVar
в целом выглядел как костыль. Рад, что от него избавляются.
Еще, например, forward references поддерживаются без костылей:
# старый костыль
# `: TypeAlias` можно не писать, но тогда
# это выглядит еще больше как просто строка
FooStr1: TypeAlias = "Foo1[str]"
T = TypeVar('T')
class Foo1(Generic[T]):
...
# новый синтаксис
type FooStr2 = Foo2[str]
class Foo2[T]:
...
А почему не упомянули про фабрику жадных корутин?
Там обещают прирост производительности до 50%
https://docs.python.org/3.12/library/asyncio-task.html#asyncio.eager_task_factory
Избавились и от устаревших и некорректно работающих функций, классов и методов. В первую очередь, это ... gzip.GzipFile ...
https://docs.python.org/3.12/library/gzip.html#gzip.GzipFile
Живее всех живых. Параметр у него один поменялся редко используемый только.
Год ожиданий — и мы получили Python 3.12. Изменения, новшества и дополнения