Код на салфетке
2.22K subscribers
748 photos
15 videos
2 files
791 links
Канал для тех, кому интересно программирование на Python и не только.

Сайт: https://pressanybutton.ru/
Чат: https://t.iss.one/+Li2vbxfWo0Q4ZDk6
Заметки автора: @writeanynotes

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Вчера была опубликована очередная интересная задача из рубрики "Что выведет данный код?". Задача вам понравилась, судя по вашей активности в комментариях. Сама задача не очень сложная, основная её сложность в понимании того, как работает цикл for. Зная принцип его работы, отследить процесс можно прямо в голове, не запуская код.

В викторине приняли участие 29 человек. Верных ответов было ... всего 3, а если убрать редакцию, то остаётся вовсе один. Кто же ты, единственный верный? Пришли скрин верного ответа в чат, впишем тебя в пост! Лидирующим вариантом стал второй, с пустым списком, а на втором месте ошибка выхода за границы списка.


Код задачи:
def delete_starting_evens(lst):
for _ in lst:
if lst[0] % 2 == 0:
lst.pop(0)
else:
break
return lst


my_list = [2, 6, 8, 10]

print(delete_starting_evens(my_list))



Структура задачи:
Сперва мы объявляем переменную my_list, записав в неё список с целыми числами.

Затем вызываем print, передавая в него функцию delete_starting_evens со списком в аргументе.

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

Так почему результат именно [8, 10], а не какой-нибудь другой?
Всё дело в устройстве работы цикла for, работу которого достаточно трудно порой отследить в голове, не вызывая print'ов. Особенно сложно приходится, когда список, по которому проходится цикл, сокращается в процессе работы цикла.

Цикл for работает "с начала и до победного", т.е., получив на вход список, он будет работать до тех пор, пока его не прервут или пока список не закончится.

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


Рассмотрим полный цикл работы функции:
1. Функция получает на вход список [2, 6, 8, 10].
2. Значение переменной _ = 2 (нулевой элемент текущего списка). Список сейчас такой: [>2, 6, 8, 10]. > - указание на каком элементе находится цикл.
3. В блоке if мы проверяем чётный или нет 0-й элемент списка. Он чётный, удаляем его.
4. Значение переменной _ = 8 (первый элемент текущего цикла). Список сейчас такой: [6, >8, 10].
5. Снова проверяем является ли нулевой элемент чётным. Является, удаляем его.
6. Значение переменной _ = ? (Второй элемент текущего цикла). Список сейчас такой: [8, 10, >?]. Как видим, сейчас значение переменной _ выходит за границы списка в его текущем виде. Значит, мы дошли до конца.
7. Цикл for заканчивает свою работу не заходя внутрь.
8. Возвращаем список.
9. Выводим его на экран.


Заключение.
В данной задаче показан пример того, что будет если изменять размер списка ДО текущего положения счётчика. Он достаточно безобидный. Однако, если изменять размер списка ПОСЛЕ счётчика, могут возникнуть проблемы, особенно, если обращаться к стоящим после счётчика элементам, т.к. после изменения списка, он перестраивается и порядок индексации изменяется.
🔥7🤯2❤‍🔥1
Первый созвон...
Автор: Некий Вестник Сплетен

Время шло, и нужно было начинать работу, а с чего её начинать, никто не знал. Путём обсуждения в чате было решено, что нам нужен общий созвон. Удобное для большинства участников время решили выбрать путём голосования, постепенно сужая выбор предлагаемых вариантов. Таким образом созвон был назначен на субботу 28 октября 2023 года в 17-00.

Начали с обсуждения самой идеи проекта. Изначально вариантов было много, и все они были очень интересными (возможно, мы захотим реализовать в будущем и их). В обсуждении активно участвовала большая часть команды, из-за чего оно получилось очень живым. В итоге выбор пал на сервис поиска проектов или коллег для совместной работы (сайт - биржа фриланса).
😎3🔥2
Определившись с направлением проекта, мы приступили к подбору подходящего названия для проекта, название команды и аватарки для более удобного поиска чата в Telegram (всё общение в нашей команде проходит на созвонах и в чате тг).

