Читать в телеге. Когда-то там были посты не только от меня.
Грабли генерации id в Hibernate
За использование фреймворков приходится платить. Пусть есть Entity с простым полем
@Id
val id: UUID = UUID.randomUUID()
Вроде все просто: при создании сущности ей сгенерируется id
, потом запишем его в базу одним запросом. Однако добрый EntityManager сделает тут один дополнительный запрос — а вдруг это не новая сущность? Поэтому приходится писать так:
@Id
@GeneratedValue
@Column(name = "id", updatable = false, nullable = false)
val id: UUID? = null
Тут мы информируем EntityManager, что это поле он должен сгенерировать сам (по умолчанию генерируется UUID), и что лезть в базу не надо, если id
отсутствует. А потом защищаемся от самих себя — запрещаем писать в базу сущность с отсутствующим id
, ведь компилятор это уже проверить не может. Зато он не знает ничего про генерацию этого поля и вполне справедливо считает, что раз оно может отсутствовать, то изволь переписать почти весь код его использующий, как минимум equals
и hashCode
.
(Скоро у меня кончатся идеи для постов и канал превратится в нытье про то, как я не люблю Spring и стандартный стек вокруг него).
Ускорение ввода-вывода в С++
Если вдруг пришлось залезть в такие дебри, что нужно это ускорять (например, чтобы сделать лабу по алгоритмам:)), то помогут две магические строчки:
ios_base::sync_with_stdio(false);
cin.tie(NULL);
Первая говорит о том, что нам плевать на C, его средствами мы читать/выводить ничего не будем, поэтому не будем синхронизировать буферы стандартных потоков ввода-вывода от С и C++. Вторая говорит о том, что не надо синхронизировать cin и cout (по умолчанию при любой операции над одним автоматически очищается буфер другого). Это можно себе позволить, если, например, программа однопоточная.
Чек-листы для кода и архитектуры микросервисов
https://kislayverma.com/programming/code-review-checklist-for-distributed-systems/ https://kislayverma.com/programming/design-review-checklist-for-distributed-systems/
Местами спорно и рассказаны довольно базовые вещи, но в целом годно — статьи дают неплохое представление, откуда начать.
Импорты в питоне
Python весьма неплох для стряпанья скриптиков. Однако если однотипных скриптиков становится много и появляется общий код, то тем или иным образом возникнет проблема с организацией папочек так, чтобы импорты работали нормально. Классический пример — импорт из “соседней” папки.
Проблема связана в основном с тем, что питон может быть запущен в непонятно каком окружении, в котором может даже не быть иерархической структуры модулей. Поэтому импортировать можно только из sys.path, в котором не всегда будет путь к нужному модулю. Основные способы решения проблемы сводятся к следующим вариантам:
- Вы все делаете неправильно, так писать код нельзя, срочно поменяйте свою структуру папок. Гвидо проклинает таких, как вы.
- Вы должны правильно настроить virtualenv, сделать одну из ваших папок пакетом и установить ее (как и любой пользователь ваших скриптов).
- Вы можете запускать свои скрипты как модули с флагом
-m
. Успехов вам в добавлении этого в мутные инструкции по запуску и в слежении за тем, чтобы при автоимпорте всех скриптов из папки ничего не сломалось. - Вы можете грязным хаком добавить нужный путь в sys.path, например, так:
Это самый простой и прямолинейный способ, но за него вы, разумеется, будете гореть в питоновском аду.
sys.path.append(str(pathlib.Path(__file__).resolve().parent.parent))
Подробнее про импорты можно прочитать тут и тут.
Я знаю, что среди немногочисленных читателей канала есть сеньор-питонисты — напишите пожалуйста в ЛС, как решаются подобные проблемы у вас.
Сборник просвещающих игр
Отличный сайт для изучения разных социальных и не очень проблем в игровом формате: https://ncase.me/.
Самая популярная — игра про доверие (базовая теория игр included!).
Еще можно отметить игры про системы голосования и сегрегацию.
В игровом формате можно узнать не только про что-то связанное с математикой, но и, например, про тревожность или про распространение ненависти.
Главный метод в Kotlin jar
Если написать такую программу
fun main(args: Array<String>) {
println("Hello, dudes!")
}
и скомпилировать ее через kotlinc 1.kt -include-runtime -d hello1.jar
, то при запуске через java -jar hello1.jar
получим приветствие.
Однако если то же самое сделаем с эквивалентным кодом
object Main {
@JvmStatic
fun main(args: Array<String>) {
println("Hello, dudes!")
}
}
то результат будет уныл:
no main manifest attribute, in ./hello2.jar
Почему не генерируется манифест можно посмотреть в исходниках компилятора. Через пару прыжков выходим на MainFunctionDetector, где, продравшись через условия, можно понять, что главными считаются только top-level функции, и в манифест Main-Class
добавляется только в первом случае. Хотя казалось бы, раз уж есть код для поиска главного метода, то ничто не мешает проставлять Main-Class
во всех случаях. Звучит довольно низкоуровнево, ведь у всех есть система сборки или IntelliJ на худой конец, но это все-таки баг.
Насколько быстро комп делает "типовые" операции?
Занимательный тест: https://computers-are-fast.github.io
Который вдобавок еще и забавный с учетом сегодняшнего индустриального стандарта, где на железе виртуалки, в виртуалках кубер, в кубере докер, в докере виртуальная машина языка, в ней — абстракции фреймворка, потом уже собственно код (причем это еще не самый страшный кейс), и все это еще и по HTTP.
Сериализация DTO python в JSON
В продолжение предыдущего поста. Чтобы иметь возможность сериализовать dataclass в JSON, можно сделать что-то вроде трейта и примешать его к датаклассам.
from dataclasses import dataclass, asdict
class Serializable:
def _asdict(self):
return asdict(self)
@dataclass(frozen=True)
class Person(Serializable):
name: str
age: int
p = Person("Jeshua", 33)
requests.get("http://httpbin.org/anything", json=p).json()
Эта штука вам вернет…
TypeError: Object of type Person is not JSON serializable
Но если поставить модуль simplejson
, который можно считать девелоперской версией встроенного json
, то ответ будет нормальным:
{... 'json': {'age': 33, 'name': 'Jeshua'}, ...}
Как же это работает?
Во-первых, в модуле requests
есть развилочка:
try:
import simplejson as json
except ImportError:
import json
Во-вторых, в модуле simplejson
есть встроенная поддержка namedtuple
: если у класса есть метод _asdict
, то он будет вызван для получения словаря, из которого потом будет сделан JSON. Т.е. с simplejson
namedtuple
можно просто передавать как параметр в dumps
без лишних телодвижений. А благодаря Serializable
датакласс маскируется под namedtuple
.
DTO в python
Еще в древнем 2.6 были namedtuple
:
from collections import namedtuple
Person = namedtuple("Person", "name age")
p = Person("Jeshua", 33)
print(p.name)
Они позволяли обращаться к полям по имени и были иммутабельными. Однако создание было не очень красивым.
Потом в 3.5 появились типы:
from typing import NamedTuple
class Person(NamedTuple):
name: str
age: int
p = Person("Jeshua", 33)
print(p.name)
Все то же самое, только запись поадекватнее, но это как-то пролетело мимо меня.
Наконец, в 3.7 появились dataclasses:
from dataclasses import dataclass
@dataclass(frozen = True)
class Person:
name: str
age: int
Казалось бы, то же самое, но есть нюансы: можно сделать мутабельными, можно организовать наследование, значения по умолчанию, сравнение более безопасное и еще пара фич. В общем, чтобы не запоминать 3 штуки, проще запомнить dataclasses и использовать везде их.
Хранение нескольких версий продукта в git
В продолжение поста про работу с ветками — неплохой доклад про то, как можно организовать поддержку кучи версий продукта в репозитории. Применение, конечно, довольно ограниченно и имеет смысл в реалиях, когда несколько клиентов, которые на поддержке и платят бабки. Да и конечное решение похоже на “так исторически сложилось”.
Однако доклад все равно полезный — в нем и кратенько про git и github flow рассказано, и проблема более-менее четко обозначена, и показаны варианты решения с плюсами и минусами.