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

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

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Сравнение array C и list Python
Автор: Arduinum628

Всем доброго дня! У меня был небольшой перерыв в написании статей, так как навалилось много дел. Заканчивал стажировку, параллельно занимаясь поиском работы и дописывая свой сайт-блог, весна и хорошая погода - всё это сделало своё дело. Сейчас я возобновляю свою работу над статьями и хочу выпускать их примерно раз в две недели. Также в ближайшем будущем я хочу запустить свой сайт-блог и дублировать свои статьи там.

Итак, продолжим. В предыдущей статье "Сравнение интерпретатора Python и компилятора C" я затрагивал вскользь темы array и list, а также циклов while. Поэтому в данной статье я сравню array (массив) из языка C и list (список) из языка Python, обход которых я сделаю на цикле while. Давненько я не писал статьи и даже очень соскучился по этому процессу. Так что приступаю к продолжению этой интересной и захватывающей рубрики.
🔥2
Сравнение array и list
Для начала начнём со знакомого многим питонистам list (список) из языка Python. Список - это неограниченная по размеру упорядоченная коллекция произвольных объектов. Для начала давайте создадим список в Python.


list_example = [1, 2, 3, 4, 5]
print(list_example)

Я создал список и назвал его list_example так как он не будет привязан к конкретным данным или назначению. Давайте запустим в IDE и посмотрим что он выдаст в эмуляторе terminal на моём Debian. Я перешёл с Kubuntu на Debian 12, но различий между двумя этими системами для демонстрации кода не будет. При нажатии на кнопку run python file в IDE VS Code видим следующий вывод.

В terminal


[1, 2, 3, 4, 5]

Функция print из строки print(list_example) печатает нам содержимое списка [1, 2, 3, 4, 5], в котором находятся числа. Прежде чем перейти к написанию кода с аналогичным функционалом на C давайте разберёмся что такое array.

Массив, или array, в языке C - это структура данных, которая позволяет хранить последовательности элементов одного типа. Это определение уже намекает нам на то, что в массиве на языке C не может быть разных типов данных. Чуть позже я объясню почему, а сейчас создадим массив, похожий на список, что я писал выше на Python.


#include <stdio.h>
#include <string.h>
int main(){
int array_example[] = {1, 2, 3, 4, 5};
char buffer[100];
sprintf(
buffer,
"[%d, %d, %d, %d, %d]",
array_example[0],
array_example[1],
array_example[2],
array_example[3],
array_example[4]
);
printf("%s\n", buffer);
return 0;
}

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

В terminal


$ gcc ./array_c.c -o array_c
$ ./array_c
[1, 2, 3, 4, 5]

После компиляции и запуска данного кода мы видим тот же результат, что и в коде на Python в эмуляторе terminal [1, 2, 3, 4, 5]. Давайте разберёмся, что мы вообще такое написали на С. Строка #include <string.h> включает заголовочный файл для компилятора, который нужен для работы со строками. В нашем случае заголовочный файл нужен, чтобы использовать функцию sprintf для формирования строки. Из предыдущей статьи вы помните, что строка int array_example[] = {1, 2, 3, 4, 5}; создаст массив целых чисел. Прошу обратить внимание, что вы не можете класть туда любые типы данных как это делается в языке Python! Компилятору явно указывается, что в массиве у нас тип данных int - целое число. Давайте взглянем на примере, что будет если положить в массив тип данных char или символ строки. При форматировании укажем %s спецификатор формата для char. Для этого давайте слегка поправим код.


#include <stdio.h>
#include <string.h>
int main(){
int array_example[] = {1, 2, 3, 4, "r"};
char buffer[100];
sprintf(
buffer,
"[%d, %d, %d, %d, %s]",
array_example[0],
array_example[1],
array_example[2],
array_example[3],
array_example[4]
);
printf("%s\n", buffer);
return 0;
}

Ещё раз компилируем и запустим код в terminal.