Устроили голосование за название проекта. Выбирать предлагалось из следующих вариантов:
- DevMatch (Developer Match)
- ЯСделяль
- I try - You pay
- Command&Project hub
- KekProjekt
- iCommand
- iSearchCommand
- CommandHub

По итогам был выбран вариант DevMatch (Developer Match). Сперва хотели от него отказаться, так как такой сайт уже есть на зарубежном пространстве, но мы в РФ, что они нам сделают?

Следом запустили ещё два голосования: выбор аватарки нашего чата и название команды.

Варианты названий команд были самыми разными:
- Victims of Сourses
- Некий проект
- DevMatch Team
- Order or the Crooked Finger
- team of commanders
- little hard workers
- Команда Вечное сияние чистого разума
- Слабоумие и отвага

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

Далее стали думать над составлением ТЗ.
Один из участников нашёл в интернете образец-черновик технического задания, затем Андрей (BackEnd) скинул еще один вариант и мы начали работу над ТЗ, опираясь на эти файлы как на шаблоны.

Были внесены первые корректировки в ТЗ. На начальном этапе в основном этим занимались Иван и Антон.

Особо много там не написали, поскольку опыта в этом деле у всех было мало.

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

Оставался ещё один важный вопрос: в каком сервисе распределять задачи по участникам?

Евгением (UX/UI) было предложено использование сервиса YouGile для распределения задач. На нём мы и остановились. Пришлось изучать новые инструменты работы.

Итак, работа закипела.

Пост на сайте
Поддержать проект

#код_на_салфетке #техническое_задание #созвон #некий_проект #биржа_фриланса #название_проекта #командная_работа
🔥4😎1
Приветствую.

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

Мы ещё достаточно далеки хотя бы для MVP, поскольку особых сроков и дедлайнов у нас нет, но работа идёт.

Сейчас нам в команду требуются фронтэнд разработчики знакомые с Vue3.

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

Если вы хотите применить свои знания на практике и поработать в команде таких же новичков, то напишите мне @proDreams
🎉5
Прошёл ещё месяц учебы, вернее не учёбы, а её завершения.

Настало время "отчётного" поста на Plkabu.

https://pikabu.ru/story/obuchenie_vosemnadtsatyiy_mesyats_posledniy_11058755?utm_source=linkshare&utm_medium=sharing

Расчехляйте лайкомёты 😊
🔥7
Django 40. Собственные страницы ошибок
Автор: Иван Ашихмин

Ошибки на сайте случаются постоянно, но далеко не все из них происходят по вине программиста. Самая знаменитая ошибка – 404 Not Found – относится к пользовательским ошибкам, когда был совершён переход на несуществующую страницу. Ещё одна известная ошибка – 500 Internal Server Error – относится к ошибкам на стороне сервера. Это означает, что сервер не справился с какой-то задачей.

Всё это называется "Кодом состояния HTTP" или по простому "HTTP статус кодом".

Всего их пять видов:
1. 1xx - информационные.
2. 2xx - успешные.
3. 3xx - перенаправления.
4. 4xx - ошибки клиента.
5. 5xx - ошибки сервера.

Полный список кодов с описанием доступен в википедии: https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP

Нас интересуют 4xx и 5xx статус коды, проще говоря – ошибки.
🔥4
Мы с вами сделаем страницы отображающиеся при следующих ошибках:
- 400 Bad Request – когда был отправлен неправильный запрос или данные.
- 403 Forbidden – когда пользователь пытается получить доступ к страницам сайта, на просмотр которых у него нет разрешения.
- 404 Not Found – когда пользователь переходит на несуществующую страницу.
- 500 Internal Server Error – когда на стороне сервера что-то пошло не так и поднялась необработанная ошибка.


