<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>С любовью о Python</title><subtitle>Бережно рассказываю про Python и автоматизацию тестирования.</subtitle><author><name>С любовью о Python</name></author><id>https://teletype.in/atom/python3_with_love</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/python3_with_love?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/python3_with_love?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-17T10:21:29.466Z</updated><entry><id>python3_with_love:builder_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/builder_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Паттерн Builder в Python</title><published>2025-06-28T17:21:09.473Z</published><updated>2025-06-28T17:23:56.197Z</updated><summary type="html">Builder, он же строитель - порождающий паттерн проектирования в программировании, который позволяет довольно удобно работать с построением сложных объектов.</summary><content type="html">
  &lt;p id=&quot;b_98508_d185e92a0ede&quot;&gt;Builder, он же строитель - порождающий паттерн проектирования в программировании, который позволяет довольно удобно работать с построением сложных объектов.&lt;/p&gt;
  &lt;p id=&quot;b_19712_fc0139889f01&quot;&gt;Паттерн позволяет вызывать функции объекта для его сборки цепочкой, и это может выглядеть примерно так:&lt;br /&gt;&lt;code&gt;UserBuilder().with_name(“Ivan“).with_email(“i_connor@mail.ru“).with_subscription().build()&lt;/code&gt;&lt;/p&gt;
  &lt;h2 id=&quot;b_66012_00446a15af7e&quot;&gt;Пример применения&lt;/h2&gt;
  &lt;p id=&quot;b_23412_65f3c23cdffd&quot;&gt;Реализация паттерна может отличаться, но мы не будем рассматривать сложные примеры. Допустим, перед нами стоит задача - написать автотест для проверки запроса на создание пользователя в какой-либо системе. &lt;code&gt;json&lt;/code&gt; для отправки содержит в себе поля:&lt;/p&gt;
  &lt;ul id=&quot;b_87666_d1e90726e59f&quot;&gt;
    &lt;li id=&quot;b_92108_529b0e029380&quot;&gt;*имя: str&lt;/li&gt;
    &lt;li id=&quot;b_12493_2da858f868db&quot;&gt;*фамилия: str&lt;/li&gt;
    &lt;li id=&quot;b_50946_e8f0a710db3e&quot;&gt;*почта: str&lt;/li&gt;
    &lt;li id=&quot;b_64072_7d7862237301&quot;&gt;подписка:&lt;/li&gt;
    &lt;ul id=&quot;b_05737_5e4a5cf700a3&quot;&gt;
      &lt;li id=&quot;b_51416_f74fb865aae1&quot;&gt;*тип подписки: str&lt;/li&gt;
      &lt;li id=&quot;b_26999_76576836b7a0&quot;&gt;*наличие подписки (да/нет): bool&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;b_60121_a33bdfdb7a08&quot;&gt;*количество бонусных баллов: int&lt;/li&gt;
    &lt;li id=&quot;b_41904_7e9cddace549&quot;&gt;*адрес: str&lt;/li&gt;
    &lt;li id=&quot;b_87144_4ccb5658bc80&quot;&gt;о себе: str&lt;/li&gt;
    &lt;li id=&quot;b_86268_f805c37dd49e&quot;&gt;друзья: list[str]&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;b_47408_4a8a5c30daba&quot;&gt;где * означает обязательность поля.&lt;/p&gt;
  &lt;p id=&quot;b_58214_f69564bd2781&quot;&gt;В формате &lt;code&gt;json&lt;/code&gt; это выглядит так:&lt;/p&gt;
  &lt;pre id=&quot;SDAV&quot; data-lang=&quot;python&quot;&gt;{
    &amp;quot;name&amp;quot;: &amp;quot;Vova&amp;quot;,
    &amp;quot;last_name&amp;quot;: &amp;quot;Ivanov&amp;quot;,
    &amp;quot;email&amp;quot;: &amp;quot;vivanov@mail.ru&amp;quot;,
    &amp;quot;subscription&amp;quot;: {
        &amp;quot;type&amp;quot;: &amp;quot;hd+&amp;quot;,
        &amp;quot;active&amp;quot;: True
    },
    &amp;quot;bonuses&amp;quot;: 1500,
    &amp;quot;address&amp;quot;: &amp;quot;Belgorod, Lenina str., b. 10&amp;quot;,
    &amp;quot;about&amp;quot;: &amp;quot;Some additional info&amp;quot;,
    &amp;quot;friends&amp;quot;: [&amp;quot;12354&amp;quot;, &amp;quot;1435462&amp;quot;, &amp;quot;626245&amp;quot;, &amp;quot;62461134&amp;quot;]
}&lt;/pre&gt;
  &lt;p id=&quot;b_36319_4c30019861f9&quot;&gt;Простор для перебора параметров довольно обширен. Рассмотрим несколько вариантов для параметризации теста:&lt;/p&gt;
  &lt;ol id=&quot;b_20668_35eec5cb9bf3&quot;&gt;
    &lt;li id=&quot;b_11351_5ee8fc909f2f&quot;&gt;Хранить заранее заготовленные  json-ы где-то в константах или отдельно в файлах, передавать их в тест&lt;/li&gt;
    &lt;li id=&quot;b_96101_8c0ba4d1c59e&quot;&gt;Сделать функцию / фикстуру с параметрами для нужных полей&lt;/li&gt;
    &lt;li id=&quot;b_64277_78d919e08832&quot;&gt;Использовать faker для генерации рандомных данных и возвращать только их, без чуткого контроля над данными&lt;/li&gt;
    &lt;li id=&quot;b_98871_25bd49833a79&quot;&gt;Создать удобный конструктор, где мы можем сами контролировать нужные поля, их наличие и значения&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;b_18030_ea9e50a480da&quot;&gt;Выберем последний вариант и имплементируем наш класс-билдер.&lt;/p&gt;
  &lt;blockquote id=&quot;b_96379_51b3886307ac&quot;&gt;Перед написанием кода нам понадобится установить дополнительную библиотеку faker.&lt;br /&gt;Для этого используем команду: &lt;code&gt;pip install Faker&lt;/code&gt;&lt;/blockquote&gt;
  &lt;pre id=&quot;2IyW&quot; data-lang=&quot;python&quot;&gt;import copy
import random
from pprint import pprint
from faker import Faker


class UserBuilder:
    def __init__(self):
        self._final_dict = {}  # Изначально пустой словарь, который будет пополняться даннными
        self.faker = Faker(locale=&amp;quot;ru_RU&amp;quot;)

    # Ниже перечислены методы, отвечающие за интересующие нас поля для сборки финального json-а

    def with_name(self, name: str | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;name&amp;quot;] = name or self.faker.first_name_female()
        return self

    def with_last_name(self, last_name: str | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;last_name&amp;quot;] = last_name or self.faker.last_name_female()
        return self

    def with_email(self, email: str | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;email&amp;quot;] = email or self.faker.email()
        return self

    def with_subscription(self, is_active: bool, s_type: str) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;subscription&amp;quot;] = {&amp;quot;active&amp;quot;: is_active, &amp;quot;type&amp;quot;: s_type}
        return self

    def with_bonuses(self, amount: int | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;bonuses&amp;quot;] = amount or random.randint(500, 2000)
        return self

    def with_address(self, address: str | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;address&amp;quot;] = address or self.faker.address()
        return self

    def with_about(self, about: str | None = None) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;about&amp;quot;] = about or self.faker.text(max_nb_chars=100)
        return self

    def with_friends(self, friends_list: list[str]) -&amp;gt; &amp;quot;UserBuilder&amp;quot;:
        self._final_dict[&amp;quot;friends&amp;quot;] = friends_list
        return self

    def build(self) -&amp;gt; dict:
        user_data = copy.deepcopy(self._final_dict)
        self._final_dict.clear()
        return user_data  # Финальный аккорд билдера. Возвращает собранный словарь
&lt;/pre&gt;
  &lt;p id=&quot;b_34948_79ea4222d0c1&quot;&gt;Коротко о том, что у нас получилось:&lt;/p&gt;
  &lt;ul id=&quot;b_15372_a0e98df4acec&quot;&gt;
    &lt;li id=&quot;b_91430_c8ce90e17a88&quot;&gt;Создали класс с пустым словарем в приватном атрибуте &lt;code&gt;self._final_dict&lt;/code&gt; в &lt;code&gt;__init__&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;b_33895_e8ca1c631963&quot;&gt;Описали поля, которыми и будет заполняться наш пустой словарик. &lt;br /&gt;- Возврат &lt;code&gt;self&lt;/code&gt; здесь является важной особенностью, т.к. это позволяет нам вызывать методы цепочкой через точку. &lt;br /&gt;- Упрощенно: при использовании билдера мы создаем объект класса и именно его и возвращаем каждый раз при вызове методов. Это позволяет всегда иметь доступ к методам и атрибутам экземпляра класса.&lt;br /&gt;- Конструкции вида &lt;code&gt;email or self.faker.email()&lt;/code&gt; позволяют нам использовать фейковую генерацию данных, если мы ничего не передали в аргумент метода.&lt;/li&gt;
    &lt;li id=&quot;b_89233_614f885a75ca&quot;&gt;В методе &lt;code&gt;build&lt;/code&gt; уже возвращается не экземпляр класса, а собранный нами словарь. Это завершающий метод билдера. &lt;br /&gt;Кроме того, для безопасной работы и возможности создания одного экземпляра класса &lt;code&gt;UserBuilder&lt;/code&gt; для генерации нескольких словарей, внутри реализован механизм копирования данных в отдельную переменную, а затем очистка собранного нами словаря.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;b_00193_aede1364833e&quot;&gt;На этом кратком объяснении и остановимся, перейдем к использованию:&lt;/p&gt;
  &lt;pre id=&quot;nLzY&quot; data-lang=&quot;python&quot;&gt;# Создаем экземпляр класса UserBuilder
builder = UserBuilder()


# Первый пользователь с полным набором полей
user1 = (
    builder
    .with_name(&amp;quot;Егор&amp;quot;)
    .with_last_name(&amp;quot;Летов&amp;quot;)
    .with_email(&amp;quot;eletov@mail.ru&amp;quot;)
    .with_subscription(is_active=True, s_type=&amp;quot;hd+&amp;quot;)
    .with_bonuses(2000)
    .with_address(&amp;quot;Омск&amp;quot;)
    .with_about(&amp;quot;Музыкант&amp;quot;)
    .with_friends([&amp;quot;1234141&amp;quot;, &amp;quot;35152446&amp;quot;])
    .build()
)


# Второй пользователь с набором полей поменьше
user2 = (
    builder
    .with_name(&amp;quot;Денис&amp;quot;)
    .with_last_name(&amp;quot;Сергеев&amp;quot;)
    .with_email(&amp;quot;dsergeev@mail.ru&amp;quot;)
    .with_bonuses(1000)
    .build()
)


# Третий пользователь с полностью сгенерированными данными с использованием библиотеки faker
user3 = (
    builder
    .with_name()
    .with_last_name()
    .with_email()
    .with_address()
    .build()
)