$ gcc ./array_c.c -o array_c
array_c.c: In function ‘main’:
array_c.c:5:40: warning: initialization of ‘int’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
5 | int array_example[] = {1, 2, 3, 4, "r"};
| ^~~
array_c.c:5:40: note: (near initialization for ‘array_example[4]’)
🔥2
Предупреждение нам говорит о том, что мы пытаемся добавить char символ к int массиву что сделать невозможно. Это сломает вашу программу так как компилятор ожидает только int тип в массиве. Перепишем код обратно на тот что был и снова перекомпилируем. Просто повторите предыдущий рабочий код, что был до изменений, и заново запустите команду компиляции. Всё должно работать как раньше.

Давайте разберём оставшиеся строки кода. Строка char buffer[100]; создаёт массив char из 99 символов и 1 символ конца строки \0. Это нужно для хранения символов строки char. Цифра 100 на самом деле не количество символов, а размер массива в байтах. Если вы введёте printf("%d\n", sizeof(char)); в свой код, то увидите что 1 char символ занимает 1 байт. Для сравнения 1 int число занимает 4 байта. Поэтому можно легко рассчитать какой размер буфера вам нужен для хранения массива char. Размер 100 байт в нашем случае очень избыточен, поэтому рассчитаем точный размер 5 * 1 + 1 = 6. Количество символов умножаем на вес одного в байтах и прибавляем 1 байт для символа конца строки. Квадратные скобки и запятые в нашем буфере не хранятся. Таким образом мы экономим 94 байта на компьютере. Программы на C требуют, чтобы вы следили за размером, который занимают ваши данные. Немного непривычно после языка Python, не так ли? На самом деле в Python тоже нужно думать о размере данных, но из-за того что он динамический и много прощает, следить за этим гораздо сложнее. Далее в функцию sprintf мы кладём наш буфер для хранения char, форматируем строку "[%d, %d, %d, %d, %d]" и для каждого спецификатора формата %d берём каждое число по индексу из массива. Далее функция sprintf заполнила наш buffer данными char в результате чего получился массив символов, последовательность которых образует строку. Ну и в конце выводим нашу строку в terminal (эмулятор терминала). В Python функция print сама выводила нам содержимое списка, а в C нам пришлось для этого форматировать строку. Возможности языка C и возможности Python разные и иногда в C просто нет простого решения. Давайте попробуем на Python написать близкий внешне вариант. Для этого перепишем наш Python код. Напишу два варианта: более старый и более новый и быстрый.


from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
str_list = '[%d, %d, %d, %d, %d]' % (list_example[0], list_example[1], \
list_example[2], list_example[3], list_example[4])
print(str_list)

Первый вариант использует оператор %, который более родственный языку C. Обратите внимание: я использую аннотацию типов List[int], показывая, что у нас должен быть тип данных list с числами int типа внутри. Это работает на уровне соглашения между разработчиками и не влияет на саму работу кода. И если туда добавить другой тип данных естественно ничего не сломается. Результатом в переменной str_list будет отформатированная оператором % строка [1, 2, 3, 4, 5], которую мы потом выводим в terminal с помощью функции print.


from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
str_list = f'[{list_example[0]}, {list_example[1]}, {list_example[2]}, '\
f'{list_example[3]}, {list_example[4]}]'
print(str_list)

Второй вариант использует f строки, которые работают быстрее оператора % и использование которых имеют более короткий и удобный синтаксис. Результат будет тот же самый что и в примере с оператором %. Если ваша Python версия поддерживает f строки, рекомендую использовать именно их.
🔥2
Далее я хочу пройти по списку и массиву с помощью цикла while и вывести каждое из чисел в списке. Довольно просто и банально, но моя цель - показать отличия Python от C, а не усложнять сам код. После того как я покажу вам основы, буду ставить задачи посложнее и писать более сложный код. Для начала напишем код на более простом языке Python.


from typing import List
list_example: List[int] = [1, 2, 3, 4, 5]
i = 0
while i <= list_example.index(list_example[-1]):
print(list_example[i])
i += 1