Утилитарное приложение utils_app.
Для обработки ошибок нам необходимо написать обработчики. Дабы не писать их в основных приложениях, создадим новое utils_app.
Для этого выполним команду: python manage.py startapp utils_app.

Не забудьте сразу добавить новое приложение в INSTALLED_APPS


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

В директории приложения создадим файл exception_handlers.py.

Создадим четыре функции:
- bad_request_handler - для обработки 400-й ошибки.
- permission_denied_handler - для обработки 403-й ошибки.
- page_not_found_handler - для обработки 404-й ошибки.
- server_error_handler - для обработки 500-й ошибки.

Каждая функция принимает два аргумента request и exception. Обратите внимание, что аргумент exception не всегда передаётся в обработчик, по этому устанавливаем ему значение по умолчанию None.

В каждой функции возвращаем работу функции render, передавая в аргументах request, путь до файла шаблона и статус-код ошибки.


Код функций:
from django.shortcuts import render  
from rest_framework import status


def bad_request_handler(request, exception=None):
return render(request, "utils_app/400.html", status=status.HTTP_400_BAD_REQUEST)


def permission_denied_handler(request, exception=None):
return render(request, "utils_app/403.html", status=status.HTTP_403_FORBIDDEN)


def page_not_found_handler(request, exception=None):
return render(request, "utils_app/404.html", status=status.HTTP_404_NOT_FOUND)


def server_error_handler(request, exception=None):
return render(request, "utils_app/500.html", status=status.HTTP_500_INTERNAL_SERVER_ERROR)



Шаблон страницы с ошибкой.
Нам нужно четыре шаблона. Создадим в директории с шаблонами новую директорию для приложения utils_app и в ней файлы шаблонов.

У меня четыре шаблона. Все четыре одинаковые, за исключением текста, поэтому покажу пример только одного шаблона:
{% extends "blog/base.html" %}
{% block title %}404 - страница не найдена!{% endblock %}

{% block content %}
<div class="d-flex align-items-center justify-content-center vh-100">
<div class="text-center">
<h1 class="display-1 fw-bold">404</h1>
<p class="fs-3"><span class="text-danger">Ой!</span> Страница не найдена.</p>
<p class="lead">
Запрашиваемая вами страница не найдена.
</p>
<a href="{% url 'blog:index' %}" class="btn btn-primary my-btn">Вернуться на главную</a>
</div>
</div>
{% endblock %}


Выводим текст соответствующий статус-коду.


Подключение обработчиков.
Осталось подключить обработчики. Для этого откроем главный файл urls.py, расположенный в директории проекта рядом с файлом settings.py.
🔥5
В нём в конце файла добавляем четыре переменные. Значением переменных будет строка вида "название_приложения.файл_с_обработчиками.название_функции_обработчика".

handler400 = "utils_app.exception_handlers.bad_request_handler"  
handler403 = "utils_app.exception_handlers.permission_denied_handler"
handler404 = "utils_app.exception_handlers.page_not_found_handler"
handler500 = "utils_app.exception_handlers.server_error_handler"


Обратите внимание! Чтобы собственные страницы ошибок заработали, параметр DEBUG в файле settings.py должен быть установлен в False!


Заключение.
Вот таким вот простым и нехитрым способом можно сделать собственные страницы ошибок в Django.

Файлы к посту, можно получить в боте по коду: 102630

Пост на сайте
Поддержать проект

#Python #Django #гайды #Bad_Request #страницы_ошибок #обработчики_ошибок #Forbidden #Internal_Server_Error #Not_Found
🔥8👍1
Что выведет код с изображения ниже?
Anonymous Quiz
16%
3
8%
2
45%
[0, 1, 2]
31%
TypeError
👍4🤯3
Что выведет этот код? №7
Вчерашняя задача - это яркий пример полиморфизма в ООП. Суть в том, что мы создаём "класс-потомок" и изменяем его поведение относительно наследуемого класса, но обо всём по порядку.