# Распечатаем все с помощью функции pprint, которая сделает наш вывод немного красивее
pprint(user1, indent=2, sort_dicts=False)
pprint(user2, indent=2, sort_dicts=False)
pprint(user3, indent=2, sort_dicts=False)
&lt;/pre&gt;
  &lt;p id=&quot;b_36796_ce061d00fa66&quot;&gt;Давайте посмотрим на результат:&lt;/p&gt;
  &lt;pre id=&quot;Wm4H&quot; data-lang=&quot;python&quot;&gt;{ &amp;#x27;name&amp;#x27;: &amp;#x27;Егор&amp;#x27;,
  &amp;#x27;last_name&amp;#x27;: &amp;#x27;Летов&amp;#x27;,
  &amp;#x27;email&amp;#x27;: &amp;#x27;eletov@mail.ru&amp;#x27;,
  &amp;#x27;subscription&amp;#x27;: {&amp;#x27;active&amp;#x27;: True, &amp;#x27;type&amp;#x27;: &amp;#x27;hd+&amp;#x27;},
  &amp;#x27;bonuses&amp;#x27;: 2000,
  &amp;#x27;address&amp;#x27;: &amp;#x27;Омск&amp;#x27;,
  &amp;#x27;about&amp;#x27;: &amp;#x27;Музыкант&amp;#x27;,
  &amp;#x27;friends&amp;#x27;: [&amp;#x27;1234141&amp;#x27;, &amp;#x27;35152446&amp;#x27;]}
{ &amp;#x27;name&amp;#x27;: &amp;#x27;Денис&amp;#x27;,
  &amp;#x27;last_name&amp;#x27;: &amp;#x27;Сергеев&amp;#x27;,
  &amp;#x27;email&amp;#x27;: &amp;#x27;dsergeev@mail.ru&amp;#x27;,
  &amp;#x27;bonuses&amp;#x27;: 1000}
{ &amp;#x27;name&amp;#x27;: &amp;#x27;Феврония&amp;#x27;,
  &amp;#x27;last_name&amp;#x27;: &amp;#x27;Кузнецова&amp;#x27;,
  &amp;#x27;email&amp;#x27;: &amp;#x27;naina_1999@example.com&amp;#x27;,
  &amp;#x27;address&amp;#x27;: &amp;#x27;с. Ухта, ш. Революционное, д. 2 к. 4/1, 906254&amp;#x27;}&lt;/pre&gt;
  &lt;p id=&quot;b_08278_d02406d9b0da&quot;&gt;Конечное использование данных может быть разным, не будем на нем концентрироваться.&lt;/p&gt;
  &lt;p id=&quot;b_62065_4eecd5de2d5f&quot;&gt;Паттерны - это не магия или что-то переусложненное, а реальные механизмы, которые помогут структурировать код и облегчить работу над сложными объектами в повседневных задачах.&lt;/p&gt;
  &lt;p id=&quot;b_78239_997f7b2c60d3&quot;&gt;Конкретно билдер позволяет нам собирать нужный объект как конструктор, при этом шаг за шагом контролируя весь процесс.&lt;/p&gt;
  &lt;p id=&quot;aSMI&quot;&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;tO3r&quot;&gt;Мой telegram-канал, присоединяйтесь :)&lt;/p&gt;
  &lt;p id=&quot;5CE9&quot;&gt;⬇⬇⬇&lt;/p&gt;
  &lt;p id=&quot;c3mN&quot;&gt;&lt;a href=&quot;https://t.me/python3_with_love&quot; target=&quot;_blank&quot;&gt;https://t.me/python3_with_love&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:getters_and_setters_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/getters_and_setters_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Геттеры и сеттеры в Python</title><published>2025-05-25T13:43:51.385Z</published><updated>2025-05-25T13:43:51.385Z</updated><summary type="html">Для начала разберемся, что это такое и когда они нам могут пригодиться. Простыми словами, геттер получает значение приватного атрибута, а сеттер его устанавливает, вот и все.</summary><content type="html">
  &lt;h2 id=&quot;3vNg&quot;&gt;Постигаем геттеры и сеттеры&lt;/h2&gt;
  &lt;p id=&quot;5Qqy&quot;&gt;Для начала разберемся, что это такое и когда они нам могут пригодиться. Простыми словами, геттер получает значение приватного атрибута, а сеттер его устанавливает, вот и все.&lt;/p&gt;
  &lt;p id=&quot;uLiD&quot;&gt;С их помощью можно поддержать инкапсуляцию (получать доступ к приватным атрибутам) или каким-либо дополнительным образом провалидировать или обработать значения.&lt;/p&gt;
  &lt;h3 id=&quot;I5hr&quot;&gt;Пара слов о property&lt;/h3&gt;
  &lt;p id=&quot;Nai7&quot;&gt;Примеры сразу будем рассматривать с удобными аннотациями &lt;code&gt;@property&lt;/code&gt; - свойствами. Это более &amp;quot;питонический&amp;quot; путь работы с поведением атрибутов, такими как получение, настройка и удаление.&lt;/p&gt;
  &lt;p id=&quot;13Wm&quot;&gt;Свойства используются так же, как и обычные атрибуты, но предоставляют возможность добавлять поведение при доступе к ним.&lt;/p&gt;
  &lt;p id=&quot;wOXA&quot;&gt;Как связаны свойства с геттерами и сеттерами? Когда мы получаем доступ к свойству, то автоматически вызывается связанный с ним getter-метод. Когда мы пытаемся изменить свойство, соответственно будет вызван setter-метод.&lt;/p&gt;
  &lt;p id=&quot;rnXs&quot;&gt;А теперь перейдем к практике.&lt;/p&gt;
  &lt;h2 id=&quot;cy9y&quot;&gt;Пример использования&lt;/h2&gt;
  &lt;p id=&quot;cFjg&quot;&gt;Создадим класс &lt;code&gt;Product&lt;/code&gt;, чтобы мы могли провернуть с ним некоторые процедуры:&lt;/p&gt;
  &lt;ul id=&quot;50XR&quot;&gt;
    &lt;li id=&quot;8RF7&quot;&gt;создать конкретный продукт, задав ему имя и базовую стоимость (без НДС)&lt;/li&gt;
    &lt;li id=&quot;g4Y3&quot;&gt;получить стоимость продукта с / без НДС&lt;/li&gt;
    &lt;li id=&quot;omTW&quot;&gt;назначить новую цену, введя сумму с НДС, выполнить расчет базовой стоимости и записать ее в приватный атрибут.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;8UFr&quot; data-lang=&quot;python&quot;&gt;class Product:  
    def __init__(self, name: str, base_price: float) -&amp;gt; None:  
        self.name = name  
        self.__base_price = base_price  
        self.__vat_percent = 0.13  
  
    @property  
    def price(self) -&amp;gt; float:  
        return round(self.__base_price * (1 + self.__vat_percent), 2)  
  
    @price.setter  
    def price(self, value: float) -&amp;gt; None:  
        # пересчитываем базовую цену, т.к. передаем в value цену с НДС
        self.__base_price = round(value / (1 + self.__vat_percent), 2)  
  
    @property  
    def base_price(self) -&amp;gt; float:  
        return self.__base_price&lt;/pre&gt;
  &lt;p id=&quot;XgzF&quot;&gt;Сразу определим, что НДС будет равен 13%.&lt;/p&gt;
  &lt;p id=&quot;IVtO&quot;&gt;Под капотом некоторые обработки для свойств:&lt;/p&gt;
  &lt;ul id=&quot;SJRt&quot;&gt;
    &lt;li id=&quot;ok4J&quot;&gt;Округление и рассчет цены с НДС при получении цены через свойство &lt;code&gt;price&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;2SQC&quot;&gt;Рассчет базовой стоимости по полученной стоимости с НДС, округление и установка значения в приватный атрибут &lt;code&gt;__base_price&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;Z02R&quot;&gt;Класс описали, посмотрим теперь на пример использования:&lt;/p&gt;
  &lt;pre id=&quot;lPse&quot; data-lang=&quot;python&quot;&gt;coffee = Product(name=&amp;quot;Флэт уайт&amp;quot;, base_price=300)  # Создаем наш кофе с ценой до НДС = 300 р.

print(coffee.name)  # Флэт уайт | Здесь просто название
print(coffee.price)  # 339.0 | Цена рассчитана с НДС, это произошло автоматически
  
coffee.price = 350  # Устанавливаем новую цену для кофе, сумма сразу с НДС, чтобы произошла корректная обработка и вычисление базовой стоимости
print(coffee.price)  # &amp;gt;&amp;gt;&amp;gt; 349.99  | Получаем цену с НДС. Не равно 350 из-за округлений при записи
print(coffee.base_price)  # &amp;gt;&amp;gt;&amp;gt; 309.73  | Базовая цена без НДС&lt;/pre&gt;
  &lt;p id=&quot;lRWd&quot;&gt;Пример получился лаконичным, так как мы производим все вычисления, просто обращаясь к свойствам, не вызываем явные методы для этих операций. При этом, напрямую не обращаемся к приватным атрибутам, не нарушая принцип инкапсуляции.&lt;/p&gt;
  &lt;h3 id=&quot;ja70&quot;&gt;Маленький бонус: deleter&lt;/h3&gt;
  &lt;p id=&quot;RyXY&quot;&gt;Хоть в нашем примере это не имеет особого смысла, но для рассмотрения еще 1 свойства можно попробовать обновить пример так, чтобы использовать deleter. Это свойство уместно, когда нужно удалить атрибут или сбросить его в изначальное состояние, или обработать удаление атрибута каким-то определенным образом.&lt;/p&gt;
  &lt;p id=&quot;HpXF&quot;&gt;Добавим в наш пример с Product следующее свойство:&lt;/p&gt;
  &lt;pre id=&quot;AAAK&quot; data-lang=&quot;python&quot;&gt;@price.deleter
def price(self):
	self.__base_price = 0&lt;/pre&gt;
  &lt;p id=&quot;dUag&quot;&gt;Тогда если мы удалим атрибут, значение &lt;code&gt;__base_price&lt;/code&gt; просто сбросится в ноль. Проверим:&lt;/p&gt;
  &lt;pre id=&quot;6FGq&quot; data-lang=&quot;python&quot;&gt;del coffee.price  
print(coffee.price)  # 0.0&lt;/pre&gt;
  &lt;h2 id=&quot;BnEv&quot;&gt;Выводы&lt;/h2&gt;
  &lt;p id=&quot;0SeF&quot;&gt;Геттеры и сеттеры:&lt;/p&gt;
  &lt;p id=&quot;T1ZT&quot;&gt;✅ помогают скрыть реализацию и сохранить инкапсуляцию&lt;br /&gt;✅ позволяют валидировать или изменять данные при доступе к ним&lt;br /&gt;✅ обеспечивают простой и читаемый интерфейс (без явных методов)&lt;/p&gt;
  &lt;p id=&quot;aQpJ&quot;&gt;Лучше использовать методы вместо свойств, если:&lt;/p&gt;
  &lt;ul id=&quot;KmbN&quot;&gt;
    &lt;li id=&quot;hQ0n&quot;&gt;операция не выглядит как &amp;quot;доступ к данным&amp;quot;&lt;/li&gt;
    &lt;li id=&quot;Hwty&quot;&gt;требуется более сложная логика&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;BHuf&quot;&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;tO3r&quot;&gt;Мой telegram-канал, присоединяйтесь :)&lt;/p&gt;
  &lt;p id=&quot;5CE9&quot;&gt;⬇⬇⬇&lt;/p&gt;
  &lt;p id=&quot;c3mN&quot;&gt;&lt;a href=&quot;https://t.me/python3_with_love&quot; target=&quot;_blank&quot;&gt;https://t.me/python3_with_love&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:enumerate_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/enumerate_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Функция enumerate в Python 🐍</title><published>2025-01-25T17:27:08.112Z</published><updated>2025-01-25T17:27:08.112Z</updated><summary type="html">Поговорим сегодня о маленькой, но очень важной встроенной функции, имя которой enumerate.</summary><content type="html">
  &lt;p id=&quot;mHmq&quot;&gt;Поговорим сегодня о маленькой, но очень важной встроенной функции, имя которой &lt;code&gt;enumerate&lt;/code&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;Uz43&quot;&gt;Что это и когда применять&lt;/h2&gt;
  &lt;h3 id=&quot;Krao&quot;&gt;Синтаксис&lt;/h3&gt;
  &lt;pre id=&quot;VHBB&quot; data-lang=&quot;python&quot;&gt;enumerate(iterable, start=0)&lt;/pre&gt;
  &lt;h3 id=&quot;9x63&quot;&gt;Описание&lt;/h3&gt;
  &lt;p id=&quot;aWei&quot;&gt;Функция &lt;code&gt;enumerate()&lt;/code&gt; возвращает объект &lt;code&gt;enumerate&lt;/code&gt;, который является итератором, создающим кортежи. Этот кортеж содержит счетчик от &lt;code&gt;start&lt;/code&gt; (который по-умолчанию равен 0) и значение, полученное в результате перебора по &lt;code&gt;iterable&lt;/code&gt;. Объект, переданный в &lt;code&gt;iterable&lt;/code&gt;, должен быть последовательностью, итератором или другим объектом, поддерживающим метод итератора &lt;code&gt;__next__()&lt;/code&gt;.&lt;/p&gt;
  &lt;h3 id=&quot;Xz5i&quot;&gt;Параметры&lt;/h3&gt;
  &lt;ul id=&quot;efqS&quot;&gt;
    &lt;li id=&quot;Ol8o&quot;&gt;&lt;code&gt;iterable&lt;/code&gt; По сути, &lt;code&gt;iterable&lt;/code&gt; — это любой объект, по которому можно пройтись в цикле (списки, строки, файлы и т.д.).&lt;/li&gt;
    &lt;li id=&quot;Pyew&quot;&gt;&lt;code&gt;start&lt;/code&gt; Позволяет задать начальное значение счетчика, что полезно, если нумерация должна начинаться не с 0, а с другого числа.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;Seke&quot;&gt;Если вы когда-нибудь писали цикл &lt;code&gt;for&lt;/code&gt; и ловили себя на мысли: &amp;quot;Вот бы знать, на каком я сейчас шаге итерации&amp;quot;, то &lt;code&gt;enumerate&lt;/code&gt; — это ваш новый лучший друг. Функция позволяет избавиться от необходимости инициировать и обновлять отдельную переменную-счётчик.&lt;/p&gt;
  &lt;h2 id=&quot;Cul8&quot;&gt;Примеры&lt;/h2&gt;
  &lt;h3 id=&quot;w8xE&quot;&gt;1. Перебор по списку&lt;/h3&gt;
  &lt;p id=&quot;1Rkt&quot;&gt;Давайте рассмотрим пример:&lt;/p&gt;
  &lt;ul id=&quot;Nod7&quot;&gt;
    &lt;li id=&quot;zXk8&quot;&gt;Создадим список&lt;/li&gt;
    &lt;li id=&quot;yuQ5&quot;&gt;Выведем каждый элемент с индексом без &lt;code&gt;enumerate&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;ik9S&quot;&gt;Выведем каждый элемент с индексом с использованием &lt;code&gt;enumerate&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;QgdD&quot;&gt;Допустим, мы перебираем гипотетический шкаф с вещами и хотим знать номер каждой вещи, которую мы достали.&lt;/p&gt;
  &lt;pre id=&quot;GJCc&quot; data-lang=&quot;python&quot;&gt;list_of_things_from_closet = [&amp;quot;толстовка&amp;quot;, &amp;quot;футболка&amp;quot;, &amp;quot;кепка&amp;quot;, &amp;quot;шорты&amp;quot;, &amp;quot;джинсы&amp;quot;]  
  
# Печатаем каждый элемент из списка с его индексом  
for i in range(len(list_of_things_from_closet)):  
    # i + 1, т.к. в обычной жизни мы не считаем элементы с 0, а начинаем с единицы  
    print(f&amp;quot;Вещь № {i + 1} - это {list_of_things_from_closet[i]}&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;BLCu&quot;&gt;В консоли увидим следующее:&lt;/p&gt;
  &lt;pre id=&quot;ZHtX&quot;&gt;&amp;gt;&amp;gt;&amp;gt; Вещь № 1 - это толстовка
&amp;gt;&amp;gt;&amp;gt; Вещь № 2 - это футболка
&amp;gt;&amp;gt;&amp;gt; Вещь № 3 - это кепка
&amp;gt;&amp;gt;&amp;gt; Вещь № 4 - это шорты
&amp;gt;&amp;gt;&amp;gt; Вещь № 5 - это джинсы&lt;/pre&gt;
  &lt;p id=&quot;FmqU&quot;&gt;Теперь попробуем реализовать ровно то же самое, но с помощью &lt;code&gt;enumerate&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;gd8c&quot; data-lang=&quot;python&quot;&gt;for index, element in enumerate(list_of_things_from_closet, start=1):
    print(f&amp;quot;Вещь № {index} - это {element}&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;emN2&quot;&gt;Вывод получим ровно такой же.&lt;/p&gt;
  &lt;p id=&quot;enLy&quot;&gt;Выглядит проще и более читаемо, не так ли?&lt;/p&gt;
  &lt;p id=&quot;EAAs&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;bifF&quot;&gt;Перейдем к более реальной задаче (хотя что может быть реальней, чем перебор шкафа с вещами?!)&lt;/p&gt;
  &lt;h3 id=&quot;QGni&quot;&gt;2. Перебор по файлу с логами&lt;/h3&gt;
  &lt;p id=&quot;kBwa&quot;&gt;Представим, что у нас есть файл с логами сервера, и нам нужно найти все строки, где упоминается ошибка (ключевое слово &amp;quot;ERROR&amp;quot;). При этом мы хотим знать номер строки, чтобы можно было быстро найти её в файле.&lt;/p&gt;
  &lt;h4 id=&quot;48KX&quot;&gt;Создадим файл с логами&lt;/h4&gt;
  &lt;p id=&quot;nAHH&quot;&gt;Для начала создадим текстовый файл &lt;code&gt;server_logs.txt&lt;/code&gt; с примером логов:&lt;/p&gt;
  &lt;pre id=&quot;D3K4&quot;&gt;2025-01-25 18:00:00 INFO: Server started
2025-01-25 18:05:23 ERROR: Disk space low
2025-01-25 18:10:45 WARNING: High CPU usage
2025-01-25 18:15:10 ERROR: Failed to connect to database
2025-01-25 18:20:30 INFO: Backup completed successfully
2025-01-25 18:25:00 ERROR: User authentication failed&lt;/pre&gt;
  &lt;h4 id=&quot;LoxY&quot;&gt;Откроем файл и обработаем его с помощью enumerate&lt;/h4&gt;
  &lt;p id=&quot;22Xm&quot;&gt;Теперь напишем небольшую функцию, которая откроет этот файл, прочитает его построчно и с помощью &lt;code&gt;enumerate&lt;/code&gt; проверит, есть ли в строке ключевое слово. Если есть, выведем номер строки и само событие.&lt;/p&gt;
  &lt;pre id=&quot;cBrx&quot; data-lang=&quot;python&quot;&gt;def get_logs_by_level(level: str, filename: str = &amp;#x27;server_logs.txt&amp;#x27;) -&amp;gt; None:  
    # Открываем файл с логами  
    with open(filename, &amp;#x27;r&amp;#x27;, encoding=&amp;#x27;utf-8&amp;#x27;) as file:  
        # Итерируемся по строкам с помощью enumerate  
        for line_number, line in enumerate(file, start=1):  
            # Проверяем, есть ли в строке ключевое слово, соответствующее уровню лога &amp;quot;level&amp;quot; (регистр не учитываем)  
            if level in line.upper():  
                # Выводим номер строки и само событие  
                print(f&amp;quot;Строка {line_number}: {line.strip()}&amp;quot;)  


# Попробуем напечатать только логи уровня Error:  
get_logs_by_level(level=&amp;quot;ERROR&amp;quot;)  

# Результат:  
# Строка 2: 2025-01-25 18:05:23 ERROR: Disk space low  
# Строка 4: 2025-01-25 18:15:10 ERROR: Failed to connect to database  
# Строка 6: 2025-01-25 18:25:00 ERROR: User authentication failed  


# А теперь напечатаем строки с ворнингами:  
get_logs_by_level(level=&amp;quot;WARNING&amp;quot;)  
  
# Результат:  
# Строка 3: 2025-01-25 18:10:45 WARNING: High CPU usage&lt;/pre&gt;
  &lt;p id=&quot;583t&quot;&gt;Класс, все работает ровно так, как нам и хотелось.&lt;/p&gt;
  &lt;h2 id=&quot;IBv6&quot;&gt;Заключение&lt;/h2&gt;
  &lt;p id=&quot;JueA&quot;&gt;Функция &lt;code&gt;enumerate&lt;/code&gt; — это простой, но мощный инструмент, который помогает сделать код чище и читаемее. Она избавляет от необходимости вручную управлять индексами и делает итерацию по элементам более удобной. Кроме того, использование &lt;code&gt;enumerate&lt;/code&gt; делает код более безопасным, так как исключает риск ошибок, связанных с некорректным обновлением индексов вручную.&lt;/p&gt;
  &lt;p id=&quot;7PoJ&quot;&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;f9mE&quot;&gt;Мой telegram-канал, присоединяйтесь :) &lt;/p&gt;
  &lt;p id=&quot;sgX0&quot;&gt;⬇⬇⬇ &lt;/p&gt;
  &lt;p id=&quot;NtLB&quot;&gt;&lt;a href=&quot;https://t.me/python3_with_love&quot; target=&quot;_blank&quot;&gt;https://t.me/python3_with_love&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:hash_bcrypt_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/hash_bcrypt_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Храним пароли безопасно. Интерактивное руководство по bcrypt в Python</title><published>2025-01-19T11:21:42.302Z</published><updated>2025-01-19T11:21:42.302Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/d6/ea/d6ea6bf9-e4a1-4f6b-b3de-ae2c9fe785f6.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/fc/62/fc627b9f-94a9-4285-8c9b-8447583c1f40.png&quot;&gt;Тема болезненная, обширная и важная и касается буквально каждого из нас, хотелось бы нам этого или нет.</summary><content type="html">
  &lt;h2 id=&quot;P25h&quot;&gt;Краткое предисловие&lt;/h2&gt;
  &lt;p id=&quot;aZAm&quot;&gt;Тема болезненная, обширная и важная и касается буквально каждого из нас, хотелось бы нам этого или нет.&lt;/p&gt;
  &lt;p id=&quot;q4fk&quot;&gt;В современном мире трудно представить человека, который не имеет, например, электронной почты и не зарегистрирован ни в каком сервисе. Как известно, при регистрации мы обычно указываем логин и пароль и на этом наша сторона ответственности заканчивается (а, нет, еще в нашей ответственности создать себе надежный пароль из примерно 16 символов, который содержит и строчные и заглавные буквы, и спецсимволы и желательно вообще нечитабельный, а еще - уникальный для каждого сервиса :)). Способ хранения паролей же лежит на стороне разработчика сервиса и обычно мы об этом никогда не узнаем, так как исходные коды обычно закрыты.&lt;/p&gt;
  &lt;h2 id=&quot;9dLf&quot;&gt;Чем же плохо хранение паролей в открытом виде?&lt;/h2&gt;
  &lt;p id=&quot;X4F1&quot;&gt;&lt;br /&gt;Это однозначно небезопасно и может привести к серьезным последствиям. &lt;/p&gt;
  &lt;p id=&quot;JhSt&quot;&gt;Как минимум, если злоумышленник вдруг получит доступ к базе данных сервиса, он получит сразу и пары логин-пароль всех пользователей и сможет делать многие действия от имени взломанных бедолаг. &lt;/p&gt;
  &lt;p id=&quot;4I04&quot;&gt;Кроме того, как мы обычно поступаем? Мы придумываем 1 пароль для многих сервисов для удобства. А если злоумышленник уже получил логин-пароль от одного сервиса, то можно считать, что он уже получил ключики от всех дверей. Осталось их только перебрать :).&lt;/p&gt;
  &lt;h2 id=&quot;rHgn&quot;&gt;Погружаемся в тему&lt;/h2&gt;
  &lt;p id=&quot;thuE&quot;&gt;&lt;br /&gt;Итак, давайте в рамках статьи представим, что у нас есть вымышленный сервис доставки &lt;code&gt;dostavallo&lt;/code&gt;, который хранит пароли в открытом виде, примерно так:&lt;/p&gt;
  &lt;p id=&quot;ZVYV&quot;&gt;------------------------------------------ &lt;br /&gt;| email                      | password                |&lt;br /&gt;| ------------------ |-------------------- |&lt;br /&gt;| user1@gmail.com | my_best_password |&lt;br /&gt;| user2@yandex.ru | secured_password |&lt;br /&gt;------------------------------------------&lt;br /&gt;Поможем ему обезопаситься и не потерять пользователей!&lt;/p&gt;
  &lt;h3 id=&quot;mIFq&quot;&gt;База&lt;/h3&gt;
  &lt;p id=&quot;iEMy&quot;&gt;Без теории никак, поэтому немного ознакомимся с основными терминами.&lt;/p&gt;
  &lt;p id=&quot;jKxP&quot;&gt;&lt;strong&gt;Хэш-функция&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;tQhC&quot;&gt;Криптографическая хэш-функция, упрощенно говоря - это математическая функция, превращающая последовательность входных данных в строку фиксированной длины, состоящей из букв и цифр.&lt;/p&gt;
  &lt;p id=&quot;CK0D&quot;&gt;Главная особенность полученных хэшей в том, что они односторонние, то есть расшифровать их и получить исходный пароль уже не получится.&lt;/p&gt;
  &lt;p id=&quot;u7Qz&quot;&gt;&lt;strong&gt;Соль&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;KOcA&quot;&gt;Часто в контексте безопасного хранения паролей можно услышать фразу &amp;quot;Надо посолить пароль&amp;quot; :) Что же это значит?&lt;/p&gt;
  &lt;p id=&quot;DdRa&quot;&gt;Соль - это случайная строка данных, которая хэшируется вместе с паролем, чтобы результат хэширования был всегда уникальным. Позволяет скрыть факт использования одинаковых паролей при использовании для них разной соли.&lt;/p&gt;
  &lt;p id=&quot;FMHD&quot;&gt;Соль в нашем случае нужна для защиты от атак с использованием радужных таблиц.&lt;/p&gt;
  &lt;figure id=&quot;cs6Y&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fc/62/fc627b9f-94a9-4285-8c9b-8447583c1f40.png&quot; width=&quot;1256&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;EeLk&quot;&gt;&lt;strong&gt;Радужные таблицы&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;tGSq&quot;&gt;Радужные таблицы - это специальный вариант таблиц поиска для обращения криптографических хеш-функций, использующий механизм разумного компромисса между временем поиска по таблице и занимаемой памятью.&lt;/p&gt;
  &lt;p id=&quot;z5Tm&quot;&gt;Проще говоря, это таблицы, которые позволяют быстро найти соответствие между хэшем и исходным паролем. Если пароли защищены солью, радужные таблицы становятся бесполезными.&lt;/p&gt;
  &lt;p id=&quot;CqcO&quot;&gt;&lt;strong&gt;Почему не шифрование?&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;cQhD&quot;&gt;К слову, почему хэширование, а не шифрование? Потому что это односторонний процесс и восстановить исходный пароль уже вряд ли получится.&lt;br /&gt;Шифрование подходит для данных, которые нужно вернуть в исходный вид, например, для передачи сообщений. В случае с паролями такой необходимости нет, поэтому хэширование предпочтительнее.&lt;/p&gt;
  &lt;h2 id=&quot;oRDF&quot;&gt;За дело берется bcrypt&lt;/h2&gt;
  &lt;p id=&quot;zyKa&quot;&gt;&lt;code&gt;bcrypt&lt;/code&gt; - адаптивная криптографическая хеш-функция формирования ключа, используемая для защищенного хранения паролей.&lt;/p&gt;
  &lt;p id=&quot;M4SG&quot;&gt;В Python уже есть готовая библиотека, которую мы и используем.&lt;/p&gt;
  &lt;h3 id=&quot;ogwC&quot;&gt;Реализация&lt;/h3&gt;
  &lt;p id=&quot;7trR&quot;&gt;Давайте напишем небольшой скрипт, который будет хэшировать пароли.&lt;/p&gt;
  &lt;p id=&quot;jZQo&quot;&gt;Для начала устанавливаем библиотеку к себе в проект:&lt;/p&gt;
  &lt;pre id=&quot;I6el&quot; data-lang=&quot;python&quot;&gt;pip install bcrypt&lt;/pre&gt;
  &lt;p id=&quot;kBvM&quot;&gt;Попробуем посолить и хэшировать пароль:&lt;/p&gt;
  &lt;pre id=&quot;kVP2&quot; data-lang=&quot;python&quot;&gt;# Наш исходный пароль: 123
password = &amp;quot;123&amp;quot;
 
# Генерируем соль, хэшируем пароль с солью и предоставляем его
# в виде строки из байткода с помощью функции decode()
hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
 
# Теперь наш пароль выглядит примерно так:
print(hashed_password)
&amp;gt;&amp;gt;&amp;gt; &amp;#x27;$2b$12$G3gSjjkBe1Bq3zwOGbmIXOUQ9C48kYcB6lJndABNOiGxJdip1.EES&amp;#x27;&lt;/pre&gt;
  &lt;p id=&quot;0GWf&quot;&gt;Отлично! Теперь мы представили наш пароль в виде, который нельзя расшифровать, но как же теперь проверить, что наш исходный пароль соответствует хэшированному?&lt;/p&gt;
  &lt;p id=&quot;DOki&quot;&gt;Все довольно просто, надо лишь использовать функцию &lt;code&gt;checkpw&lt;/code&gt;, которая возвращает результат типа &lt;code&gt;bool&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;6xSw&quot; data-lang=&quot;python&quot;&gt;password = &amp;quot;123&amp;quot;
hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
 
# Проверяем, что указанный нами пароль соответствует хэшу
print(bcrypt.checkpw(password.encode(), hashed_password.encode()))
&amp;gt;&amp;gt;&amp;gt; True
 
# Попробуем указать неправильный пароль и получим False
wrong_password = &amp;quot;1234&amp;quot;
print(bcrypt.checkpw(wrong_password.encode(), hashed_password.encode()))
&amp;gt;&amp;gt;&amp;gt; False&lt;/pre&gt;
  &lt;p id=&quot;2QwE&quot;&gt;Теперь мы убедились, что можно хранить пароль в хэшированном виде, который абсолютно не читаем и устойчив к атакам. Даже если он утечет, с ним мало что можно сделать. При этом, всегда можно проверить, корректен ли введенный пароль хэшированному, чтобы принять решение, пускать ли пользователя в систему.&lt;/p&gt;
  &lt;h2 id=&quot;TvbF&quot;&gt;Набираем обороты и помогаем сервису dostavallo с безопасностью&lt;/h2&gt;
  &lt;p id=&quot;7jJK&quot;&gt;Давайте теперь рассмотрим пример посложнее, реализуем небольшое приложение командной строки, которое будет реализовывать функционал регистрации и входа в систему, а пары логин-пароль мы будем просто хранить в json-файлике.&lt;/p&gt;
  &lt;blockquote id=&quot;box9&quot;&gt;Обратите внимание, что в реальных приложениях пароли и другие данные пользователей хранятся в защищенных базах данных, а не в файлах. :)&lt;/blockquote&gt;
  &lt;p id=&quot;RHG6&quot;&gt;Пример будет рассмотрен одним блоком кода с комментариями / документацией. Погнали!&lt;/p&gt;
  &lt;h3 id=&quot;wTDk&quot;&gt;Реализация программы&lt;/h3&gt;
  &lt;pre id=&quot;cS30&quot; data-lang=&quot;python&quot;&gt;import bcrypt  