У меня получился довольно простой и элегантный вариант в версии Python. Когда я пишу на Python, я не пытаюсь усложнить задачу, а наоборот - стараюсь пользоваться его средствами для упрощения себе работы. В данном случае я использую функцию index для удобства. У нас есть переменная i для индекса, по которому нужно брать элементы из списка list_example. Строка while i <= list_example.index(list_example[-1]): говорит нам о том, что мы не выйдем из цикла while пока значение переменной i не будет равно последнему индексу списка. Далее выводим данные на экран в строке print(list_example[i]). После чего прибавляем на единицу переменную индекса i . Давайте запустим наш код в VS Code и взглянем на результат.


1
2
3
4
5

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


#include <stdio.h>
int main(){
int array_example[] = {1, 2, 3, 4, 5};
int i = 0;
int i_end = sizeof(array_example) / sizeof(array_example[0]) - 1;
while (i <= i_end){
printf("%d\n", array_example[i]);
++i;
}
return 0;
}

Давайте взглянем на наш C код и на отличия в работе его логики. У нас имеется переменная для индекса int i = 0;. Строка получения длины списка вам уже известна из предыдущей главы и единственное здесь отличие - мы отнимаем единицу. Это нужно для того, чтобы получить последний индекс списка. Строка int i_end = sizeof(array_example) / sizeof(array_example[0]) - 1; успешно получит число 4. Далее идёт условие работы цикла: while i <= i_end работает аналогично примеру из Python. Строка printf("%d\n", array_example[i]); выведет число из списка в terminal. В конце ++i; мы прибавляем единицу к переменной индекса i. Вот тут хочу остановиться поподробнее. Дело в том, что есть два варианта: ++i и i++. Первый вариант ++i это префиксная форма и она сначала увеличивает значение, а потом возвращает его. Второй вид i++ это суффиксная форма, которая сначала вернёт текущее значение, а потом увеличит его. Будьте внимательны при использовании, у них будут разные результаты и может быть ошибка в логике программы.
Теперь давайте скомпилируем и запустим программу.


$ gcc array_c.c -o array_c
$ ./array_c
1
2
3
4
5

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


Бонус
Как известно, в Python, увы, нет встроенного в язык типа данных array, но зато есть встроенный модуль array, который обеспечивает схожую функциональность. Давайте посмотрим как он работает на примере.


arr = array('i', [0]*5)
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
print(arr)
print(arr[0])

Первая строка arr = array('i', [0]*5) создаёт аналог массива (по функциональности) и заполняет его пятью элементами нулями, i указывает, что это массив с данными int типа. Далее, как и в обычном list, мы присваиваем значение по индексу arr[0] = 1 и т.д. Давайте запустим код и убедимся, что всё работает.
🔥21
array('i', [1, 2, 3, 4, 5])
1

Мы видим, что у нас создался объект array, который заполнился данными. Далее мы выводим элемент цифру 1 по индексу 0. Согласитесь, не очень удобно заполнять нулями, а потом присваивать по каждому индексу значения? Мы можем сразу заполнить массив нужными значениями, уменьшив этим количество строк кода. Заодно давайте добавим новый элемент в массив, а так же добавим другой тип данных.


arr = array('i', [1, 2, 3, 4, 5])

print(arr)
arr.append(6)
print(arr)
arr.append('number')
print(arr)

Запустим наш новый код в terminal.


array('i', [1, 2, 3, 4, 5])
array('i', [1, 2, 3, 4, 5, 6])
Traceback (most recent call last):
File "/home/arduinum628/Документы/Helper_for_programmer/Articles/Code/Код из статьи array C list Python/list_python.py", line 43, in <module>
arr.append('number')
TypeError: 'str' object cannot be interpreted as an integer