В викторине приняли участие 31 человек из них верно ответили всего 5. Большая часть сошлись во мнении, что должна выйти ошибка TypeError. Вторым популярным ответом стал [0, 1, 2], означающий, что должен вывестись весь список.


Код задачи:
class MyList(list):
def __init__(self, *args):
super().__init__()
for i in args:
self.append(i)

def __hash__(self):
return 1

def __str__(self):
return str(self.__len__())


lst = MyList(0, 1)
lst.append(2)
dct = {lst: lst}
print(dct[lst])



Разберём задачу.
Начнём с класса. Мы создали класс MyList и унаследовали его от встроенного в Python класса list. Этот класс, экземпляры хорошо известной нам структуры данных - списка.

В нашем вновь созданном классе мы переопределяем три метода:
- Конструктор класса __init__ – в этом методе инициализируем super-класс, т.е. вызываем метод __init__ из родительского класса. Далее в цикле добавляем все переданные позиционные аргументы в список, используя метод append, который мы унаследовали из родительского класса привычного нам списка.
- dunder-метод __hash__ – данный метод возвращает хэш-значение объекта класса. Если тип данных не хэшируется (как, например, типа данных список), то возвращается None. В нашем случае мы возвращаем значение 1, что соответствует True.
- dunder-метод __str__ – данный метод возвращает строковое представление объекта класса. Для списков это - "[1, 2, 3]", в нашем случае возвращается строка с указанием длинны списка (за это отвечает dunder-метод __len__).

После класса идёт ряд действий:
1. В переменную lst помещается объект созданного нами только что класса MyList с аргументами (0, 1). Тем самым формируется список [0, 1] (здесь и дальше мы будем употреблять слово "список" условно, называя так объекты нашего авторского класса, унаследованного от списка).
2. К списку добавляется число 2, получается следующий список - [0, 1, 2].
3. В переменную dct помещаем словарь, в котором ключ и значение - это значение, записанное в переменную lst (то есть объект нашего авторского класса со списком [0, 1, 2]).
4. Последним шагом выводим значение словаря передав объект из переменной lst как ключ.

Почему удалось добавить список как ключ?
Тут всё достаточно просто. Действительно, словарь не даёт делать ключом нехэшируемые объекты, и на то есть свои причины: списки и другие изменяемые типы данных не хэшируются, поскольку изменение приводило бы к изменению их хэша, а словари хранят ключи в хэш-таблице. Таким образом, записав значение по одному ключу, который в последствии изменится, мы не сможем получить записанные данные.

Что касается нашего случая, то когда мы переопределили dunder-метод __hash__, мы изменили поведение класса list, сделав его хэшируемым. Именно это позволило использовать переменную со списком в качестве ключа.

Почему в выводе 3?
И тут ответ "на поверхности". Когда для вывода объектов используется функция print, вызывается dunder-метод __str__. Мы переопределили его, возвращая вместо стандартного строкового представления списка – его длину.

Заключение.
Вот такая задачка у нас вышла. В задаче показан пример полиморфизма в действии, когда класс list изменяет своё поведение, будучи унаследован пользовательским классом.
🔥9🤯1
AIOgram3 16. Перевод голосовых сообщений в текст
Автор: Иван Ашихмин

Голосовые сообщения – весьма спорная тема. Кто-то без них жить не может, а кто-то ненавидит их всей душой. Если опустить прения на эту тему, то у голосовых сообщений есть одна проблема: их банально не всегда удобно слушать. Чаще всего голосовые сообщения – это "информация в моменте", и, когда появляется возможность прослушать их, они теряют свою актуальность.

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