import json
 

# Путь до файла, в котором будут храниться данные о пользователях  
USERS_FILEPATH = &amp;quot;users.json&amp;quot;

  
def hash_password(password: str) -&amp;gt; str:  
    &amp;quot;&amp;quot;&amp;quot;Функция для хэширования пароля&amp;quot;&amp;quot;&amp;quot;  
    return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()  
  
  
def is_password_correct(password: str, hashed_password: str) -&amp;gt; bool:  
    &amp;quot;&amp;quot;&amp;quot;Функция для проверки пароля. Если ок - возвращает True&amp;quot;&amp;quot;&amp;quot;  
    return bcrypt.checkpw(password.encode(), hashed_password.encode())  
  
  
def load_users(file_path=USERS_FILEPATH) -&amp;gt; dict:  
    &amp;quot;&amp;quot;&amp;quot;Функция для загрузки пользователей из файла&amp;quot;&amp;quot;&amp;quot;  
    try:  
        with open(file_path, &amp;quot;r&amp;quot;) as file:  
            return json.load(file)  
    except FileNotFoundError:  
        return {}  
  
  
def save_user(user: dict, file_path=USERS_FILEPATH) -&amp;gt; None:  
    &amp;quot;&amp;quot;&amp;quot;Функция для сохранения регистрационных данных пользователя (login-password) в файл&amp;quot;&amp;quot;&amp;quot;  
    with open(file_path, &amp;quot;w&amp;quot;) as file:  
        json.dump(user, file, indent=4)  
  
  