При запуске нашего кода мы видим, что у нас создался такой же массив как и в примере выше. Далее мы добавили новый элемент с помощью метода append. Потому что размер array динамический в данной библиотеке. Обратите внимание что в языке C массив имеет фиксированный размер и выйти за его размеры у вас не получиться. Таким образом мы ограничиваем ресурсы нашего ПК в языке C. В Array мы получили фиксированную типизацию для массива указав i (int). Поэтому мы получили ошибку при добавлении в него типа данных str строки number. Также возможности встроенной библиотеки array позволяют удалять элемент по индексу del arr[0].


Заключение
- Узнали, чем отличается array (массив) в C от list (список) в Python;
- Научились работать с bufer для хранения символов типа данных char для строк;
- Посмотрели как работает цикл while на обоих языках;
- Затронули тему аннотации типов в Python;
- Бонусом посмотрели встроенный в Python модуль array, который может создавать подобие массива;




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

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

#Python #Сравнение_Python_и_C #C #while #цикл #Python_array #массив_в_Python
🔥3
Приветствуем!

Продолжаем рубрику "Вопросы и ответы"!

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

Ровно через неделю мы подготовим пост с ответами на появившиеся вопросы.

Также у нас есть чат, в котором тоже можно задавать вопросы и просто общаться)
🔥4
Всем привет!

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

Фильм: Унесённые призраками

Год: 2001

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

https://www.sspoisk.ru/film/370/

Приятного ппросмотра!
🔥5
Что выведет этот код? №27
Вчерашняя задача была короткой, но интересной и как оказалось сложной. Правильно ответили всего 9 человек из 33-х, что равно 27%.

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

print(numbers)



Разбор задачи
Сперва пройдёмся по строкам.

Сначала объявляем переменную numbers и присваиваем ей список чисел [1, 2, 3].

Затем у переменной numbers вызываем метод .append(), передав в него распакованный список [4, 5].

В конце вызываем функцию print(), передав в неё переменную numbers для вывода результата.


Что могло пойти не так?
Функции в Python принимают два вида аргументов: позиционные и ключевые.
В нашем случае, мы передаём в метод .append() один позиционный аргумент - список [4, 5], однако, затем мы применяем к нему распаковку. Мы уже писали про работу распаковки в разборе задачи "Найди ошибку №2".

Всё дело в том, что, когда применяется распаковка, все элементы передаются как отдельные аргументы, в связи с чем и получаем ошибку TypeError, поскольку метод .append() принимает только один позиционный аргумент, а мы ему передаём два.
🔥6
Подготовили видео ко вчерашней задаче!

https://youtube.com/shorts/gx1UZlqQ3sc

Будем благодарны за лайк и комментарий!
🔥8
Tips & Tricks. №2
2. Chocolatey - пакетный менеджер для Windows.

Многие знают про пакетные менеджеры в Linux, такие, как rpm, deb и другие. Если с ними ещё не сталкивались, то точно столкнётесь при работе с VPS.
В Windows с приложениями, обычно, всё сильно проще - зашёл на сайт, скачал exe или msi, запустил и установил. Нужно обновить приложение в котором нет автоматического обновления? Повторяешь цикл. Это не говоря о том, что пользователь по ошибке может скачать не то, что хочет...

Chocolatey - на текущий момент самый популярный менеджер пакетов для Windows. Обеспечивает удобное управление установленными приложениями, оперативно обновляет версии в репозитории, при этом тщательно проверяет находящиеся там пакеты.

Внимание! Для полноценной работы с Chocolatey необходимо использовать Powershell, запущенный от имени администратора!

Установка Chocolatey.
Для установки выполните команду ниже в PowerShell, открытом от имени администратора:
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))


Установка пакета.
choco install имя_пакета

choco install python312
# установится последняя на дынный момент версия 3.12.4

choco install python312 --version=3.12.0
# установится версия 3.12.0


Устаревшие пакеты.
choco outdated


Обновление пакета.
choco upgrade имя_пакета

choco upgrade python312
# если был установлен, к примеру, 3.12.0, то будет обновлено до 3.12.4

