Pull to refresh
23
0.2
Send message

Недавно я осознал, что если функция "чистая", то есть не модифицирует входные аргументы, то такой код абсолютно нормальный:

Не совсем. Можно поменять аннотацию list на typing.Sequence. Мы по прежнему можем передать list в качестве аргумента, на как только начнем его мутировать внтури, статический анализатор запоёт.

def foo(var: int, checks: Sequence[Callable] = []):
	for check in checks:
		check(var)

В общем, питон - динамический. Очень динамический. Даже слишком. По
моему опыту, в 99% случаев мне эта динамика вообще не впёрлась - я знаю,
какие где типы ожидаются и какие атрибуты у моих классов, но я всё
равно плачу за "гибкость" питона. Плачу скоростью выполнения кода и
количеством ошибок.

На этой магии всё остальное работает. Иногда смотришь внутрь и удивляешься.

Как вы думает, что такое NamedTuple в этом примере из документации? https://docs.python.org/3/library/typing.html?highlight=namedtuple#typing.NamedTuple

class Employee(NamedTuple):
    name: str
    id: int

https://github.com/python/cpython/blob/main/Lib/typing.py#L2770

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

Как педант, педанту, в Питоне всё рантайм.

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

Я считаю, что спрашивать про case insensitive и мультимапы на собеседовании не имеет смысла. Если человек занет как мэпы устроенны, то он научится их использовать в пять минут.

Я никода не использовал case insensitive Map. И в отрытом вопросе врядли бы упомянул её. С мультимапами сталкивался (HTTP get params), но как отдельный класс их тоже бы не выделил, это же мапа со значеним типа лист.

Array, HashMap, Tree стоят в основе большиства стракур данных, всё остальное специфика, отсутствие опыта в которой не сделает кандидата хуже.

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

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

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

Я неплохо прокачался в структурах данных реализуя их или используя для получения лучшего (алгоритмического или по памяти) результата решая задачки на litcode.

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

Наши справочники обновляются очень редко. То есть, быстрее будет сделать frontend- и backend- части, тестирование и обучение для бизнеса, чем просто прислать excel-документ (более доступный для клиента) и далее написать миграцию на несколько строчек?

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

чем просто прислать excel-документ (более доступный для клиента)

Клиенты разные бывают. Иногда такие данные приходят, что надо 10 раз переспросить и переделать.

Если все равно придется вручную проверять, что лежит в БД, зачем тогда нужен еще один инструмент?

Вы эксперт в домене клиента, что сможете проверить 10К записей на соответсвование бизнес требованиями? Кто будет отвечать если вы проверили не так как надо?

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

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

Правильный - не значит самый экономный по затратам времени.

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

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

Это очень хорошо видно на Leetcode в секции решений.

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

Это сплошь и рядом случается. Я бы винил тут не знание, а подход "и так сойдёт".

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

А на дом, то в через тесты решается достаточно просто.

Ну это не такая простая задача. Там достаточно много заморочек, даже если ты знаешь алгоритм.

Вот например реализация в стандартной библиотеке Питона.

https://github.com/python/cpython/blob/24fb627ea7a4d57cf479b7516bafdb6c253a1645/Lib/collections/init.py#L83

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

Если файлы положить во временные каталоги, они удалятся по завершении тестов.

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

Monkeypatch

Пример использования не содержит теста. Его можно переписть намного проще.

os.getcwd = lambda: '/'

Вся сила этой фикстуры, в том что она возвращает старое значение после завершения теста.

Также у column есть 3 параметр type. В этом параметре указывается тип
формата данных, который лежит в csv. Но библиотека Liquibase достаточно
умная, чтобы сама понимать, что лежит, так что его можно не писать


Явное лучше неявного. Лучше пишите.

Так как изначально мы подготовили csv, то мы просто его скидываем и
просим поправить этот файл, при необходимости объяснив структуру csv

Если у вас есть экспертиза в вебе, то возможно CRUD будет быстрее сделать. Пусть клиенты сами всё правят.

