{"id":29662972,"url":"https://github.com/yuldashov10/topic_11","last_synced_at":"2025-09-11T12:41:06.143Z","repository":{"id":305562829,"uuid":"1023207353","full_name":"yuldashov10/topic_11","owner":"yuldashov10","description":"11. Работа с кортежами","archived":false,"fork":false,"pushed_at":"2025-07-20T19:15:30.000Z","size":315,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-20T21:07:37.308Z","etag":null,"topics":["learning-python","programming","python","python3","shox-py"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yuldashov10.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-20T18:45:18.000Z","updated_at":"2025-07-20T19:23:43.000Z","dependencies_parsed_at":"2025-07-20T21:07:46.752Z","dependency_job_id":"3b944d5e-26ed-4c8d-80b5-691d8f72ea1e","html_url":"https://github.com/yuldashov10/topic_11","commit_stats":null,"previous_names":["yuldashov10/topic_11"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/yuldashov10/topic_11","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuldashov10%2Ftopic_11","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuldashov10%2Ftopic_11/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuldashov10%2Ftopic_11/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuldashov10%2Ftopic_11/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yuldashov10","download_url":"https://codeload.github.com/yuldashov10/topic_11/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yuldashov10%2Ftopic_11/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266481732,"owners_count":23935938,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["learning-python","programming","python","python3","shox-py"],"created_at":"2025-07-22T11:06:28.832Z","updated_at":"2025-07-22T11:06:34.739Z","avatar_url":"https://github.com/yuldashov10.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 11. Работа с кортежами\n\n![cover.svg](img/cover.svg)\n\n---\n\n## Вступление\n\nДобро пожаловать в тему кортежей!\n\nМы уже вскользь касались кортежей в пятой теме, теперь пришло время разобраться в них подробнее.  \nВ этой теме разберём, чем кортежи отличаются от списков, как с ними работать, и зачем вообще они нужны.\n\n---\n\n## Содержание\n\n- [Что такое кортежи](#что-такое-кортежи)\n- [Как создавать и обращаться к кортежам](#как-создавать-и-обращаться-к-кортежам)\n- [В чем отличие от списков](#в-чем-отличие-от-списков)\n- [Какие операции и методы можно применять к кортежам](#какие-операции-и-методы-можно-применять-к-кортежам)\n- [Где они реально применяются](#где-они-реально-применяются)\n- [Аннотации типов](#аннотации-типов)\n- [Задания](#задания)\n\n---\n\n## Что такое кортежи\n\nВ прошлой теме мы рассматривали списки, уже знаем, что с ними можно делать:\n\n- _читать, изменять, добавлять, удалять и заменять элементы_.\n\nС кортежами всё строже, они **неизменяемы**. Это значит, что после создания кортежа\nмы не можем изменить его содержимое:\n\n- _ни добавить, ни удалить, ни заменить элемент_.\n\nТо есть операции, которые были привычны для списков, вроде `.remove()`, `.insert()`, `.pop()`, `.append()`,  \nв случае с кортежами просто не работают. У кортежей этих методов нет вовсе.\n\nОднако это не делает кортежи бесполезными, напротив, из-за _неизменяемости_ у них появляются сильные стороны,\nо которых поговорим дальше.\n\n---\n\n## Как создавать и обращаться к кортежам\n\nКортежи можно создавать несколькими способами, с помощью круглых скобок или функции `tuple()`.  \nТакже нужно помнить про особенности создания пустого кортежа и кортежа с одним элементом.\n\nРассмотрим все эти варианты на примерах.\n\n**Создание пустого кортежа:**\n\n* С помощью круглых скобок\n\n  ```python\n  empty: tuple = ()\n  print(empty)  # ()\n  ```\n\n* С использованием функции `tuple()`\n\n  ```python\n  empty_2: tuple = tuple()\n  print(empty_2)  # ()\n  ```\n\n**Создание кортежа с данными:**\n\n* С помощью круглых скобок\n\n  ```python\n  nums: tuple[int | float, ...] = (3.1415, 12, 25, 2.78, 0.000343)\n  ```\n\n* С использованием функции `tuple()`\n\n  ```python\n  chars: tuple[str, ...] = tuple(\"abc\")\n  print(chars)  # ('a', 'b', 'c')\n  ```\n\n  ```python\n  odds: tuple[int, ...] = tuple(range(1, 18, 2))  # кортеж нечетных чисел от 1 до 17 включительно\n  print(odds)  # (1, 3, 5, 7, 9, 11, 13, 15, 17)\n  ```\n\n  ```python\n  nums: list[int] = [0, -3, 10, 1, 1, -9, 10, -14, -13, 12]\n  \n  nums.sort()\n  print(nums, type(nums))  # [-14, -13, -9, -3, 0, 1, 1, 10, 10, 12] \u003cclass 'list'\u003e\n  \n  sorted_nums: tuple[int, ...] = tuple(nums)\n  print(sorted_nums, type(sorted_nums))  # (-14, -13, -9, -3, 0, 1, 1, 10, 10, 12) \u003cclass 'tuple'\u003e\n  ```\n\n* Создание кортежа с одним элементом\n\nЕсли необходимо создать кортеж, состоящий из одного элемента, важно не забыть про запятую,\nиначе это будет вовсе не кортеж, а просто скобки вокруг значения.\n\n```python\nits_wrong: str = (\"Write once, run anywhere\")\nprint(its_wrong, type(its_wrong))  # Write once, run anywhere \u003cclass 'str'\u003e\n\nits_right: tuple[str] = (\"The Zen of Python\",)\nprint(its_right, type(its_right))  # ('The Zen of Python',) \u003cclass 'tuple'\u003e\n```\n\n* Обращение к элементам кортежа\n\nОбращение к элементам кортежа работает точно так же, как и со списками:\n\n- по индексу\n- с использованием срезов\n\n```python\nfruits: tuple[str, ...] = (\"яблоко\", \"банан\", \"груша\", \"айва\", \"абрикос\",)\n\nprint(fruits[2])  # груша\nprint(fruits[-2])  # айва\nprint(fruits[1:5])  # ('банан', 'груша', 'айва', 'абрикос')\n\nfruits[3] = \"киви\"  # TypeError: 'tuple' object does not support item assignment\n```\n\nКак видно, в плане обращения к элементам кортеж ничем не отличается от списка. Основное отличие проявляется,\nкогда мы хотим что-то изменить, и тут кортеж говорит строгое **\"нельзя\"**.\n\n---\n\n## В чем отличие от списков\n\nНа первый взгляд, кортежи и списки выглядят очень похоже и там, и там можно хранить набор значений,\nобращаться к элементам по индексу, использовать срезы и итерироваться в цикле.\nНо ключевое различие между ними - **изменяемость**.\n\n- **Список** - изменяемый (`mutable`) - Можно добавлять, удалять, заменять элементы.\n- **Кортеж** - неизменяемый (`immutable`) - После создания ничего менять нельзя.\n\n**Почему это важно?**\n\nИногда важно защитить данные от изменений - например, если у нас есть список месяцев, который в принципе меняться\nне должен. Вот тут кортеж то, что нужно.\n\n```python\nmonths_unsafe: list[str] = [\n    \"Январь\", \"Февраль\", \"Март\", \"Апрель\", \"Май\", \"Июнь\",\n    \"Июль\", \"Август\", \"Сентябрь\", \"Октябрь\", \"Ноябрь\", \"Декабрь\"\n]\n\nmonths_safe: tuple[str, ...] = (\n    \"Январь\", \"Февраль\", \"Март\", \"Апрель\", \"Май\", \"Июнь\",\n    \"Июль\", \"Август\", \"Сентябрь\", \"Октябрь\", \"Ноябрь\", \"Декабрь\"\n)\n\nmonths_unsafe[2] = \"Март 03\"\nprint(\n    months_unsafe)  # ['Январь', 'Февраль', 'Март 03', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']\n\nmonths_safe[4] = \"Май 05\"  # TypeError: 'tuple' object does not support item assignment\nprint(months_safe)\n```\n\n**Сравнение по памяти**\n\nЧтобы узнать, сколько памяти занимает список и кортеж, можно использовать атрибут `.__sizeof__()` или\nчерез `sys.getsizeof()`:\n\n```python\nimport sys\n\nprint(sys.getsizeof(months_unsafe))  # 152\nprint(sys.getsizeof(months_safe))  # 136\n\nprint(months_unsafe.__sizeof__())  # 136\nprint(months_safe.__sizeof__())  # 120\n```\n\n\u003e - `__sizeof__()` показывает **чистый размер объекта в байтах**, без учёта вспомогательной информации интерпретатора.\n\u003e - `sys.getsizeof()` - обёртка над `__sizeof__()`, которая добавляет размер **служебной структуры Python**,\n    \u003e обычно +16 байт. Поэтому `getsizeof()` почти всегда немного больше, чем `__sizeof__()`.\n\u003e - На практике лучше использовать `sys.getsizeof()`, особенно если нужно понять, сколько в итоге памяти всё это съест.\n\nЗакономерность одна, **кортежи почти всегда занимают меньше памяти**, чем списки, это происходит из-за того,\nчто у них меньше \"служебных\" возможностей.\n\n**Сравнение по скорости**\n\nИз-за своей неизменяемости кортежи обрабатываются быстрее, особенно при передаче в функции и при создании.  \nМожем убедиться в этом, сравнив время выполнения:\n\n```python\nimport timeit\n\nprint(\n    \"Список:\", timeit.timeit(\"['Январь', 'Февраль', 'Март']\", number=1_000_000), \"мс\"\n)  # Список: 0.03748495801119134 мс\n\nprint(\n    \"Кортеж:\", timeit.timeit(\"('Январь', 'Февраль', 'Март')\", number=1_000_000), \"мс\"\n)  # Кортеж: 0.005023542005801573 мс\n```\n\nПрактика показывает, что кортеж создаётся быстрее. Опять же, причина - внутренняя оптимизация под \"неизменяемость\".\n\n### Таблица 11.1: Сравнение списков и кортежей\n\n| Критерий                                    | Список                  | Кортеж                   |\n|---------------------------------------------|-------------------------|--------------------------|\n| Изменяемость                                | Да                      | Нет                      |\n| Скорость создания                           | Медленнее               | Быстрее                  |\n| Используемая память                         | Больше                  | Меньше                   |\n| Методы: `append`, `pop`, `remove`, `insert` | Есть                    | Нет                      |\n| Подходит для:                               | Часто меняющихся данных | Статичных наборов данных |\n\nЕсли вам нужно **много разных операций с данными**, в том числе изменяющих структуру, лучше использовать список.\n\n---\n\n## Какие операции и методы можно применять к кортежам\n\nМы уже видели, что кортежи поддерживают такие же действия, как и списки индексацию и срезы.\nТеперь рассмотрим примеры итерации в цикле, проверки принадлежности элементов и т.д. Но самое главное никаких методов,\nкоторые изменяют содержимое.\n\n**Операции над кортежами**\n\nХотя кортежи и **неизменяемы**, с ними всё же можно выполнять ряд операций. Эти операции не изменяют кортеж,\nа создают новый объект.\n\n- Создание с повторением\n\n```python\nnums: tuple[int, ...] = (8,) * 5\nprint(nums)  # (8, 8, 8, 8, 8)\n```\n\n- Сложение (объединение) кортежей\n\nМожно \"сложить\" два кортежа - результатом будет новый кортеж, содержащий элементы обоих.\n\n```python\nfruits: tuple[str, ...] = (\"яблоко\", \"банан\", \"груша\", \"айва\", \"абрикос\",)\nberries: tuple[str, ...] = (\"клубника\", \"ежевика\", \"малина\", \"виноград\", \"арбуз\")\n\nprint(id(fruits), id(berries))  # 4368944400 4368946880\n\nmix: tuple[str, ...] = fruits + berries\nprint(mix)\n# ('яблоко', 'банан', 'груша', 'айва', 'абрикос', 'клубника', 'ежевика', 'малина', 'виноград', 'арбуз')\nprint(id(mix))  # 4368409536\n```\n\n- Операторы `in` и `not in`\n\nКортежи поддерживают проверку на наличие элемента.\n\n```python\nfruits: tuple[str, ...] = (\"яблоко\", \"банан\", \"груша\", \"айва\", \"абрикос\",)\n\nprint(\"банан\" in fruits)  # True\nprint(\"ананас\" not in fruits)  # True\n```\n\n- Итерирование в цикле\n\nМожно перебирать кортеж в `for`, как и список.\n\n```python\nfruits: tuple[str, ...] = (\"яблоко\", \"банан\", \"груша\", \"айва\", \"абрикос\",)\n\nfor fruit in fruits:\n    print(fruit)\n```\n\n**Методы кортежей**\n\nУ кортежей всего два метода, они не меняют кортеж, а просто возвращают информацию.\n\n`tuple.count(x)` - считает, сколько раз элемент `x` встречается в кортеже.\n\n```python\nnames: tuple[str, ...] = (\"Алиса\", \"Петя\", \"Вася\", \"Алиса\", \"Толя\", \"Ваня\", \"Алиса\", \"Саша\", \"Миша\")\n\nprint(names.count(\"Алиса\"))  # 3\n```\n\n---\n\n`tuple.index(x)` - возвращает индекс первого вхождения элемента `x`.\n\n```python\nnames: tuple[str, ...] = (\"Алиса\", \"Петя\", \"Вася\", \"Алиса\", \"Толя\", \"Ваня\", \"Алиса\", \"Саша\", \"Миша\")\n\nprint(names.index(\"Алиса\"))  # 0\nprint(names.index(\"Толя\"))  # 4\n```\n\nЕсли элемент не найден - будет ошибка `ValueError`:\n\n```python\nnames: tuple[str, ...] = (\"Алиса\", \"Петя\", \"Вася\", \"Алиса\", \"Толя\", \"Ваня\", \"Алиса\", \"Саша\", \"Миша\")\n\nprint(names.index(\"Макс\"))  # ValueError: tuple.index(x): x not in tuple\n```\n\n### Таблица 11.2: Основные операции и методы кортежей\n\n| Операция / Метод | Описание                                      |\n|------------------|-----------------------------------------------|\n| `in`, `not in`   | Проверка наличия элемента в кортеже           |\n| `for x in tuple` | Перебор элементов                             |\n| `tuple + tuple`  | Сложение кортежей                             |\n| `tuple * n`      | Повторение кортежа `n` раз                    |\n| `tuple.count(x)` | Сколько раз элемент `x` встречается в кортеже |\n| `tuple.index(x)` | Индекс первого вхождения элемента `x`         |\n\nЕсли вы работаете с постоянным набором данных, к которому не нужно вносить изменения - кортеж подойдёт идеально.\n\n---\n\n## Где они реально применяются\n\nКортежи часто встречаются в реальных проектах, особенно там, где важно, чтобы данные не менялись.\nВот несколько типичных случаев:\n\n- Функции, возвращающие несколько значений\n\nЕсли функция возвращает сразу несколько результатов, их удобно упаковать в кортеж:\n\n```python\ndef get_user() -\u003e tuple[str, int]:\n    return \"Анна\", 30\n\n\nres: tuple[str, int] = get_user()\nprint(res)  # ('Анна', 30)\n```\n\n- Координаты, размеры, цвета\n\nЧасто используются в графике, играх, математике:\n\n```python\npoint: tuple[int, int] = (10, 20)\nsize: tuple[int, int] = (1920, 1080)\ncolor: tuple[int, int, int] = (255, 255, 0)\n```\n\n- Хешируемые ключи в словарях\n\nКортеж - неизменяемый, а значит его можно использовать как ключ в `dict`:\n\n```python\nphone_book: dict[tuple[str, str], str] = {\n    (\"Иванов\", \"Иван\"): \"8-900-123-45-67\",\n    (\"Петрова\", \"Мария\"): \"8-908-765-43-21\",\n}\n\nprint(phone_book[(\"Петрова\", \"Мария\")])  # 8-908-765-43-21\n```\n\n\u003e На данный момент эти примеры могут показаться сложными, пожалуйста, наберитесь терпения, мы скоро рассмотрим эти темы.\n\n---\n\n## Аннотации типов\n\nЕсли вы ещё не используете аннотации типов, то **используйте** - это хорошая практика.\n\n- Подход до Python 3.9\n\nДля более старых версий Python необходимо импортировать `Tuple` из модуля `typing`:\n\n```python\nfrom typing import Tuple\n\nperson: Tuple[str, int] = (\"Алиса\", 30)\n```\n\nЭто говорит о том, что кортеж содержит два значения: \"строка\" и \"целое число\".\n\n- Подход начиная с Python 3.9\n\nНачиная с версии **3.9** можно использовать встроенный синтаксис, без импорта:\n\n```python\nperson: tuple[str, int] = (\"Алиса\", 30)\n```\n\nЭто то же самое, что было в примере выше.\n\n- Что означает `...` в аннотациях?\n\nЕсли вы работаете с кортежем произвольной длины, где все элементы одного типа, можно использовать многоточие `...`:\n\n```python\nnumbers: tuple[int, ...] = (1, 2, 3, 4, 5, 6)\n```\n\n\u003e `tuple[int, ...]` означает: \"кортеж, содержащий только `int`, любой длины\".\n\n```python\ncoords_2d: tuple[float, float] = (12.5, -3.8)  # длина кортежа постоянная - 2.\n\nnames: tuple[str, ...] = (\"Алиса\", \"Боб\", \"Чарли\")  # кортеж строк произвольной длины.\n\nusers: list[tuple[str, int]] = [\n    (\"Вася\", 25),\n    (\"Петя\", 31),\n    (\"Саша\", 18),\n]  # список кортежей из двух элементов (строка, целое число) произвольной длины.\n```\n\nТакой подход полезен и для читаемости, и при работе со статическими анализаторами кода\n[mypy](https://pypi.org/project/mypy/), [pyright](https://pypi.org/project/pyright/), и т.д.\n\n---\n\n## Заключение\n\nМы рассмотрели кортежи достаточно подробно, но это ещё не всё. Всегда найдётся что-то новенькое.  \nНапример, мы не стали подробно останавливаться на **распаковке значений**, **вложенных структурах**  \nи особенностях работы с изменяемыми объектами внутри кортежей. Всё лучшее приходит с опытом - тема за темой  \nвы будете получать всё больше знаний о структурах. Ниже несколько примеров для размышления.\n\n**Распаковка значений**:\n\n```python\nfirst_name, last_name, age = \"Иван\", \"Иванов\", 25  # да, это кортеж - круглые скобки не обязательны,\n# но всё же, лучше указывать их явно: \"Явное лучше, чем неявное\"\n\nprint(first_name)  # Иван\nprint(last_name)  # Иванов\nprint(age)  # 25\n```\n\n**Вложенные структуры**:\n\n- Кортеж кортежей:\n\n```python\nusers: tuple[tuple[str, int], ...] = (\n    (\"Вася\", 25),\n    (\"Петя\", 31),\n    (\"Саша\", 18),\n)\n```\n\n- Кортеж списков\n\n\u003e Важно!: сам кортеж изменить нельзя, но если он содержит **изменяемые объекты**, например, список,\n\u003e то такие вложенные элементы - изменяемы.\n\n```python\nusers: tuple[list[str | int], ...] = (\n    [\"Вася\", 25],\n    [\"Петя\", 31],\n    [\"Саша\", 18],\n)\n\nusers[1][0] = \"Толя\"\n\nprint(users)  # (['Вася', 25], ['Толя', 31], ['Саша', 18])\n\n```\n\nЭтих знаний уже достаточно, чтобы эффективно применять кортежи в своём коде. Дальше будет только практика.\nВ следующей теме мы научимся работать со множествами.\n\n---\n\n## [Задания](./tasks/TASKS.md)\n\nПрактические задания для самостоятельной работы.\n\n- Постарайтесь использовать только те знания, которые были изучены в пройденных темах.\n  Не стоит использовать конструкции, которые мы ещё не разбирали.\n- Убедитесь, что удалили все лишние комментарии из вашего кода.\n- Постарайтесь указывать аннотации типов для переменных.\n- Рекомендуем решить все задания самостоятельно.\n\nЕсли возникнут трудности, не стесняйтесь обратиться за помощью в наш [Телеграм-чат](https://t.me/shox_py_discuss).\nЧтобы перейти к заданиям, кликните на заголовок **Задания** или нажмите [сюда](./tasks/TASKS.md).\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyuldashov10%2Ftopic_11","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyuldashov10%2Ftopic_11","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyuldashov10%2Ftopic_11/lists"}