choco upgrade all
# обновит версии всех устаревших пакетов


Удаление пакета.
choco uninstall имя_пакета


Список установленных пакетов.
choco list


#код_на_салфетке #chocolatey #пакетный_менеджер #полезное #winget #python
🔥3
Подготовили новое видео посвящённое Chocolatey!

https://youtube.com/shorts/JwVtQnkYsQs?feature=share

Будем рады вашим лайкам и комментариям!
🔥3
Приветствую!

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


Оглавления:
Для удобства навигации есть посты с оглавлениями по темам:

"Сайт на Django"
"Telegram-бот на AIOgram3"
"Применение Docker"
"Полезные инструменты"
"Путь в IT."
"Код в мешке"
"Boosty эксклюзив"
"Сравнение Python и C"

Задачи "Что выведет этот код?"
Задачи "Найди ошибку в коде"


Ресурсы канала:

Уютный и немного безумный чат канала.
Бот с материалами к постам
Сайт со всеми постами
Канал в Dzen
Сообщество в VK


Поддержка.

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

Также поддержать канал можно:
Подпиской или донатом на Boosty.

Донатом в нашем Telegram-боте.
Отправив донат в DonationAlerts.

Или внеся сайт в исключения вашего блокировщика рекламы.
🔥3
Обновление бота автоответчика и ответы на вопросы о Telegram Stars
Автор: Иван Ашихмин

Приветствую.

Невозможно сразу знать или учесть все нюансы во время программирования, как бы этого не хотелось. Также порой приходят мысли по улучшению или изменению чего-либо. В этом посте я проведу некую "работу над ошибками", опишу изменения в нескольких постах, а также постараюсь расписать всё, что на данный момент известно о Telegram Stars, вопросы о котором поступают с удивляющей частотой.

Обновления затронут следующие посты:

- Бот-автоответчик с ChatGPT для Бизнес-аккаунта в Telegram на Aiogram 3
- AIOgram3 18. Подключаем оплату Telegram Stars



Бот-автоответчик с ChatGPT для Бизнес-аккаунта в Telegram на Aiogram 3
🔥6
В этом посте в процессе написания миддлвари для отслеживания времени работы в профиле Telegram, я не нашёл способ получить время при помощи бота, поэтому обращался за этой информацией к серверам Telegram. На Github репозитория AIOgram я задавал по этому поводу вопрос и получил ответ - я неправильно пытался получить информацию. Что ж, бывает, давайте это исправим.

У нас был следующий код в файле business_middleware.py:


async with httpx.AsyncClient() as client:  
resp = await client.get(
f"https://api.telegram.org/bot{secrets.token}/getChat?chat_id={secrets.admin_id}"
)
chat = resp.json()
full_chat = ChatFullInfo(**chat["result"])

if check_opening_hours(full_chat.business_opening_hours):

Уберём всё, что было до блока if и заменим на две строчки:


from app.settings import secrets, bot


chat = await bot.get_chat(secrets.admin_id)
hours = chat.business_opening_hours

if check_opening_hours(hours):

Можно было получить информацию о чате администратора просто обратившись к боту. Спасибо за эту информацию пользователю Olegt0rr с Github.


Для Boosty.
Также, для подписчиков на Boosty обновил репозиторий с проектом. Добавил поддержку контекста для бесед (ведение истории диалогов) в Redis.


AIOgram3 18. Подключаем оплату Telegram Stars

Ответы на вопросы.
Прочитать подробнее можно в "Условиях обслуживания": https://telegram.org/tos/bot-developers#6-payments