Нейронных сетей, заточенных под распознавание голоса, свыше десятка, но они требуют серьёзных вычислительных мощностей, чего мой не самый мощный сервер не может предоставить.
🔥5😁1😱1
Готовые сервисы тоже есть, но практически все - платные. Не беря в расчёт "пробные периоды" из бесплатных (вернее с бесплатным лимитом в месяц) есть два сервиса:

- Google Cloud - думаю не стоит рассказывать о Google. Они предлагают бесплатный тариф - 60 минут в месяц.
- SpeechFlow - сервис, позиционирующий себя как "лидера рынка". Поддерживают транскрипцию с 14-ти языков. Предлагают бесплатный тариф: 30 минут онлайн-распознавания (на сайте) и пять часов по API в месяц.

Сервис от Google нам не подходит: 60 минут на целый месяц слишком мало, поэтому выбор был сделан в пользу SpeechFlow. Пять часов в месяц – тоже не то чтобы много, но с этим уже можно работать. Ну, и будет интересно посмотреть статистику длительности голосовых и их количества.


Регистрация и получение API-ключа.
Переходим на сайт сервиса: https://speechflow.io/ru/

Регистрация максимально проста, особенно если заходить через Google-аккаунт.

После регистрации попадаем в личный кабинет. Там в левой панели выбираем раздел "API".
На открывшейся странице нажимаем кнопку "Сгенерировать ключ API".
В появившемся окне будет две строки:

- KeyId - идентификатор ключа
- KeySecret - секретный ключ
ВНИМАНИЕ! Ключ отображается всего один раз! Сохраните его в удобном для вас месте.

Также на этой странице есть примеры кода на различных языках, включая и Python. Пример для Python избыточный. Мы возьмём его за основу и упростим для нашей задачи.


Отслеживание и расшифровка голосовых сообщений.
В посте "AIOgram3 14. Фильтруем запрещённые слова" мы писали обработку для проверки сообщений на запрещённые слова. В этом посте мы дополним ее функционалом проверки голосовых сообщений.

Но сперва добавим ключи в класс Secrets.

Откроем файл settings.py и в классе Secrets добавим две строки:
audio_key_id: str = "ваш_KeyId"  
audio_key_secret: str = "ваш_KeySecret"


В значение полей вставьте полученные от SpeechFlow данные доступа.


Обработка голосовых сообщений.
Откройте файл filter_words.py.

Тут у нас уже есть функция check_message с условием if message.text. Для того, чтобы "отлавливать" голосовые сообщения, нужно другое условие - if message.voice.

Добавим его ниже, использовав elif.

Сперва нам необходимо скачать голосовое сообщение с сервера Telegram. Для этого выполним несколько шагов:
1. Создадим переменную file_id, в которую получим идентификатор голосового сообщения.
2. Создадим переменную file, в которую асинхронно получим информацию о файле голосового сообщения.
3. Создадим переменную file_path, в которую получим путь до файла.
4. Создадим переменную file_name, в которой укажем путь и имя сохраняемого файла на нашем сервере. Убедитесь, что директория, в которой вы собираетесь сохранять файл, существует.
5. Асинхронно выполним команду скачивания файла.

Получается вот такой блок кода:
file_id = message.voice.file_id  
file = await bot.get_file(file_id)
file_path = file.file_path
file_name = f"files/audio{file_id}.mp3"
await bot.download_file(file_path, file_name)


Создаём четыре переменные, которые понадобятся далее в коде:
1. headers – в этой переменной создаём словарь с ключами keyId и keySecret, в которые передаём значения соответствующих полей из класса Secrets.
2. create_url – в этой переменной определяем URL-адрес, на который отправляем файл для транскрипции. Обратите внимание на query-параметр lang: в нём указываем язык голосового сообщения.
👍3🔥1😱1
3. query_url – в этой переменной определяем URL-адрес, на который будем обращаться для получения результата транскрипции.
4. files – в этой переменной создаём словарь с ключом file, в значение которого помещаем открытый аудиофайл.