Так что лучший способ профильтровать ошибки — на локальной среде
пытаться провести у себя, залив в базу данных, находя и исправляя
ошибки, на которые ругается Liquibase.

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

Я их мира Питона, там есть хороший инструментарий для этого (Pandas). Конечно тестирование с базой это не заменяет, но понять, что за данные и почисить их можно намнгого быстрее (если есть опыт работы с инстументами).

Это явно на мидла который готов себя продать по цене джуна.

Хотя если там подразумевается релокация, то минимальный ЗП должна быть на хорошем уровне.

Как результаты и сколько времени потратили?

Как скормить базе данных список из 10К офисов


10K это очень маленькие данные для базы данных.

Liquibase is an open-source database schema change management solution

По факту это инструмент для миграции, а не для данных.

Если заголовок прочитать, как: Сделать миграцию 10K записей, через Liquibase" то норм.

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

В Питоне это даже внесли в синтсксис языка. Можно сделать такую сигнатуру функции, когда вызов будет только в таком виде.

def myfunc(*, height, speed):
    print(f"{height=} {speed=}")

myfunc(height="speed", speed="height")
myfunc("speed", "height") # TypeError: myfunc() takes 0 positional arguments but 2 were given
myfunc(height="speed") # TypeError: myfunc() missing 1 required keyword-only argument: 'speed' 

А если миллион?

А давайте попробуем. Возьмём Питон, он не далает оптимизаций, и в черезмерном быстродействии не был замечен.

Цикл на миллион, запущен 100 раз. То есть 100 миллионов вызовов. Звучит серьёзно.

С дополнительной функцией 2.993393 секунд
Без дополнительной функции 2.991126 секунд

Итого 0.003 секунды разницы на одном из запусков.

Погрешность мужду запусками примерно 0.002. Сопоставимо с разницей.

from timeit import timeit


def foo():
    result = 0
    for x in range(1_000_000):
        result = result + x
    return result


print("Foo", timeit("foo()", number=100, globals={"foo": foo}))


def goo(a, b):
    return a + b


def boo():
    result = 0
    for x in range(1_000_000):
        result = goo(result, x)
    return result


print("Boo", timeit("boo()", number=100, globals={"boo": foo}))

Проверка на то, что там реально миллион вызовов.

from profile import run
run("boo()")
         1000005 function calls in 0.766 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.766    0.766 :0(exec)
        1    0.000    0.000    0.000    0.000 :0(setprofile)
        1    0.000    0.000    0.766    0.766 <string>:1(<module>)
        1    0.000    0.000    0.766    0.766 profile:0(boo())
        0    0.000             0.000          profile:0(profiler)
  1000000    0.375    0.000    0.375    0.000 scratch_15.py:14(goo)
        1    0.391    0.391    0.766    0.766 scratch_15.py:18(boo)

Лишние вызовы. Создание новых объектов (чтобы не таскать из метода в метод по 10 параметров)

Это копейки. На 10 вызовах в секунду вы даже померить разницу не сможете.

Новые обращения к диску и сети (потому что теперь объекты независимо обращаются итп).

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

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

Эм, вот и просадка - вместо одного запроса к серверу получаем N. Это уже не "спички" (вызовы), сетевой запрос - тяжёлая штука.


Тяжёлая. Но, если контент тяжелее издержек, то в параллель будет быстрее. Это к тому, что даже распиливание IO на части не всегда роняет производительность, а иногда может даже ускорять. Тут аксиомы не годятся, тут нужны измерения.

Если "в лоб" разделить код по модулям (согласно тому, кто за что отвечает), можно получить отдельные хождения в IO, которые уронят производительность.


С мыслью, что если делать как попало, то получится черти-что я согласен.

В моем мире среди "согласно тому, кто за что отвечает" как раз IO и будет таким компонентом разбиения. Чтение диска и работа с сетью, это низкоуровневый код и бизнес логика ничего об этом знать не должна. Это один из принципов того, как я разделяю на модули.

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

Нет, изи это просто проверить, что цикл есть, эта задача средняя.
https://leetcode.com/problems/linked-list-cycle-ii/

Information

Rating
2,834-th
Registered
Activity