- Для кого это всё? - Звёзды ввели для продажи цифровых товаров и услуг. Они не распространяются на продажу физических товаров. Если вы продаёте что-то электронное или не существующее физически, например, доступ к закрытому чату, курс и т.д., подключать звёзды
обязательно. Telegram будет следить и проверять транзакции в ботах, они уже сообщали, что заблокировали некоторое количество ботов которые всё ещё не перешли на звёзды.
Пункт 6.2. Digital Goods and Services.
- Куда уходят звёзды после оплаты? - это сейчас главная загадка. Возможность получать за звёзды вознаграждения (именно так назвали вывод) или тратить их на рекламу появится в Июле 2024г. Вероятно, будет внедрено в платформу Fragment. Более подробной информации на данный момент нет. Также учтите, что звёзды на балансе будут появляться с задержкой до 21-го дня.
Пункты 6.2.4. Rewards for Stars и 6.2.4.1. Receiving Rewards.
- Сколько я получу за звёзды? - За каждую полученную звезду вы получите эквивалент 0.013 USD, вероятно в криптовалюте TON. Также была предоставлена таблица с разъяснением ценообразования: https://core.telegram.org/file/400780400418/1/NMQCu-wgDpo.397006/5304141797eccc38c4
Грубо говоря, за 100 звёзд купленных пользователем по цене в 189 рублей, на выходе получим примерно ~110 рублей, это ещё не считая комиссии за вывод/обмен TON.
Пункт 6.2.4. Rewards for Stars.


Рефактор кода.
Провёл небольшие изменения в коде бота. Обновил файл поста с материалами.


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



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

#Python #код_на_салфетке #рефакторинг #aiogram #гайды #telegram #telegram_stars #криптовалюта #TON #звёзды_Telegram #middleware #изменения
🔥7
ООП на Python, ч. 3. Методы __str__ и __repr__.
Автор: Андрей Лебедев

Продолжаем цикл постов об ООП на Python. В прошлый раз мы говорили о статических и классовых методах. Сегодня разберем методы __str__ и __repr__.
👍3🔥2
В рамках первого поста об ООП на Python мы уже рассматривали метод __init__. Все три эти метода (__str__, __repr__ и __init__), как нетрудно заметить, объединяют по два нижних подчеркивания до и после собственно названия метода. Такой синтаксис обозначает так называемые магические (или dunder - от английского “double under(scores)” - “двойное нижнее подчёркивание”) методы. Они используются для определения специального поведения объектов и вызываются во время использования стандартных операций (сложение, вычитание, доступ к атрибутам и др.).


Метод __str__().
Наверняка вам доводилось создать объект какого-нибудь класса и сразу же этот объект распечатать методом print(). Чаще всего результат выглядел немного загадочно. Давайте создадим свой собственный класс и попробуем распечатать его объект.

Используем класс футбольного клуба из предыдущего поста:



class FootballClub:

def __init__(self, name, description=""):
self.name = name
self.description = description

def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)

club = FootballClub(“Манчестер Юнайтед”, “Манчестер, Англия”)
print(club)


Вместо того, чтобы вызвать метод describe_club(), мы просто распечатаем вновь созданный объект нашего класса. Результат получается примерно таким:


<__main__.FootballClub object at 0x1050effd0>


Если явным образом не оповестить Python о том, как должно выглядеть представление объекта в виде строки, будет распечатано сообщение, содержащее следующую информацию:

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

Эта информация бывает полезна в процессе отладки кода, когда нужна заниматься поиском и устранением багов, но простому пользователю она мало о чем говорит. И выглядит, откровенно говоря, не очень читабельно для неподготовленного человека. Метод __str__() позволяет переопределить, что должно выводиться на печать, когда по отношению к объекту класса используется метод print().

Переопределим метод __str__() для нашего класса:



class FootballClub:

def __init__(self, name, description=""):
self.name = name
self.description = description

def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)

def __str__(self):
return self.name


club = FootballClub("Манчестер Юнайтед", "Манчестер, Англия")
print(club)


И результат выполнение кода теперь выглядит куда дружелюбнее:


Манчестер Юнайтед


Метод __str__() используется не только для вывода строк на печать в терминале. Многие панели управления “ищут” именно __str__() для отображения данных, связанных с экземплярами того или иного класса. Так, к примеру, происходит в админ панели Django, поэтому в большинстве случаев метод __str__() определен в классах моделей.