Блок кода:
headers = {"keyId": Secrets.audio_key_id, "keySecret": Secrets.audio_key_secret}  
create_url = "https://api.speechflow.io/asr/file/v1/create?lang=ru"
query_url = "https://api.speechflow.io/asr/file/v1/query?taskId="
files = {"file": open(file_name, "rb")}


Далее отправляем POST-запрос на адрес create_url и записываем ответ в переменную response.
Если статус-код ответа равен статус-коду 200, проваливаемся внутрь условия. Иначе завершаем выполнение обработки.

Внутри условия в переменную create_result получаем JSON ответа.
Затем к переменной query_url добавляем идентификатор задачи из create_result и query-параметр resultType.
Всего есть четыре значения resultType:

1. Тип результата по умолчанию: формат JSON для предложений и слов с указанием начального и конечного времени.
2. Формат JSON для сгенерированных субтитров с указанием начального и конечного времени.
3. Формат SRT для сгенерированных субтитров с указанием начального и конечного времени.
4. Чистый текстовый формат для результатов транскрипции без указания начального и конечного времени.

Для ответа в чате лучше всего подходит 4-й вариант.

Блок кода:
response = requests.post(create_url, headers=headers, files=files)  
if response.status_code 200:
create_result = response.json()
query_url += create_result["taskId"] + "&amp;resultType=4"


Запускаем бесконечный цикл.
Внутри цикла отправляем GET-запрос на адрес query_url и сохраняем ответ в переменную response.
Если статус-код ответа равен 200, проваливаемся внутрь условия. Иначе прерываем цикл и завершаем выполнение обработки.

В переменную query_result получаем JSON ответа.

Далее у нас два условия:

Если в query_result по ключу code лежит значение 11000, значит, транскрипция завершена.
Проваливаемся внутрь условия.

Проверяем, что в query_result по ключу result есть данные, иначе прерываем цикл. Такое может произойти, когда сервису не удалось распознать речь на голосовом или если оно было без речи вовсе.

В переменную result получаем значение ключа result из query_result. Для удобства можно дополнительно применить метод .replace(), чтобы заменить два переноса строки, например, на пробел.
Асинхронно отправляем ответ на голосовое сообщение.
Удаляем файл с сервера.
Прерываем цикл.

Иначе-если (elif) в query_result по ключу code лежит значение 11001, значит транскрипция ещё не завершена и стоит попробовать чуточку позже.
Выставляем сон на три секунды и переходим к следующей итерации цикла.

Если оба условия не были удовлетворены, то мы прерываем цикл.


Полный код:
🔥4😱1
import os
import requests
import time

from botlogic.settings import bot, Secrets


elif message.voice:
file_id = message.voice.file_id
file = await bot.get_file(file_id)
file_path = file.file_path
file_name = f"/code/pressanybutton_bot/files/audio{file_id}.mp3"
await bot.download_file(file_path, file_name)

headers = {"keyId": Secrets.audio_key_id, "keySecret": Secrets.audio_key_secret}
create_url = "https://api.speechflow.io/asr/file/v1/create?lang=ru"
query_url = "https://api.speechflow.io/asr/file/v1/query?taskId="
files = {"file": open(file_name, "rb")}

response = requests.post(create_url, headers=headers, files=files)
if response.status_code 200:
create_result = response.json()
query_url += create_result["taskId"] + "&amp;resultType=4"
while True:
response = requests.get(query_url, headers=headers)

if response.status_code 200:
query_result = response.json()
if query_result["code"] 11000:
if query_result["result"]:
result = query_result["result"].replace("\n\n", " ")
await message.reply(f"&lt;pre&gt;&lt;code&gt;{result}&lt;/code&gt;&lt;/pre&gt;")
os.remove(file_name)
break
elif query_result["code"] 11001:
time.sleep(3)
continue
else:
break
else:
break



