All streams
Search
Write a publication
Pull to refresh
51
0.2
Valentin Nechayev @netch80

Программист (backend/сети)

Send message

На лаптопах обычно не ставят динамик для этого, а вот у десктопных материнок есть контакты, а если не сильно дешёвая, то и крошечный динамик в комплекте, кто хочет, тот прицепляет его.

Вот даже внутри кода, а не снаружи от тела текущей функции и без Type Annotations?

Ну где написать def make_adder из моего примера - вопрос стиля и чуть производительности, может, тут будет оптимизация, если увидят, что сама эта функция "чистая" и окружение не захватывает. Можно и снаружи. Так и класс описывать можно и внутри, и снаружи. Если снаружи - к нему надо идти, его читать, ещё и в логике отдельно инициализатор, а отдельно __call__, а если def, то всё это более связно.

Вдобавок, я просто очень не люблю вложенные функции и использование там "внешних" переменных.

Вот это точно вкусовщина. Я понимаю, но именно вариант описать где используется и предельно прямо в коде - по мне (и по команде), выигрывает в среднем. Дао Питона, как известно, "явное лучше неявного", даже когда от этого страдает производительность.

а вот в случае с lambda, по крайней мере, PyCharm туда не залезает.

Это явная недоработка PyCharm, но да, может быть проблемой. У меня самые толстые проекты, где реально нужно что-то отлаживать, это телефония, там интерактивным отладчиком не залезешь - надо только логать. Поэтому мне такая разница не очень важна. Имя функции в стектрейсе, да, регулярно важно.

И поменять я ее могу w._x = 2*w._x, Ваш ход?

func.__closure__. Неизменяемо, да (ну если без особых хаков), но и не было нужно. Опять таки специфика, вот ещё ни разу за 20+ лет с питоном не делал такое;
Заранее признаю, что мой домен сильно перекошен.

Но вот читабельность мы сравнивали - и явный def в среднем лучше всего, а вот делать класс с __call__ заметно проигрывало, по общему мнению команды.

Ну, совсем не проблема. Единственно что - порядок аргументов в случае лямбды неудобный (может, я ещё какого трюка не знаю), но в случае функции уже проблем нет:

adders = []
for x in (111, 222, 333, 444, 555, 666):
    ## Вот тут если писать lambda x=x, y - ругается: 
    ## SyntaxError: non-default argument follows default argument
    adders.append(lambda y, x=x: x+2*y)
for y in [-1, 0, 1]:
    print([w(y) for w in adders])
print('-'*64)
adders = []
def make_adder(x):
    def adder(y):
        return x + 2*y
    return adder
for x in (111, 222, 333, 444, 555, 666):
    adders.append(make_adder(x))
for y in [-1, 0, 1]:
    print([w(y) for w in adders])

Можно было бы ещё с functools.partial построить, но не хочется влезать. Вариант с def внутри функции-создателя (которая таким образом создаёт блок) более чем достаточен.

Вероятно, ваш вариант выгоднее по времени, тут уже надо бенчмаркать, и не на голом коде, а в чём-то более-менее реальном. Но мне сейчас неоткуда взять такое реальное. И для большинства читателей, я думаю, вложенная функция понятнее.

Можете привести пример именно в вашем стиле? (Хочу избежать ещё 1-2 циклов доводки/шлифовки.)

Это если вы встроенный float округляете:) А вот эффект следующего уровня:

>>> from decimal import *
>>> getcontext().rounding = ROUND_DOWN
>>> round(Decimal("1.5"), 0)
Decimal('1')
>>> round(Decimal("2.5"), 0)
Decimal('2')
>>> getcontext().rounding = ROUND_UP
>>> round(Decimal("1.5"), 0)
Decimal('2')
>>> round(Decimal("2.5"), 0)
Decimal('3')
>>> getcontext().rounding = ROUND_05UP
>>> round(Decimal("3.5"), 0)
Decimal('3')
>>> round(Decimal("4.5"), 0)
Decimal('4')
>>> round(Decimal("5.5"), 0)
Decimal('6')
>>> round(Decimal("6.5"), 0)
Decimal('6')

Функция стандартная round(), но зависит от контекста, определённого где-то там далеко, потому что реально у объекта она вызывает __round__(), а он уже знает, куда смотреть. А ещё round(x) и round(x, 0) тут работают по-разному.

В моём практическом случае нужна была именно функция, потому что какая функция вызывается - тоже было важно.

Ну, у меня была задача сравнить собственно функцию с функцией, в зависимости от метода создания. Довернуть ещё и объект с возможностью вызова как функция это уже следующий уровень наворота, полезный где-то на практике, но тут затрудняющий понимание примера.

Разумеется, он также показывает, как создание нового контекста (а тут это объект) помогает отвязаться от одного контекста на всех.

Вот тестик (давно валялся, вытащил к теме):

def moo(x):
    return repr(x)

def gen_moof2(i):
    def moof2():
        return moo(i)
    return moof2

if __name__ == "__main__":
    clos_l1 = []
    clos_l2 = []
    clos_f1 = []
    clos_f2 = []
    for i in range(4):
        p_i = str(i)
        clos_l1.append(lambda: moo(p_i))
        clos_l2.append(lambda p_i=p_i: moo(p_i))
        def moof1():
            return moo(p_i)
        clos_f1.append(moof1)
        clos_f2.append(gen_moof2(p_i))
    print(*[x() for x in clos_l1])
    print(*[x() for x in clos_l2])
    print(*[x() for x in clos_f1])
    print(*[x() for x in clos_f2])