Рассмотрим пример из официального туториала по созданию приложений на Django. В нём разбирается пример класса модели Question - вопроса в рамках приложения для проведения анкетирования. Код модели выглядит следующим образом:



class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date_published')

def __str__(self):
return self.question_text
🔥4
Так как метод __str__() переопределен, в админ панели будет выводиться непосредственно сам вопрос. Если бы метод не переопределялся, то там бы виднелось что-то вроде Question object (1). Дело в том, что в Django все модели наследуются от базового класса модели в модуле django.db.models, где метод __str__() определяется следующим образом:



def __str__(self):
return "%s object (%s)" % (self.__class__.__name__, self.pk)


Таким образом, на печать выводится название класса, слово object и первичный ключ объекта в скобках.


Метод __repr__().
Прежде чем пускаться в объяснения работы метода __repr__(), разберёмся с тем, что же он делает. Для этого откроем терминал и зайдём в папку, где лежит наш файл с классом футбольного клуба, который мы создали ранее. Таким образом мы сможем получить доступ к классам и функциям нашего файла прямо из терминала:



$ ls
football.py
$ python
>>> from football import FootballClub
>>> club = FootballClub("Манчестер Юнайтед")
>>> print(club)
Манчестер Юнайтед
>>>


Сначала мы импортируем класс FootballClub. Затем создаем объект этого класса, передав в него название клуба ("Манчестер Юнайтед”), и записываем его в переменную club. Далее выводим на печать содержимое этой самой переменной. Срабатывает метод __str__(), который мы создали, и мы видим название клуба. А что случится, если мы в терминале просто введем имя переменной, не передавая его предварительно в метод print()?



>>> club
<football.FootballClub object at 0x10bdc8040>


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

“Если это возможно, вывод должен быть в виде выражения Python, которое можно использовать для воссоздания объекта с тем же значением (при условии наличия подходящего окружения). Если это невозможно, вывод должен быть в виде строки с текстом в таком формате <…полезная информация…>. Возвращаемое значение должно быть строкой Python.

Вывод метода обычно используется для дебаггинга, поэтому важно, чтобы он был информативным и недвусмысленным”.

Альтернативный вывод метода __repr__() должен помочь заново создать эквивалентный объект данного класса. Давайте добавим метод __repr__() в класс FootballClub:



class FootballClub:

def __init__(self, name, description=""):
self.name = name
self.description = description

def describe_club(self):
msg = f"{self.name}: {self.description}"
print(msg)

def __str__(self):
return self.name

def __repr__(self):
if self.description:
return (f"FootballClub(name={self.name}, "
f"description={self.description})")
else:
return f"FootballClub(name={self.name})"


Чтобы этот код сработал, надо заново открыть окно терминала и повторить весь порядок ввода:



$ python
>>> from football import FootballClub
>>> club = FootballClub("Манчестер Юнайтед")
>>> club
FootballClub("Манчестер Юнайтед")


На этот раз выводится не стандартная запись, а вывод нашего перезаписанного метода __repr__(): строка, позволяющая создать объект, полностью эквивалентный тому, в котором и был вызван метод __repr__().
🔥4
В нашем примере срабатывал кусок кода, записанный в else. Если мы добавим в наш объект описание (description), вывод будет уже другим:



$ python
>>> from football import FootballClub
>>> club = club = FootballClub("Манчестер Юнайтед", "Английский футбольный клуб")
>>> club
FootballClub(name=Манчестер Юнайтед, description=Английский футбольный клуб)


Python вызывает метод __repr__(), когда генерирует вывод для дебаггинга. В момент дебаггинга кусок кода, который позволит сгенерировать объект, задействованный в проблемном коде.

В следующий раз поговорим о методе __new__().



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

#Python #ООП #код_на_салфетке #код_в_мешке #класс #init #метод #магический_метод #repr #dunder #str #dunder_метод
🔥6