Заключение.
Качество распознавания не всегда хорошее. Тот же WhisperAI на модели small выдавал куда лучший результат, но он требователен к железу. Интересно, как быстро кончится лимит в 5 часов.

Файлы к посту, можно получить в боте по коду: 790636

Пост на сайте
Поддержать проект

#Python #Гайды #Telegram #AIOgram #Telegram_бот #AIOgram3 #бот #нейронные_сети #whisper #SpeechFlow #транскрипция #голосовые_сообщения
🔥5👍1😱1
Приветствую вас, друзья, в этот волшебный вечер пятницы!

А волшебен он именно тем, что впереди два дня свободы от трудовых будней, начальства и всяческой рутины. И выходные лучше всего начать с просмотра хорошего фильма.

Фильм: Семь

Год: 1995

Детектив Уильям Сомерсет - ветеран уголовного розыска, мечтающий уйти на пенсию и уехать подальше от города и грешных обитателей. За 7 дней до пенсии на Сомерсета сваливаются две неприятности: молодой напарник Миллс и особо изощренное убийство. Острый ум опытного сыщика сразу определяет, что за этим преступлением, скорее всего, последуют другие. Новости подтверждают его догадку. Поняв, что убийца наказывает свои жертвы за совершенные ими смертные грехи, детектив встает перед выбором: вернуться к работе либо уйти и передать дело своему менее опытному напарнику?

https://www.sspoisk.ru/film/377/?utm_referrer=www.google.com

Хорошего просмотра)
🔥6
Что выведет этот код? №8
🥳Нас уже 404🥳
😁12🎉5🔥4😱2🤪1
Вчера мы задали простую, но весьма хитрую задачку. Суть задачи заключается в понимании типов данных Python, а именно того, как работают изменяемые типы данных. Перед чтением разбора рекомендуем прочитать наш пост "Питон на измене".

В викторине приняли участие 44 человека! Верных ответов, правда, набралось всего 8, что равно 18-ти процентам... Самым популярным вариантом, оказался "Ничего из перечисленного выше", набрав 39%, следом за ним идёт первый вариант с 32% ответов.


Код задачи:
my_list = [[1, 2, 3]] * 3
my_list[0].append(4)
my_list[1].pop()
my_list[2].append(5)
print(my_list)



Разберём задачу.
Создаём переменную my_list в которой умножаем список [[1, 2, 3]] на три. Получаем следующий список: [[1, 2, 3], [1, 2, 3], [1, 2, 3]].

Далее производим ряд действий над элементами списка:
1. В нулевой элемент списка добавляем цифру 4.
2. Из первого элемента списка вынимаем последнюю цифру.
3. Во второй элемент списка добавляем цифру 5.
4. Выводим весь список на экран.

В итоге мы ожидаем получить [[1, 2, 3, 4], [1, 2], [1, 2, 3, 5]].

Почему тогда верный ответ `[[1, 2, 3, 5], [1, 2, 3, 5], [1, 2, 3, 5]]`?
Вспоминаем, каким типом данных является список? Верно, изменяемым. Ещё такие типы данных именуют "ссылочными".

Всё дело в том, что когда мы в самом начале умножали список [[1, 2, 3]] на три, мы по сути создали список из трех ссылок на один и тот же список.

Это легко проверить, запустив следующий код:
my_list = [[1, 2, 3]] * 3  
print(id(my_list))
print(id(my_list[0]), id(my_list[1]), id(my_list[2]))

>>> 2239539514496
>>> 2239534636480 2239534636480 2239534636480


Как видим, все три элемента списка ссылаются на одну и ту же ячейку памяти, т.е. на один и тот же список.

В связи с этим, изменяя один элемент списка, мы изменяем и остальные.


Заключение.
Вот так вот простейший с виду код может обмануть программиста.
🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
Мы ещё дипломы не получили, а ГБ уже предлагает сменить профессию...

Видео записал одногруппник/подписчик @mssw_wssm
😁7🤡2👀1