Выводит, Python 3.10.12:

'3' '3' '3' '3'
'0' '1' '2' '3'
'3' '3' '3' '3'
'0' '1' '2' '3'

То есть от того, что мы просто завернули в функцию (локальный def) вместо лямбды, ничего не поменялось: копия значения для конкретного замыкания - не создалась.

Чтобы её создать, надо явно скопировать переменную. А чтобы эта копия была действительно уникальной - нужно своё пространство, что достигается, как сказано в статье, или аргументами лямбды, или отдельной функцией - генератором этой самой лямбды.

А то, что у def есть имя, а у lambda - нет, вопрос не связанный. Кстати, тут переопределение имени функции может быть нужно даже при def, если нам нужен какой-то уникальный идентификатор для трейса.

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

Со значениями по умолчанию у меня был случай, но не как аргумент функции, а как поле объекта: я определил его в классе. На этом состоянии релизеры отфоркнули версию. Я потом по ходу заметил и исправил, просто за компанию с другими проблемами, а потом сообразил... пришлось пинать службу поддержки: типа, в 18.0 эту функциональность использовать нельзя, срочно готовьте хотфикс... А мог и не заметить, тогда был бы вопрос "а почему система нормально живёт до первого трансфера, а потом ей сносит крышу".

И с переменными в лямбде нарывался, только мне тогда посоветовали синтаксис: funcs.append(lambda i=i: i) (автор тут разделяет x и i, что таки излишне).

Ну а сравнения в плавучке это вечная тема. Причём он только равенство вспомнил, с неравенствами не лучше:

>>> 0.1 + 0.2 <= 0.3
False
>>> 10.4 + 20.8 > 31.2
True
>>> 0.8 - 0.1 > 0.7
True

Это точно так же, как с управлением памятью по счётчику ссылок или GC по недостижимости. Вы, по сути, рекламируете GC по недостижимости, говоря, что он что-то умеет. Но практика альтернативных подходов во всех прочих БД показывает, что по счётчику ссылок работает, с теми же умениями. Так чем здешний GC по недостижимости лучше? Лучше ли он в статистическом большинстве сценариев?

Ну, специфический сценарий всегда можно подобрать.

Я просто к тому, что наблюдается проблема с этим счётчиком и отложенной сборкой при vacuum у всех (ну, 95+% или даже 99+%), а выгоду получают считанные единицы. Хорошо, пусть новые строки заводятся в той же таблице. Почему не сделать установку флага "свободен" на старых версиях строк (или удалённых строках) как часть процедуры фиксации коммита, без откладывания? Какая такая особенная ценность организации отдельного регулярного GC для этих строк?

или вообще 32-разрядный 68000? Они все на рынке уже были, появились в 1976-78 годах. Вот только цена у них была совсем разная.

Смотрим вики:

Formally introduced in September 1979, initial samples were released in February 1980, with production chips available over the counter in November. Initial speed grades were 4, 6, and 8 MHz. 10 MHz chips became available during 1981, and 12.5 MHz chips by June 1982.

То есть 68k безнадёжно опоздал, не было его тогда.

А вот про Z8000 - интересно послушать, чем он не подошёл, если рассматривали.

Вы с Агатами напрямую работали? Если да, с какой версией? У меня в школе были Агат-7, я их вдоль и поперёк знал, ну и немного пощупал Агат-9. Никакой слой совместимости не помог бы Агату поддержать формат данных видеопамяти Apple II. Агат-9 умел это специальным железом, а Агат-7 - нет, программы от Apple поэтому не шли. У них ещё была масса различий.

Ваши ссылки не в тему, потому что BBC Micro был совместим снизу вверх с Apple II, а для Dendy описана среда создания, но не запуска (ну как сейчас ничто не мешает на нём запустить даже эмулятор x86-64 процессора - всё будет, кроме скорости).

Всё больше начинает быть похожим, что MVCC стиля Postgres был ошибкой.
В варианте, когда строка заменяется на новую, а старая версия живёт в журнале до момента окончательной фиксации, можно делать хоть 256-битные счётчики, всё равно они много места не займут, а аналог vacuum выполнится просто дежурной чисткой журнала...

Или у стиля Postgres есть какие-то суперпреимущества, которые полезны в некоторых типовых сценариях?

Диалог:

— Какая интерпретация лучше - многомировая или пилотной волны?

— Не знаю, я в этом не копенгаген!

У Кена вообще замечательнейший блог...

Лучше forced-commands-only, при этом в authorized_keys записаны команды и их легко верифицировать.

Но Паркинсон при этом утверждал, что принцип Питера не работает.
https://n-t.ru/ri/pr/zp44.htm

ни тире, ни даже дефиса. Только минус.

Согласно последнему постановлению ~ВЦСПС~, немного не так:

002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;;

то есть он одновременно и минус, и дефис.

По отдельности они как раз есть в юникоде.

Information

Rating
2,661-st
Location
Киев, Киевская обл., Украина
Date of birth
Registered
Activity