Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.
Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support
Last updated 2 weeks, 2 days ago
Канал для поиска исполнителей для разных задач и организации мини конкурсов
Last updated 1 month ago
Знаете ли вы о возможностях multistage build в Docker для снижения размера образов?
Многошаговая сборка (multistage build) в Docker позволяет использовать разные образы для промежуточной сборки и финального приложения, чтобы значительно снизить итоговый размер образа.
```
# Стадия сборки
FROM golang:1.20-alpine AS builder
WORKDIR /app
# Копируем только нужные файлы
COPY go.mod go.sum ./
RUN go mod download
# Копируем source и build
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp -ldflags="-w -s"
# Финальная версия
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Копируем только скомпилированный бинарник
COPY --from=builder /app/myapp .
# Ставим non-root пользователя
USER nobody
CMD ["./myapp"]
```
– В первом этапе мы используем полноценный образ для сборки.
– Во втором этапе берем только скомпилированный бинарник.
Таким образом, финальный образ занимает гораздо меньше места, чем образ с полностью установленными инструментами разработки.
Привет, друзья!
Прошла еще одна неделя, и у нас накопилось много интересных материалов: статьи, новости и полезные советы. Вот наш недельный дайджест:
Понедельник (09.12.2024) Дайджест за предыдущую неделю
Вторник (10.12.2024) Хотите узнать, как сочетать asyncio и aiohttp для асинхронного веб-скрапинга?
Пятница (13.12.2024) Пятничный кинорелакс
Спасибо, что остаетесь с нами! Надеемся, что эти материалы будут вам полезны. Удачи в новой неделе!
С уважением,
Команда канала "Код на салфетке".
Всем привет!
Помимо программирования, я активно изучаю и DevOps направление: Администрирование серверов, контейнеризацию, CI/CD и это только малая часть того, что нужно знать и уметь.
В своих проектах я применяю CI/CD для проверки кода линтером, тестирования (если написаны тесты), сборки и деплоя. На любом из этих этапов может произойти ошибка, и если GitHub уведомляет по почте, то собственный git на базе Gitea так не делает (или я не разобрался), тем не менее, уведомления на почту не очень удобны. Тогда я задался вопросом оповещений в Telegram, что позволит оперативно реагировать на события в пайплайнах.
Было найдено несколько готовых решений с разным уровнем функциональности, однако, только один из них поддерживал отправку в топики супергрупп. Тогда я решил, что неплохо бы написать свой Action.
Но на чём его писать? Можно бы было на Python, но я и так всё пишу на нём, хотелось "чего-то нового" и я стал выбирать между GoLang и Rust. Оба языка набирают популярность и имеют свои сильные и слабые стороны. Уж не знаю почему именно, но я выбрал для решения этой задачи Rust.
Начал "как водится", с чтения официальной книги по Rust. Она хорошо написана, но просто так, читая книгу что-то выучить трудно и я решил параллельно начать писать. В процессе, количество открытых вкладок поисковиков, Stack Overflow и других сайтов росло в геометрической прогрессии, это не считая вопросов, которые я задавал чату GPT, прося его объяснить мне в максимальных подробностях.
Результатом стала программа для выполнения в среде GitHub (и не только) Actions под названием Telegram Notify Action. Программа, при срабатывании триггера (они могут быть разные, например always
, как понятно из названия, срабатывает всегда, а failure
, только при сбое) получает данные из окружения текущего workflow и формирует текст сообщения, а затем отправляет его в Telegram-бота по API.
Если вам интересно, хотите опробовать или просто поддержать поставив "звёздочку", прошу в репозиторий: https://github.com/proDreams/actions-telegram-notifier
Буду рад вашим комментариям и отзывам!
А я пойду дальше читать учебник, наверняка придётся ещё не раз рефакторить написанный код, а возможно и добавлять новый функционал.
Вчера мы задали задачу, похожую на Задачу №46, но с небольшим отличием. Задачу решило 5 человек из 15-ти проголосовавших. Давайте резберём правильный ответ.
Задача:
Вы пишете функцию, которая выполняет фильтрацию и модификацию словаря на основе вложенных условий. Чтобы упростить логику, вы решили использовать итерацию по ключам словаря и изменять его "на лету". Что получится в результате выполнения написанного кода?
Код задачи
```
def filter_dict(data: dict[int, int]) -> dict[int, int]:
for key in list(data.keys()):
if key % 2 == 0:
data[key + 1] = data.pop(key) * 2
return data
data = {1: 10, 2: 20, 3: 30, 4: 40}
result = filter_dict(data)
print(result)
```
Разбор кода:
1. Функция filter_dict принимает словарь data
с ключами и значениями типа int
.
for key in list(data.keys())
проходит по всем ключам словаря. - Здесь используется list(data.keys())
, чтобы создать список ключей, так как мы будем изменять сам словарь data
в процессе итерации. Если бы мы проходили по data.keys()
напрямую, это могло бы вызвать ошибки.
3. Внутри цикла для каждого ключа проверяется, делится ли он на 2 без остатка (key % 2 == 0
). Это условие выделяет чётные ключи.
- Значение по этому ключу удваивается (data.pop(key) * 2
) и присваивается новому ключу, который равен key + 1
.
- Метод pop
удаляет старый ключ из словаря и возвращает его значение.
5. По завершении цикла функция возвращает модифицированный словарь.
data
, а результат работы функции выводится через print
. Правильный ответ: {1: 10, 3: 40, 5: 80}
Как это работает?
Разберём пошагово, что происходит с данным словарём {1: 10, 2: 20, 3: 30, 4: 40}
при прохождении через функцию:
1
(нечётный). Условие key % 2 == 0
не выполняется, пропускаем этот ключ. 2
(чётный). - Выполняется data.pop(2)
→ удаляем ключ 2
, возвращаем его значение 20
.
- Новый ключ 2 + 1 = 3
. Присваиваем значение 20 * 2 = 40
новому ключу 3
.
Теперь словарь выглядит так: {1: 10, 3: 40, 4: 40}
.
3. Итерация 3:
Ключ 3
(нечётный). Условие key % 2 == 0
не выполняется, пропускаем.
4. Итерация 4:
Ключ 4
(чётный).
- Выполняется data.pop(4)
→ удаляем ключ 4
, возвращаем его значение 40
.
- Новый ключ 4 + 1 = 5
. Присваиваем значение 40 * 2 = 80
новому ключу 5
.
Теперь словарь выглядит так: {1: 10, 3: 40, 5: 80}
.
Цикл завершён, функция возвращает финальный словарь {1: 10, 3: 40, 5: 80}
.
Почему результат именно такой?
1. Итерация по списку ключей, а не по словарю:
Мы создаём копию ключей (list(data.keys())
), чтобы изменения в словаре data
не влияли на процесс итерации. Без этого подхода могли возникнуть ошибки или пропуск ключей.
Метод pop
и добавление нового ключа:
Метод pop
удаляет чётные ключи и их значения из словаря. Удвоенное значение присваивается новому ключу (key + 1
). Это приводит к замене чётных ключей на нечётные с модифицированным значением.
Последовательность операций:
Изменения в словаре происходят строго по порядку, так как мы проходим по изначальному списку ключей. Это исключает влияние на порядок обработки данных.
Вчера мы предложили вам довольно простую задачу, в котором за, казалось бы, сложными операциями, скрывается сама простая ошибка. Задачу решило из проголосовавших человек. Давайте резберём правильный ответ.
Задача:
Вы экспериментируете с оптимизацией обработки данных. В представленном коде для фильтрации списка чисел используется вложенная функция и модификация самого списка во время его обработки. Цель задачи — понять, как работает данный код, и выяснить, какой результат будет выведен на экран.
Код задачи:
```
def process_data(data: list[int]) -> list[int]:
def modify_and_filter():
for i in range(len(data)):
if data[i] % 2 == 0:
data.pop(i)
yield i
indices = list(modify\_and\_filter())
return indices + data
data_lst = [1, 2, 3, 4, 5, 6]
result = process_data(data_lst)
print(result)
```
Разбор кода
Давайте разберём этот код построчно и посмотрим, что происходит.
Функция process_data
Это основная функция, принимающая список целых чисел data
и возвращающая обработанный результат.
Вложенная функция modify_and_filter
Эта функция должна:
data
. data.pop(i)
).yield
). Ключевой момент: modify_and_filter
— это генератор, так как она использует yield
. Вызов этой функции не выполняет её код сразу, а создаёт генераторный объект, выполнение которого начинается только при итерации.
Цикл for i in range(len(data))
Цикл проходит по индексам от 0
до len(data) \- 1
. Внутри цикла вызывается data[i]
для проверки условия.
Проблема: Во время работы цикла список data
модифицируется. Это влияет на длину списка и смещение элементов, что приводит к несогласованности индексов.
Удаление элемента data.pop(i)
Удаление элемента приводит к изменению длины списка и смещению индексов остальных элементов. Например, если удалить элемент с индексом 1
, то элемент с индексом 2
станет на место элемента 1
, а длина списка уменьшится.
Генератор возвращает индексы
Функция modify_and_filter
возвращает индексы удалённых элементов в процессе итерации.
Возврат результата
Функция process_data
создаёт список из всех возвращённых генератором индексов (indices
) и объединяет его с текущим состоянием списка data
.
Правильный ответ: IndexError
Почему возникает ошибка IndexError?
Рассмотрим, что происходит при выполнении программы:
data_lst = [1, 2, 3, 4, 5, 6]
. process_data(data_lst)
запускает modify_and_filter
. i = 0
, проверяется data[0]
(значение 1
). Условие не выполнено, идёт следующая итерация.i = 1
, проверяется data[1]
(значение 2
). Условие выполнено:data[1]
(pop(1)
), список становится [1, 3, 4, 5, 6]
.i = 1
.data[2]
, но из-за удаления индексы изменились: на месте data[2]
сейчас стоит 4
, а длина списка уменьшилась. Попытка доступа к несуществующему индексу приводит к IndexError
. Вывод
Ошибка в программе возникает из-за попытки изменить список во время его итерации. Это меняет длину списка и смещает индексы, что делает дальнейшие попытки доступа к элементам небезопасными. Для избежания подобных ошибок важно:
Модификация списка — распространённая ошибка новичков, но с пониманием устройства циклов и индексов её легко избежать.
Привет, Друзья!
Хотим порекомендовать вам авторский канал про веб-разработку и сетевые технологии - "Давайте про IT".
Богдан специалист в Python-разработке и охотно делится со всеми гайдами, а также снимает видео! Сейчас на его канале активно рассказывается про OpenWRT и обходы сами знаете чего ?
Наверняка вы найдёте много полезного для себя.
Сегодня разберём вчерашнюю задачу, где нам нужно найти пользователей сайта. Верно ответили 17% из 18-ти человек.
Задача:
У вас есть два списка посещений пользователей на сайте за разные дни. Необходимо найти пользователей, которые заходили на сайт в эти дни, но не были активны в промежуток между этими днями.
Условие:
- Первый список содержит идентификаторы пользователей, которые заходили на сайт в первый день.
- Второй список содержит идентификаторы пользователей, которые заходили на сайт во второй день.
- Третий список содержит идентификаторы пользователей, которые заходили на сайт между этими днями.
Код задачи:
```
day1 = {101, 102, 103, 104, 105, 106}
day2 = {104, 105, 106, 107, 108, 109}
midday = {105, 107, 110}
common = day1 & day2
inactive_in_between = common - midday
day2.add(102)
midday.add(103)
result = len(inactive_in_between |
midday -
day2)
print(result)
```
Разбор задачи
Перед нами три множества, представляющие пользователей, заходивших на сайт в разные дни. Всё, что нам нужно сделать, это найти пользователей, которые заходили в эти дни, но не были активны в промежуточный день.
day1
и day2
с помощью оператора &
(пересечение). common = day1 & day2
Оператор &
находит общие элементы между двумя множествами. В данном случае, мы находим пользователей, которые заходили на сайт в оба дня. Получаем:
common = {104, 105, 106}
\-
(разность). inactive\_in\_between = common \- midday
Оператор \-
убирает из множества все элементы, которые есть в другом множестве. Здесь мы убираем пользователей, заходивших в промежуток между днями. Остаётся:
inactive\_in\_between = {104, 106}
day2
пользователя 102, а в midday
— 103. day2.add(102)
midday.add(103)
Теперь:
- day2 = {102, 104, 105, 106, 107, 108, 109}
- midday = {103, 105, 107, 110}
result = len(inactive\_in\_between | midday \- day2)
Внутри функции len()
, мы выполняем две операции: |
и \-
, однако, выполнение идёт не по порядку. В Python приоритет операций с множествами такой, что сначала выполняется операция вычитания (\-
), а затем объединение (|
). Д
- Первым делом выполняется вычитание midday \- day2
. Убираем всех пользователей, которые были во второй день, из тех, кто заходил между днями:
{103, 105, 107, 110} \- {102, 104, 105, 106, 107, 108, 109} = {103, 110}
- Затем выполняется объединение с множеством inactive_in_between
. Объединяем множество пользователей, которые были неактивны в промежутке, с теми, кто заходил между днями и не зашёл во второй день:
{104, 106} | {103, 110} = {103, 104, 106, 110}
Теперь в результате остаются пользователи: {103, 104, 106, 110}
.
Осталось посчитать количество элементов в этом множестве:
result = len({103, 104, 106, 110}) = 4
Правильный ответ: — 4.
Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.
Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support
Last updated 2 weeks, 2 days ago
Канал для поиска исполнителей для разных задач и организации мини конкурсов
Last updated 1 month ago