def register_user(users: dict) -&amp;gt; dict:  
    &amp;quot;&amp;quot;&amp;quot;
    Функция регистрации пользователя.
    Внутри происходит хэширование пароля и сохранение данных пользователя в файл.
    &amp;quot;&amp;quot;&amp;quot;
    username = input(&amp;quot;Введите имя пользователя: &amp;quot;).strip()  
    if username in users:  
        print(f&amp;quot;Пользователь {username} уже существует.&amp;quot;)  
        return users  
  
    password = input(&amp;quot;Введите пароль: &amp;quot;).strip()  
    hashed_password = hash_password(password)  
    users[username] = hashed_password  
    save_user(users)  
    print(f&amp;quot;Пользователь {username} успешно зарегистрирован!&amp;quot;)  
    return users  
  
  
def login_user(users: dict) -&amp;gt; None:  
    &amp;quot;&amp;quot;&amp;quot;  
    Функция для логина пользователя в систему.  
    При удачном входе, выполнение программы завершается.
    &amp;quot;&amp;quot;&amp;quot;
    username = input(&amp;quot;Введите имя пользователя: &amp;quot;).strip()  
    if username not in users:  
        print(f&amp;quot;Пользователь {username} не найден.&amp;quot;)  
        return  
  
    while True:  
        password = input(&amp;quot;Введите пароль (или нажмите &amp;#x27;q&amp;#x27; для возврата в главное меню): &amp;quot;).strip()  
        if password.lower() == &amp;#x27;q&amp;#x27;:  
            print(&amp;quot;Возврат в главное меню.&amp;quot;)  
            return  
  
        if is_password_correct(password=password, hashed_password=users[username]):  
            print(f&amp;quot;\nВведен Правильный пароль, вход выполнен. \nДобро пожаловать, {username}!&amp;quot;)  
            exit()  
        else:  
            print(&amp;quot;Неверный пароль! Попробуйте еще раз.&amp;quot;)  
  
  
# Основной цикл программы  
def main():  
    users = load_users()  
    print(&amp;quot;Добро пожаловать в сервис доставки &amp;#x27;dostavallo&amp;#x27;!&amp;quot;)  
    while True:  
        print(&amp;quot;\nВыберите действие:&amp;quot;)  
        print(&amp;quot;1. Зарегистрироваться&amp;quot;)  
        print(&amp;quot;2. Войти в систему&amp;quot;)  
        print(&amp;quot;3. Выход&amp;quot;)  
  
        choice = input(&amp;quot;Ваш выбор: &amp;quot;).strip()  
  
        match choice:  
            case &amp;quot;1&amp;quot;:  
                users = register_user(users)  
            case &amp;quot;2&amp;quot;:  
                login_user(users)  
            case &amp;quot;3&amp;quot;:  
                print(&amp;quot;Выход из программы. До свидания!&amp;quot;)  
                break  
            case _:  
                print(&amp;quot;Неверный выбор. Попробуйте еще раз.&amp;quot;)  
  
  
if __name__ == &amp;quot;__main__&amp;quot;:  
    main()&lt;/pre&gt;
  &lt;h3 id=&quot;9Emq&quot;&gt;Запускаем&lt;/h3&gt;
  &lt;p id=&quot;VURV&quot;&gt;Давайте залогиним моего кота в систему, чтобы он мог заказать себе вкусных рыбов.&lt;/p&gt;
  &lt;p id=&quot;rzIj&quot;&gt;Запустим программу:&lt;/p&gt;
  &lt;pre id=&quot;0gGj&quot; data-lang=&quot;bash&quot;&gt;python3 bcrypt_test.py&lt;/pre&gt;
  &lt;p id=&quot;Tm2J&quot;&gt;В зависимости от системы, здесь может быть просто &lt;code&gt;python bcrypt_test.py&lt;/code&gt;. Также программу можно запустить через IDE.&lt;/p&gt;
  &lt;p id=&quot;p2AS&quot;&gt;После запуска программа дружелюбно приветствует нас:&lt;/p&gt;
  &lt;pre id=&quot;v9AS&quot;&gt;Добро пожаловать в сервис доставки &amp;#x27;dostavallo&amp;#x27;!

Выберите действие:
1. Зарегистрироваться
2. Войти в систему
3. Выход
Ваш выбор: &lt;/pre&gt;
  &lt;p id=&quot;MJoE&quot;&gt;Выберем регистрацию, для этого достаточно ввести цифру 1.&lt;br /&gt;Вводим имя пользователя и пароль:&lt;/p&gt;
  &lt;pre id=&quot;mqgj&quot;&gt;Ваш выбор: 1    
Введите имя пользователя: Bublik
Введите пароль: Fi$h&lt;/pre&gt;
  &lt;p id=&quot;TeeV&quot;&gt;Видим сообщение об успешной регистрации и переход в главное меню:&lt;/p&gt;
  &lt;pre id=&quot;1rvq&quot;&gt;Пользователь Bublik успешно зарегистрирован!

Выберите действие:
1. Зарегистрироваться
2. Войти в систему
3. Выход
Ваш выбор: &lt;/pre&gt;
  &lt;p id=&quot;jhut&quot;&gt;Выберем вход, для этого введем цифру 2.&lt;br /&gt;Далее вводим логин и пароль, которые мы только что создали:&lt;/p&gt;
  &lt;pre id=&quot;zOAU&quot;&gt;Ваш выбор: 2
Введите имя пользователя: Bublik
Введите пароль (или нажмите &amp;#x27;q&amp;#x27; для возврата в главное меню): Fi$h

Введен Правильный пароль, вход выполнен. 
Добро пожаловать, Bublik!&lt;/pre&gt;
  &lt;p id=&quot;QIIm&quot;&gt;Удача буквально преследует нас, все получилось!&lt;/p&gt;
  &lt;h3 id=&quot;Ad0u&quot;&gt;А что с паролями для dostavallo?&lt;/h3&gt;
  &lt;p id=&quot;eimu&quot;&gt;Давайте откроем файлик &lt;code&gt;users.json&lt;/code&gt; и посмотрим, как хранятся данные наших пользователей. Внутри мы увидим примерно такое:&lt;/p&gt;
  &lt;pre id=&quot;0oUW&quot; data-lang=&quot;python&quot;&gt;{  
    &amp;quot;Bublik&amp;quot;: &amp;quot;$2b$12$VgDqlE5Xcwm7ID20Aw9isuA.qQp1o.hnMN1./8xxrqS1qXdRpuCW2&amp;quot;  
}&lt;/pre&gt;
  &lt;p id=&quot;DnsU&quot;&gt;Теперь мы можем быть спокойны. Никто просто так не получит пароль нашего пользователя, даже при условии, что у него был задан довольно простой пароль.&lt;/p&gt;
  &lt;p id=&quot;5YQz&quot;&gt;Злоумышленники не смогут поднять историю заказов и пошантажировать кота тем, что недавно он заказывал руководство &amp;quot;Как заставить хозяев играть с тобой по ночам&amp;quot;.&lt;/p&gt;
  &lt;h3 id=&quot;aYLt&quot;&gt;Послесловие&lt;/h3&gt;
  &lt;p id=&quot;blqo&quot;&gt;Теперь мы знаем, что соль бывает не только поваренной, а радужные таблицы - это вовсе не про закрашенные ячейки в MS Excel :)&lt;/p&gt;
  &lt;p id=&quot;Ogve&quot;&gt;Познакомились поближе с хэш-функциями и спасли сервис доставки от беды, а кота - от шантажа.&lt;/p&gt;
  &lt;p id=&quot;tIE4&quot;&gt;Кстати, если хотите проверить, не утекал ли ваш пароль куда-нибудь, можете сделать это с помощью сайта https://haveibeenpwned.com. Просто вводите туда свою почту и получите ответ :). &lt;/p&gt;
  &lt;p id=&quot;FtL5&quot;&gt;Если увидели там что-то, поступаем просто - меняем пароль для указанного сервиса.&lt;/p&gt;
  &lt;p id=&quot;IsJk&quot;&gt;Я вот себя нашел. По информации сайта, мои данные подвергались утечке 3 раза.&lt;/p&gt;
  &lt;p id=&quot;gfgQ&quot;&gt;&lt;strong&gt;Подробнее:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;4oqR&quot;&gt;
    &lt;li id=&quot;Vsxd&quot;&gt;https://www.securitylab.ru/blog/personal/xiaomite-journal/353801.php&lt;/li&gt;
    &lt;li id=&quot;csuF&quot;&gt;https://en.wikipedia.org/wiki/Have_I_Been_Pwned%3F&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;LGn3&quot;&gt;Берегите себя во всех смыслах.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;iaz2&quot;&gt;Мой telegram-канал, присоединяйтесь :)&lt;br /&gt;⬇⬇⬇&lt;br /&gt;&lt;a href=&quot;https://t.me/python3_with_love&quot; target=&quot;_blank&quot;&gt;https://t.me/python3_with_love&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:dict_get</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/dict_get?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Как безопасно работать со словарями в Python и избежать KeyError</title><published>2025-01-18T16:40:13.576Z</published><updated>2025-01-18T16:40:13.576Z</updated><summary type="html">Словарь (или dict) в Python, одна из базовых и очень часто используемых структур, с которой мы обычно взаимодействуем также часто, как и с любимой кружечкой, из которой пьем кофе по утрам. :)</summary><content type="html">
  &lt;p id=&quot;S53A&quot;&gt;Словарь (или dict) в Python, одна из базовых и очень часто используемых структур, с которой мы обычно взаимодействуем также часто, как и с любимой кружечкой, из которой пьем кофе по утрам. :)&lt;/p&gt;
  &lt;p id=&quot;1Da0&quot;&gt;Мы используем их повсеместно: для работы с данными из API, хранения конфигураций и многого другого. Однако работа с отсутствующими ключами в словаре неожиданно может привести к исключению &lt;code&gt;KeyError&lt;/code&gt;. В этой статье разберём, как безопасно извлекать значения из словаря, чтобы избежать подобных ошибок.&lt;/p&gt;
  &lt;p id=&quot;zlhc&quot;&gt;Обычно мы получаем значение по ключу через квадратные скобки, например так:&lt;/p&gt;
  &lt;pre id=&quot;PhAU&quot; data-lang=&quot;python&quot;&gt;my_dict = {&amp;quot;key_1&amp;quot;: &amp;quot;value_1&amp;quot;, &amp;quot;key_2&amp;quot;: &amp;quot;value_2&amp;quot;}
print(my_dict[&amp;quot;key_1&amp;quot;])

&amp;gt;&amp;gt;&amp;gt; value_1&lt;/pre&gt;
  &lt;p id=&quot;orTZ&quot;&gt;Вопросов к этому способу нет, все работает как часы и мы всегда получаем ровно то что нам нужно.&lt;/p&gt;
  &lt;p id=&quot;eUE7&quot;&gt;Но что, если мы работаем с словарями, полученными из внешних источников, например, http запросов, и какое либо поле является опциональным, а нам в коде надо в зависимости от этого прописать какое-то условие? Мы можем поступить ровно также, используя квадратные скобки для сравнения значений:&lt;/p&gt;
  &lt;pre id=&quot;UZib&quot; data-lang=&quot;python&quot;&gt;if new_dict[&amp;quot;key&amp;quot;] == &amp;quot;value&amp;quot;:
    print(&amp;quot;ok!&amp;quot;)
else:
    print(&amp;quot;no!&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;QzEF&quot;&gt;И это будет работать ровно до того момента, пока эта пара ключ-значение есть внутри словаря. Если же ключ не найдется, мы получим исключение &lt;code&gt;KeyError&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;bF1z&quot; data-lang=&quot;python&quot;&gt;new_dict = {&amp;quot;another_key&amp;quot;: &amp;quot;another_value&amp;quot;}
 
if new_dict[&amp;quot;key&amp;quot;] == &amp;quot;value&amp;quot;:
    print(&amp;quot;ok!&amp;quot;)
else:
    print(&amp;quot;no!&amp;quot;)

&amp;gt;&amp;gt;&amp;gt; KeyError: &amp;#x27;key&amp;#x27;&lt;/pre&gt;
  &lt;p id=&quot;4M85&quot;&gt;Чтобы этого избежать, можно использовать встроенный метод словаря &lt;code&gt;get()&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;LPNo&quot; data-lang=&quot;python&quot;&gt;new_dict = {&amp;quot;another_key&amp;quot;: &amp;quot;another_value&amp;quot;}
 
# Проверяем ключ безопасно с помощью get()
if new_dict.get(&amp;quot;key&amp;quot;) == &amp;quot;value&amp;quot;:
    print(&amp;quot;ok!&amp;quot;)
else:
    print(&amp;quot;no!&amp;quot;)
 
&amp;gt;&amp;gt;&amp;gt; no!&lt;/pre&gt;
  &lt;p id=&quot;2bPY&quot;&gt;Также можно просто проверить присутствие ключа в словаре, и в зависимости от этого уже выполнять какие-либо действия:&lt;/p&gt;
  &lt;pre id=&quot;ra1z&quot; data-lang=&quot;python&quot;&gt;new_dict = {&amp;quot;another_key&amp;quot;: &amp;quot;another_value&amp;quot;}

if new_dict.get(&amp;quot;key&amp;quot;):
    ...&lt;/pre&gt;
  &lt;p id=&quot;yO3H&quot;&gt;Здесь мы просто проверяем объект на наличие. &lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;Mga0&quot;&gt;По умолчанию для не найденных ключей метод &lt;code&gt;get()&lt;/code&gt; подставляет значение &amp;#x60;&lt;code&gt;None&lt;/code&gt;.&lt;br /&gt;Вместо &lt;code&gt;None&lt;/code&gt; можно подставить любое необходимое значение:&lt;/p&gt;
  &lt;pre id=&quot;xsgH&quot; data-lang=&quot;python&quot;&gt;new_dict = {&amp;quot;another_key&amp;quot;: &amp;quot;another_value&amp;quot;} 
 
# Используем значение по умолчанию для отсутствующего ключа
print(new_dict.get(&amp;quot;key&amp;quot;, &amp;quot;default_value&amp;quot;))
 
&amp;gt;&amp;gt;&amp;gt; default_value&lt;/pre&gt;
  &lt;p id=&quot;Agz2&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;iggo&quot;&gt;Методы, такие как &lt;code&gt;get()&lt;/code&gt;, делают работу со словарями в Python более безопасной. Это особенно важно при работе с данными из внешних источников, где структура словаря может быть непредсказуемой. Используя эти подходы, вы не только избежите ошибок, но и сделаете ваш код более читаемым и устойчивым к неожиданным данным.&lt;/p&gt;
  &lt;p id=&quot;Kcwj&quot;&gt;&lt;/p&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;tb1J&quot;&gt;Мой telegram-канал, присоединяйтесь :)&lt;/p&gt;
  &lt;p id=&quot;Ruby&quot;&gt;⬇⬇⬇&lt;/p&gt;
  &lt;p id=&quot;vKUV&quot;&gt;&lt;a href=&quot;https://t.me/python3_with_love&quot; target=&quot;_blank&quot;&gt;https://t.me/python3_with_love&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:mro_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/mro_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>MRO или Method Resolution Order в Python</title><published>2025-01-12T19:12:38.650Z</published><updated>2025-01-12T19:12:38.650Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/56/a1/56a114da-7e1a-4f3f-8e71-dc80934fefaf.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/bb/37/bb37bc22-7ae6-4b65-a42f-90837622736a.png&quot;&gt;MRO - Это порядок разрешения методов и о нем обычно говорят, когда речь заходит о множественном наследовании (кстати, если с ним не знакомы, настоятельно рекомендую немного покопаться в теме).</summary><content type="html">
  &lt;h2 id=&quot;oS1g&quot;&gt;Что такое MRO&lt;/h2&gt;
  &lt;p id=&quot;ATCy&quot;&gt;MRO - Это порядок разрешения методов и о нем обычно говорят, когда речь заходит о множественном наследовании (кстати, если с ним не знакомы, настоятельно рекомендую немного покопаться в теме).&lt;/p&gt;
  &lt;p id=&quot;2sZF&quot;&gt;Он определяет последовательность, по которой Python ищет методы и атрибуты в классе и его родителях. Чтобы чуть глубже понять, предлагаю рассмотреть принцип его работы на конкретном примере.&lt;/p&gt;
  &lt;h2 id=&quot;YHI0&quot;&gt;Разбираемся с MRO на примере&lt;/h2&gt;
  &lt;p id=&quot;MQ8d&quot;&gt;Допустим, у нас есть некий класс D, который наследуется от классов B и C, которые наследуются от класса A, который ... :) пожалуй, остановимся на этом.&lt;/p&gt;
  &lt;figure id=&quot;eLwU&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/bb/37/bb37bc22-7ae6-4b65-a42f-90837622736a.png&quot; width=&quot;1184&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ywNk&quot;&gt;У этих классов определены методы &lt;code&gt;who_am_i&lt;/code&gt;, которые просто печатают имя класса, которому они принадлежат. Классу D мы не будем создавать этот метод, а сделаем из него просто класс-заглушку:&lt;/p&gt;
  &lt;pre id=&quot;2Pwp&quot; data-lang=&quot;python&quot;&gt;class A:
    def who_am_i(self):
        print(&amp;quot;A&amp;quot;) 


class B(A): 
    def who_am_i(self):
        print(&amp;quot;B&amp;quot;) 


class C(A):
    def who_am_i(self):
        print(&amp;quot;C&amp;quot;) 


class D(B, C):
    ...
&lt;/pre&gt;
  &lt;p id=&quot;4qwI&quot;&gt;Теперь создадим экземпляр класса &lt;code&gt;D&lt;/code&gt; и попробуем вызвать у него метод &lt;code&gt;who_am_i&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;rzXx&quot; data-lang=&quot;python&quot;&gt;d = D()
d.who_am_i()&lt;/pre&gt;
  &lt;p id=&quot;Q83R&quot;&gt;Что произойдет? А вот что. В выводе мы увидим:&lt;/p&gt;
  &lt;pre id=&quot;4Iu1&quot; data-lang=&quot;python&quot;&gt;&amp;gt;&amp;gt;&amp;gt; B&lt;/pre&gt;
  &lt;p id=&quot;NmtR&quot;&gt;Почему именно &lt;code&gt;B&lt;/code&gt;, нам может рассказать MRO. Для удобства можно просто вызвать метод &lt;code&gt;mro()&lt;/code&gt; или атрибут &lt;code&gt;__mro__&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;ptBT&quot; data-lang=&quot;python&quot;&gt;print(D.mro())  # или: print(D.__mro__)
&amp;gt;&amp;gt;&amp;gt; (&amp;lt;class &amp;#x27;__main__.D&amp;#x27;&amp;gt;, &amp;lt;class &amp;#x27;__main__.B&amp;#x27;&amp;gt;, &amp;lt;class &amp;#x27;__main__.C&amp;#x27;&amp;gt;, &amp;lt;class &amp;#x27;__main__.A&amp;#x27;&amp;gt;, &amp;lt;class &amp;#x27;object&amp;#x27;&amp;gt;)&lt;/pre&gt;
  &lt;p id=&quot;rlF6&quot;&gt;Из вывода консоли можно увидеть порядок обращений. Сначала метод ищется в классе &lt;code&gt;D&lt;/code&gt;, но его там нет, поэтому Python смотрит в следующем классе - &lt;code&gt;B&lt;/code&gt;. Метод &lt;code&gt;who_am_i&lt;/code&gt; есть у класса &lt;code&gt;B&lt;/code&gt;, поэтому именно он и вызывается. Соответственно, при печати мы и видим &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;QERg&quot;&gt;Почему MRO работает именно так?&lt;/h2&gt;
  &lt;p id=&quot;OjlZ&quot;&gt;Во всем виновата линеаризация и конкретно алгоритм &lt;code&gt;C3 linearization&lt;/code&gt;. Разберем кратко, что это такое.&lt;/p&gt;
  &lt;p id=&quot;WYI7&quot;&gt;&lt;strong&gt;Линеаризация&lt;/strong&gt; – это упорядочение классов (включая сам класс и его родителей) в список, отсортированный в порядке их &amp;quot;Удаленности&amp;quot;. Этот список Python использует для поиска методов и атрибутов. А алгоритм C3 определяет три главные особенности такого порядка:&lt;/p&gt;
  &lt;ul id=&quot;lOXH&quot;&gt;
    &lt;li id=&quot;B4AE&quot;&gt;устойчивый и расширяющийся (по старшинству)&lt;/li&gt;
    &lt;li id=&quot;XBRB&quot;&gt;сохранение локального порядка старшинства&lt;/li&gt;
    &lt;li id=&quot;72o7&quot;&gt;монотонность&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;9GcE&quot;&gt;Почитать подробнее и ознакомиться с примерами &lt;a href=&quot;https://ru.wikipedia.org/wiki/C3-%D0%BB%D0%B8%D0%BD%D0%B5%D0%B0%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F&quot; target=&quot;_blank&quot;&gt;можно на Вики&lt;/a&gt;&lt;/p&gt;
  &lt;p id=&quot;tzlx&quot;&gt;Таким образом, MRO определяет, как Python ищет методы и атрибуты в классах при множественном наследовании, делая этот процесс предсказуемым и логичным.&lt;/p&gt;

</content></entry><entry><id>python3_with_love:defaultdict_in_python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/defaultdict_in_python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>defaultdict в Python</title><published>2024-11-03T12:47:52.576Z</published><updated>2024-11-03T12:47:52.576Z</updated><summary type="html">Что еще за defaultdict и зачем нам еще один dict? Давайте об этом и поговорим в статье.</summary><content type="html">
  &lt;p id=&quot;CdRN&quot;&gt;Что еще за &lt;code&gt;defaultdict&lt;/code&gt; и зачем нам еще один &lt;code&gt;dict&lt;/code&gt;? Давайте об этом и поговорим в статье.&lt;/p&gt;
  &lt;h2 id=&quot;0YMU&quot;&gt;Что такое defaultdict&lt;/h2&gt;
  &lt;p id=&quot;nrg6&quot;&gt;&lt;br /&gt;Это подкласс встроенного класса &lt;code&gt;dict&lt;/code&gt;, который вызывает фабричную функцию, позволяющую задать дефолтное значение для новых / несуществующих ключей. Во всем остальном он схож с уже знакомым нам &lt;code&gt;dict&lt;/code&gt;.&lt;br /&gt;Если упростить и вывести термин, опираясь на название, то получается, что это просто словарь с значениями по умолчанию.&lt;br /&gt;&lt;/p&gt;
  &lt;h3 id=&quot;VIPI&quot;&gt;Синтаксис&lt;/h3&gt;
  &lt;pre id=&quot;0gJm&quot; data-lang=&quot;python&quot;&gt;from collections import defaultdict

defaultdict(default_factory=None, /, [...]) --&amp;gt; dict with default factory&lt;/pre&gt;
  &lt;p id=&quot;005z&quot;&gt;&lt;br /&gt;&lt;strong&gt;Аргументы&lt;br /&gt;&lt;/strong&gt;Первый аргумент предоставляет начальное значение для атрибута default_factory, которое по умолчанию равно &lt;code&gt;None&lt;/code&gt;. Все остальные аргументы обрабатываются так же, как если бы они были переданы конструктору &lt;code&gt;dict&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;NIpR&quot;&gt;&lt;strong&gt;Пара слов про метод &lt;code&gt;__missing__()&lt;/code&gt;&lt;br /&gt;&lt;/strong&gt;Если аргумент &lt;code&gt;default_factory&lt;/code&gt; != &lt;code&gt;None&lt;/code&gt;, то этот метод и вызывается для предоставления значений по умолчанию, когда запрошенный ключ не найден.&lt;/p&gt;
  &lt;p id=&quot;qXAk&quot;&gt;Чтобы в полной мере понять происходящее, давайте рассмотрим несколько примеров.&lt;/p&gt;
  &lt;h2 id=&quot;8i1L&quot;&gt;Примеры. Какие проблемы решает defaultdict&lt;/h2&gt;
  &lt;h3 id=&quot;2Mk5&quot;&gt;Получаем нужное нам значение по умолчанию&lt;/h3&gt;
  &lt;p id=&quot;pZfa&quot;&gt;Давайте создадим 2 словаря: 1 - &lt;code&gt;dict&lt;/code&gt;, другой - &lt;code&gt;defaultdict&lt;/code&gt; и попробуем получить значения для существующих ключей:&lt;br /&gt;&lt;/p&gt;
  &lt;pre id=&quot;bU9C&quot; data-lang=&quot;python&quot;&gt;from collections import defaultdict  
  
dict_1 = {&amp;quot;first&amp;quot;: 1, &amp;quot;second&amp;quot;: 2}  
dict_2 = defaultdict(int, first=1, second=2)

print(dict_1[&amp;quot;first&amp;quot;])  # 1
print(dict_2[&amp;quot;first&amp;quot;])  # 1&lt;/pre&gt;
  &lt;p id=&quot;Q8eW&quot;&gt;В обоих случаях получим 1.&lt;/p&gt;
  &lt;p id=&quot;y9k1&quot;&gt;А что, если запросить значение для несуществующего ключа:&lt;/p&gt;
  &lt;pre id=&quot;vrs0&quot; data-lang=&quot;python&quot;&gt;print(dict_1[&amp;quot;missing_key&amp;quot;])  # KeyError: &amp;#x27;missing_key&amp;#x27;
print(dict_2[&amp;quot;missing_key&amp;quot;])  # 0&lt;/pre&gt;
  &lt;p id=&quot;Jhsc&quot;&gt;В первом случае мы получили исключение &lt;code&gt;KeyError&lt;/code&gt;, а вот уже с &lt;code&gt;defaultdict&lt;/code&gt; мы получили значение по умолчанию: &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;aj5O&quot;&gt;Можно ли обойти эту ситуацию с помощью dict? Да, можно. Например, так:&lt;/p&gt;
  &lt;pre id=&quot;0C4u&quot; data-lang=&quot;python&quot;&gt;print(dict_1.get(&amp;quot;missing_key&amp;quot;, 0))  # 0&lt;/pre&gt;
  &lt;p id=&quot;PRM2&quot;&gt;Здесь мы явно задали значение по умолчанию в виде 0, если не получится найти ключ.&lt;/p&gt;
  &lt;p id=&quot;Fz3q&quot;&gt;Идем дальше.&lt;br /&gt;&lt;/p&gt;
  &lt;h3 id=&quot;U8Lu&quot;&gt;Считаем количество слов в списке&lt;/h3&gt;
  &lt;p id=&quot;5lrf&quot;&gt;Допустим, у нас есть список из слов &lt;code&gt;list_1&lt;/code&gt; и надо посчитать, сколько раз каждое слово встречается в списке, затем вывести все это в формате словаря.&lt;br /&gt;Как сделать это удобно? Конечно с &lt;code&gt;defaultdict&lt;/code&gt;!&lt;br /&gt;&lt;/p&gt;
  &lt;pre id=&quot;uxLB&quot; data-lang=&quot;python&quot;&gt;
from collections import defaultdict  

list_1 = [&amp;quot;building&amp;quot;, &amp;quot;thee&amp;quot;, &amp;quot;sun&amp;quot;, &amp;quot;python&amp;quot;, &amp;quot;sun&amp;quot;, &amp;quot;python&amp;quot;, &amp;quot;python&amp;quot;, &amp;quot;thee&amp;quot;, &amp;quot;python&amp;quot;] 

# 1.
result = defaultdict(int)   

# 2.
for word in list_1:
    result[word] += 1

# 3.
print(dict(result))  # {&amp;#x27;building&amp;#x27;: 1, &amp;#x27;thee&amp;#x27;: 2, &amp;#x27;sun&amp;#x27;: 2, &amp;#x27;python&amp;#x27;: 4}&lt;/pre&gt;
  &lt;p id=&quot;1x90&quot;&gt;&lt;strong&gt;&lt;br /&gt;Что здесь происходит?&lt;br /&gt;&lt;/strong&gt;1. Мы инициализируем &lt;code&gt;defaultdict&lt;/code&gt; классом &lt;code&gt;int&lt;/code&gt; для того, чтобы для каждого нового слова было задано значение по умолчанию = 0.&lt;br /&gt;2. Проходимся по всем словам из списка &lt;code&gt;list_1.&lt;/code&gt; &lt;br /&gt;   - Если слово не встречалось ранее, в &lt;code&gt;result&lt;/code&gt; создается новая пара ключ-значение, где ключ - слово, а значение - &lt;code&gt;0&lt;/code&gt;, после чего оно сразу увеличивается на 1. &lt;br /&gt;   - Если слово уже есть в словаре, то его значение просто увеличивается на 1.&lt;br /&gt;3. Печатаем наш результат, предварительно преобразовав наш &lt;code&gt;defaultdict&lt;/code&gt; в обычный &lt;code&gt;dict&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;dpzA&quot;&gt;В результате получаем нужный нам ответ: &lt;code&gt;{&amp;#x27;building&amp;#x27;: 1, &amp;#x27;thee&amp;#x27;: 2, &amp;#x27;sun&amp;#x27;: 2, &amp;#x27;python&amp;#x27;: 4}&lt;/code&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;TYm6&quot;&gt;Делаем выводы&lt;/h2&gt;
  &lt;p id=&quot;gEQZ&quot;&gt;&lt;br /&gt;&lt;code&gt;defaultdict&lt;/code&gt; в Python — это удобный инструмент для создания словарей с заданным значением по умолчанию для новых ключей. Он упрощает код, позволяя избежать явных проверок на наличие ключа перед его использованием, что делает код более чистым и сокращает необходимость в дополнительных условиях.&lt;/p&gt;
  &lt;p id=&quot;T1HZ&quot;&gt;Это особенно полезно при работе с данными, где нужно сгруппировать или подсчитать элементы. &lt;code&gt;defaultdict&lt;/code&gt; поддерживает различные типы значений по умолчанию, включая списки, множества и даже пользовательские функции, что делает его гибким и мощным инструментом для многих задач.&lt;br /&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:auto-enum</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/auto-enum?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Автоматическое присваивание значений в Enum</title><published>2024-09-06T20:40:01.582Z</published><updated>2024-09-06T20:40:01.582Z</updated><summary type="html">Представим ситуацию: вы любите все структурировать и храните много данных в енамах: Enum, StrEnum, IntEnum и так далее.</summary><content type="html">
  &lt;p id=&quot;PF7U&quot;&gt;Представим ситуацию: вы любите все структурировать и храните много данных в енамах: &lt;code&gt;Enum&lt;/code&gt;, &lt;code&gt;StrEnum&lt;/code&gt;, &lt;code&gt;IntEnum&lt;/code&gt; и так далее.&lt;/p&gt;
  &lt;p id=&quot;zefe&quot;&gt;В какой-то момент обязательно накопится большое количество структур, где символическое имя будет равно значению, как в примере ниже:&lt;/p&gt;
  &lt;pre id=&quot;p14d&quot; data-lang=&quot;python&quot;&gt;from enum import Enum  
  
  
class MyMood(Enum):  
    HAPPY = &amp;quot;HAPPY&amp;quot;  
    SAD = &amp;quot;SAD&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;zr26&quot;&gt;Как красиво выйти из этой ситуации? Об этом мы и поговорим.&lt;/p&gt;
  &lt;h2 id=&quot;NipC&quot;&gt;Функция auto()&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;QjBi&quot;&gt;В первую очередь, стоит обратить внимание на класс &lt;code&gt;auto&lt;/code&gt; из &lt;code&gt;Enum&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;Lpsa&quot;&gt;Он позволяет нам автоматически присваивать значения для каждого символического имени, мы можем просто указать его в виде значений:&lt;/p&gt;
  &lt;pre id=&quot;LSrL&quot; data-lang=&quot;python&quot;&gt;from enum import Enum, auto  
  
  
class MyMood(Enum):  
    HAPPY = auto()  
    SAD = auto()&lt;/pre&gt;
  &lt;h2 id=&quot;dqrv&quot;&gt;Как это работает&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;6fKb&quot;&gt;Класс &lt;code&gt;auto&lt;/code&gt; автоматически присваивает значение для символического имени в виде номера по порядку.&lt;br /&gt;Например, для блока кода выше будут присвоены значения &lt;code&gt;HAPPY = 1&lt;/code&gt;, &lt;code&gt;SAD = 2&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;gg6w&quot; data-lang=&quot;python&quot;&gt;print(MyMood.HAPPY.value, MyMood.SAD.value)
&amp;gt; 1 2&lt;/pre&gt;
  &lt;h2 id=&quot;vfkf&quot;&gt;Когда это может пригодиться?&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;66R0&quot;&gt;Например, когда мы перебираем Enum-ы через &lt;code&gt;match-case&lt;/code&gt; и значения нам не важны.&lt;/p&gt;
  &lt;p id=&quot;rsRi&quot;&gt;А если важны?&lt;/p&gt;
  &lt;h2 id=&quot;9wxx&quot;&gt;Что делать, если в value нужно хранить именно символическое имя?&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;K5Zu&quot;&gt;Представим ситуацию, когда нам очень нужно, чтобы &lt;code&gt;name&lt;/code&gt; было равно &lt;code&gt;value&lt;/code&gt;, и чтоб это &lt;code&gt;value&lt;/code&gt; присваивалось автоматически.&lt;/p&gt;
  &lt;p id=&quot;tjEG&quot;&gt;Здесь нам на помощь придет переопределение метода &lt;code&gt;_generate_next_value_&lt;/code&gt;.&lt;/p&gt;
  &lt;h3 id=&quot;UWFR&quot;&gt;Синтаксис&lt;/h3&gt;
  &lt;p id=&quot;j6td&quot;&gt;&lt;code&gt;_generate_next_value_(name, start, count, last_values)&lt;/code&gt;&lt;br /&gt;Этот метод и отвечает за увеличение значения символического имени на 1 в исходной реализации, но мы можем подстроить его под себя.&lt;/p&gt;
  &lt;p id=&quot;6MhP&quot;&gt;Это можно сделать прямо внутри нужного нам Enum-а:&lt;/p&gt;
  &lt;pre id=&quot;1Srr&quot; data-lang=&quot;python&quot;&gt;class MyMood(Enum):  
    @staticmethod  
    def _generate_next_value_(name, start, count, last_values):  
        return name  
  
    HAPPY = auto()  
    SAD = auto()  &lt;/pre&gt;
  &lt;p id=&quot;VMVs&quot;&gt;главное, переопределить метод перед членами Enum-а, иначе словим исключение &amp;#x60;&lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;miyL&quot;&gt;И тогда при выводе значений в терминал мы получим уже имена членов Enum, а не индексы:&lt;/p&gt;
  &lt;pre id=&quot;qrQP&quot; data-lang=&quot;python&quot;&gt;print(MyMood.HAPPY.value, MyMood.SAD.value)
&amp;gt; HAPPY SAD&lt;/pre&gt;
  &lt;p id=&quot;OuQo&quot;&gt;Если ситуация часто повторяется, то можно сделать одно общее решение для всех. Создадим класс &lt;code&gt;AutoName&lt;/code&gt;, унаследованный от &lt;code&gt;Enum&lt;/code&gt; и переопределим метод &lt;code&gt;_generate_next_value_&lt;/code&gt;:  &lt;/p&gt;
  &lt;pre id=&quot;nhNo&quot; data-lang=&quot;python&quot;&gt;from enum import Enum, auto  


class AutoName(Enum):  
    @staticmethod  
    def _generate_next_value_(name, start, count, last_values):  
        return name  &lt;/pre&gt;
  &lt;p id=&quot;IN00&quot;&gt;Нам остается отнаследоваться от этого класса и использовать &lt;code&gt;auto()&lt;/code&gt; в качестве значений символических имен Enum-а:&lt;/p&gt;
  &lt;pre id=&quot;oHth&quot; data-lang=&quot;python&quot;&gt;class MyMood(AutoName):  
    HAPPY = auto()  
    SAD = auto()&lt;/pre&gt;
  &lt;p id=&quot;jb82&quot;&gt;Убедимся, что наша шалость удалась:&lt;/p&gt;
  &lt;pre id=&quot;AZ8C&quot; data-lang=&quot;python&quot;&gt;print(MyMood.HAPPY.value, MyMood.SAD.value)
&amp;gt; HAPPY SAD&lt;/pre&gt;
  &lt;p id=&quot;nX2Z&quot;&gt;Мы получили то, что нам было нужно, избавились от дублирования и лишней копипасты, сделав код еще немного чище.&lt;br /&gt;&lt;/p&gt;

</content></entry><entry><id>python3_with_love:Elegantnoe-podavlenie-isklyuchenij-s-contextlibs</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/Elegantnoe-podavlenie-isklyuchenij-s-contextlibs?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Элегантное подавление исключений с contextlib.suppress</title><published>2024-09-04T08:49:14.790Z</published><updated>2024-09-04T08:51:35.519Z</updated><summary type="html">Бывает так, что какой-либо метод при работе программы выдает исключение, но нам не надо его обрабатывать каким-нибудь хитрым образом, а достаточно лишь подавить исключение и идти дальше.</summary><content type="html">
  &lt;p id=&quot;yzrj&quot;&gt;Бывает так, что какой-либо метод при работе программы выдает исключение, но нам не надо его обрабатывать каким-нибудь хитрым образом, а достаточно лишь подавить исключение и идти дальше.&lt;/p&gt;
  &lt;p id=&quot;QQ22&quot;&gt;Это можно сделать с помощью классического &lt;code&gt;try-except&lt;/code&gt;, но есть и более элегантный способ. Когда это может пригодиться и как пользоваться? Об этом и поговорим в статье.&lt;/p&gt;
  &lt;h2 id=&quot;Зачем-подавлять-исключения?&quot;&gt;&lt;strong&gt;Зачем подавлять исключения?&lt;/strong&gt;&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;lksZ&quot;&gt;Делать это без надобности строго не рекомендуется. Но бывают моменты, когда мы знаем, почему вызывается исключение и уверены, что оно не несет никаких рисков для нас. Поэтому мы временно можем его пропустить.&lt;/p&gt;
  &lt;p id=&quot;GS3d&quot;&gt;&lt;strong&gt;Конкретный пример:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;PeGm&quot;&gt;Допустим, вы пишете автотест, и после создания некоторой сущности и выполнения манипуляций с ней, вам надо эту сущность удалить.&lt;/p&gt;
  &lt;p id=&quot;mxcO&quot;&gt;Все отрабатывает корректно, вот только запрос на удаление зависает в статусе &lt;code&gt;pending&lt;/code&gt; и ответ не приходит, вызывается исключение по таймауту, но при этом сущность удаляется.&lt;/p&gt;
  &lt;p id=&quot;WY6T&quot;&gt;Зная все это, мы можем временно подавить исключение до решения проблемы с запросом и пустить тест в работу.&lt;/p&gt;
  &lt;h2 id=&quot;Как-это-сделать?&quot;&gt;&lt;strong&gt;Как это сделать?&lt;/strong&gt;&lt;/h2&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;5p9P&quot;&gt;Рассмотрим 2 способа: канонический &lt;code&gt;try-except&lt;/code&gt; и &lt;code&gt;contextlib.suppress&lt;/code&gt;&lt;/p&gt;
  &lt;h3 id=&quot;Классический-способ&quot;&gt;&lt;strong&gt;Классический способ&lt;/strong&gt;&lt;/h3&gt;
  &lt;pre id=&quot;Q1Uu&quot; data-lang=&quot;python&quot;&gt;try:
    requests.delete(url=https://example.com/users/user1)
except requests.TimeoutException:
    pass
&lt;/pre&gt;
  &lt;h3 id=&quot;contextlib.suppress&quot;&gt;&lt;strong&gt;contextlib.suppress&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;O66J&quot;&gt;&lt;strong&gt;Синтаксис&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;QuNj&quot;&gt;contextlib.&lt;strong&gt;suppress&lt;/strong&gt;(*&lt;em&gt;exceptions&lt;/em&gt;)&lt;/p&gt;
  &lt;p id=&quot;I0UE&quot;&gt;&lt;strong&gt;Пример&lt;/strong&gt;:&lt;/p&gt;
  &lt;pre id=&quot;xjuL&quot; data-lang=&quot;python&quot;&gt;from contextlib import suppress

with suppress(requests.TimeoutException):
    requests.delete(url=https://example.com/users/user1)
&lt;/pre&gt;
  &lt;p id=&quot;dx1L&quot;&gt;&lt;code&gt;suppress&lt;/code&gt; возвращает контекстный менеджер, который подавляет переданные исключения, если они встречаются в теле выражения &lt;code&gt;with&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;GtKY&quot;&gt;Данный контекстный менеджер предлагает нам удобный способ работы с исключениями. Но не стоит забывать, что &lt;code&gt;suppress&lt;/code&gt; cледует использовать только в специфических случаях.&lt;/p&gt;

</content></entry><entry><id>python3_with_love:Nemnogo-pro-lyambda-funkcii-v-Python</id><link rel="alternate" type="text/html" href="https://teletype.in/@python3_with_love/Nemnogo-pro-lyambda-funkcii-v-Python?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=python3_with_love"></link><title>Немного про лямбда-функции в Python</title><published>2024-09-04T08:46:36.353Z</published><updated>2024-09-04T08:48:37.695Z</updated><summary type="html">Lambda или анонимные функции - это по сути небольшие функции без имени, написанные в одну строку.</summary><content type="html">
  &lt;h3 id=&quot;Что-это-такое?&quot;&gt;Что это такое?&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;0GHg&quot;&gt;Lambda или анонимные функции - это по сути небольшие функции без имени, написанные в одну строку.&lt;/p&gt;
  &lt;p id=&quot;5scl&quot;&gt;Для их объявления нам не нужно следовать классической схеме - указывать литерал &lt;code&gt;def&lt;/code&gt; и имя будущей функции. Нам достаточно следовать такой конструкции:&lt;/p&gt;
  &lt;pre id=&quot;pdYA&quot; data-lang=&quot;python&quot;&gt;lambda arguments: expression&lt;/pre&gt;
  &lt;p id=&quot;AqQF&quot;&gt;Это и есть весь синтаксис лямбда-функций.&lt;/p&gt;
  &lt;p id=&quot;8OKl&quot;&gt;Главные моменты:&lt;/p&gt;
  &lt;ul id=&quot;9qnl&quot;&gt;
    &lt;li id=&quot;erpZ&quot;&gt;Аргументов может быть несколько&lt;/li&gt;
    &lt;li id=&quot;vNGb&quot;&gt;Выражение может быть только одно&lt;/li&gt;
    &lt;li id=&quot;odfO&quot;&gt;Выражение должно быть написано в одну строку&lt;/li&gt;
    &lt;li id=&quot;Ru2p&quot;&gt;Лямбда-функция всегда возвращает значение&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;Как-и-когда-использовать?&quot;&gt;Как и когда использовать?&lt;/h3&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;TKhv&quot;&gt;Чаще всего, лямбда-функции используют в нескольких случаях:&lt;/p&gt;
  &lt;ul id=&quot;N2zy&quot;&gt;
    &lt;li id=&quot;OTQi&quot;&gt;Как аргумент в других функциях (часто в функциях высшего порядка)&lt;/li&gt;
    &lt;li id=&quot;bF9c&quot;&gt;Для создания замыканий&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;pCxv&quot;&gt;Рассмотрим чуть подробнее.&lt;/p&gt;
  &lt;h3 id=&quot;Лямбда-функция-как-аргумент-в-других-функциях&quot;&gt;Лямбда-функция как аргумент в других функциях&lt;/h3&gt;
  &lt;p id=&quot;mztk&quot;&gt;Очень удобно применять лямбда-функции при работе с &lt;code&gt;map()&lt;/code&gt; и &lt;code&gt;filter()&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;0Kw8&quot;&gt;Допустим, нам нужно преобразовать список. Здесь нам поможет функция &lt;code&gt;map()&lt;/code&gt;, которая применит лямбда-функцию ко всем элементам списка:&lt;/p&gt;
  &lt;pre id=&quot;2eT8&quot; data-lang=&quot;python&quot;&gt;list_1 = [&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot;]
new_list = list(map(lambda x: &amp;quot;new_&amp;quot; + x, list_1))

print(new_list)
&amp;gt;&amp;gt;&amp;gt; [&amp;#x27;new_one&amp;#x27;, &amp;#x27;new_two&amp;#x27;, &amp;#x27;new_three&amp;#x27;]&lt;/pre&gt;
  &lt;p id=&quot;tmFX&quot;&gt;Лямбда помогает нам задать нечто вроде правила, по которому будет обрабатываться объект. В нашем примере мы добавили префикс к каждому слову в списке.&lt;/p&gt;
  &lt;p id=&quot;BfZJ&quot;&gt;Эту же операцию можно решить и с помощью &lt;code&gt;list comprehension&lt;/code&gt; (списочных выражений):&lt;/p&gt;
  &lt;pre id=&quot;Ljtg&quot; data-lang=&quot;python&quot;&gt;new_list = [&amp;quot;new_&amp;quot; + x for x in list_1]&lt;/pre&gt;
  &lt;p id=&quot;CsLB&quot;&gt;Теперь рассмотрим простой пример с фильтрацией:&lt;/p&gt;
  &lt;pre id=&quot;5aMe&quot; data-lang=&quot;python&quot;&gt;list_1 = [&amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot;]
new_list = list(filter(lambda x: len(x) &amp;lt;= 3, list_1))

print(new_list)
&amp;gt;&amp;gt;&amp;gt; [&amp;#x27;one&amp;#x27;, &amp;#x27;two&amp;#x27;]&lt;/pre&gt;
  &lt;p id=&quot;SBlN&quot;&gt;Мы применили функцию &lt;code&gt;filter()&lt;/code&gt; и получили новый список с элементами, у которых количество символов меньше или равно 3.&lt;/p&gt;
  &lt;p id=&quot;brGN&quot;&gt;Эту операцию тоже можно решить с помощью &lt;code&gt;list comprehension&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;UxRR&quot; data-lang=&quot;python&quot;&gt;new_list = [x for x in list_1 if len(x) &amp;lt;= 3]&lt;/pre&gt;
  &lt;p id=&quot;z1Yn&quot;&gt;Рассмотрим еще 1 применение при сортировке по ключу. Он будет интереснее, так как будем работать с объектами посложнее:&lt;/p&gt;
  &lt;pre id=&quot;BqBs&quot; data-lang=&quot;python&quot;&gt;# Создадаем класс с атрибутами экземпляра: имя, возраст и порода
class Cat:
    def __init__(self, name: str, age: int, breed: str) -&amp;gt; None:
        self.name = name
        self.age = age
        self.breed = breed

    # Переопределяем магический метод __repr__, чтобы при печати экземпляров выводился сразу человекочитаемый текст
    def __repr__(self) -&amp;gt; str:
        return f&amp;quot;&amp;lt;&amp;lt;Cat {self.name}, {self.breed}, {self.age} years old&amp;gt;&amp;gt;&amp;quot;

# Создаем экземпляры класса Cat
cat_1 = Cat(name=&amp;quot;Mars&amp;quot;, age=7, breed=&amp;quot;British Shorthair&amp;quot;)
cat_2 = Cat(name=&amp;quot;Oleg&amp;quot;, age=1, breed=&amp;quot;Devon Rex&amp;quot;)
cat_3 = Cat(name=&amp;quot;Bublik&amp;quot;, age=2, breed=&amp;quot;Russian Blue&amp;quot;)

# Создаем неотсортированный список с питомцами
list_of_cats = [cat_3, cat_1, cat_2]

# Сортируем список по имени, возрасту и породе
name_sorted = sorted(list_of_cats, key=lambda x: x.name)
age_sorted = sorted(list_of_cats, key=lambda x: x.age)
breed_sorted = sorted(list_of_cats, key=lambda x: x.breed)

print(name_sorted, age_sorted, breed_sorted, sep=&amp;quot;\n&amp;quot;)

[&amp;lt;&amp;lt;Cat Bublik, Russian Blue, 2 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Mars, British Shorthair, 7 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Oleg, Devon Rex, 1 years old&amp;gt;&amp;gt;]
[&amp;lt;&amp;lt;Cat Oleg, Devon Rex, 1 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Bublik, Russian Blue, 2 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Mars, British Shorthair, 7 years old&amp;gt;&amp;gt;]
[&amp;lt;&amp;lt;Cat Mars, British Shorthair, 7 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Oleg, Devon Rex, 1 years old&amp;gt;&amp;gt;, &amp;lt;&amp;lt;Cat Bublik, Russian Blue, 2 years old&amp;gt;&amp;gt;]&lt;/pre&gt;
  &lt;p id=&quot;ykc4&quot;&gt;Выглядит просто, читаемо и эффективно.&lt;/p&gt;
  &lt;p id=&quot;Rmqi&quot;&gt;Идем дальше.&lt;/p&gt;
  &lt;h3 id=&quot;Лямбда-функции-и-замыкания&quot;&gt;Лямбда-функции и замыкания&lt;/h3&gt;
  &lt;p id=&quot;WmXR&quot;&gt;Для начала пара слов о замыканиях.&lt;/p&gt;
  &lt;p id=&quot;lVqd&quot;&gt;&lt;strong&gt;Замыкание&lt;/strong&gt; — это вложенная функция, которая имеет доступ к переменным внешней функции даже после закрытия этой самой внешней функции.&lt;/p&gt;
  &lt;p id=&quot;QJOk&quot;&gt;С помощью замыканий можно сохранять значения и состояния между вызовами функций.&lt;/p&gt;
  &lt;p id=&quot;C6rP&quot;&gt;Может звучать немного запутанно, но пока не будем вдаваться в детальное объяснение и рассмотрим пример. Более подробно обсудим замыкания в будущих статьях. Ну а после примера, в любом случае, станет понятнее.&lt;/p&gt;
  &lt;pre id=&quot;tl02&quot; data-lang=&quot;python&quot;&gt;def multiplier(x):
    return lambda y: x * y

multiply_by_2 = multiplier(x=2)
multiply_by_5 = multiplier(x=5)

print(multiply_by_2(y=2))
print(multiply_by_5(y=2))

&amp;gt;&amp;gt;&amp;gt; 4
&amp;gt;&amp;gt;&amp;gt; 10&lt;/pre&gt;
  &lt;p id=&quot;kwVa&quot;&gt;Что происходит:&lt;/p&gt;
  &lt;ul id=&quot;nfz5&quot;&gt;
    &lt;li id=&quot;egh6&quot;&gt;Мы объявили функцию &lt;code&gt;multiplier&lt;/code&gt; с параметром x, внутри которой возвращается вложенная лямбда-функция, которая умножает &lt;code&gt;x&lt;/code&gt; на &lt;code&gt;y&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;NeNG&quot;&gt;Далее мы по сути делаем из переменных &lt;code&gt;multiply_by_2&lt;/code&gt; и &lt;code&gt;multiply_by_5&lt;/code&gt; функции-перемножители, которые запоминают значение переменной &lt;code&gt;x&lt;/code&gt;, которое будет доступно нам всегда.&lt;/li&gt;
    &lt;li id=&quot;W6Vo&quot;&gt;Затем вызываем уже внутреннюю лямбда-функцию, передавая перемножителю параметр &lt;code&gt;y&lt;/code&gt;.&lt;/li&gt;
    &lt;li id=&quot;Hg55&quot;&gt;В результате мы получаем произведение двух чисел - значений переменных &lt;code&gt;x&lt;/code&gt; и &lt;code&gt;y&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;Mtp8&quot;&gt;Таким образом, замыкания помогают на лету создавать функции, при этом частично изолировав логику выполнения (как мы видели на примере с конкретными перемножителями).&lt;/p&gt;
  &lt;p id=&quot;Krfz&quot;&gt;В этой статье мы окунулись в сферу применения лямбда-функций и затронули пару таких тем как функции высшего порядка и замыкания.&lt;/p&gt;
  &lt;p id=&quot;FZ3k&quot;&gt;Зачастую можно обойтись и без лямбда-функций, но в определенных моментах они могут добавить удобства и чистоты в код.&lt;/p&gt;

</content></entry></feed>