https://github.com/yuldashov10/topic_11
11. Работа с кортежами
https://github.com/yuldashov10/topic_11
learning-python programming python python3 shox-py
Last synced: 7 months ago
JSON representation
11. Работа с кортежами
- Host: GitHub
- URL: https://github.com/yuldashov10/topic_11
- Owner: yuldashov10
- License: mit
- Created: 2025-07-20T18:45:18.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-07-20T19:15:30.000Z (8 months ago)
- Last Synced: 2025-07-20T21:07:37.308Z (8 months ago)
- Topics: learning-python, programming, python, python3, shox-py
- Language: Python
- Homepage:
- Size: 308 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# 11. Работа с кортежами

---
## Вступление
Добро пожаловать в тему кортежей!
Мы уже вскользь касались кортежей в пятой теме, теперь пришло время разобраться в них подробнее.
В этой теме разберём, чем кортежи отличаются от списков, как с ними работать, и зачем вообще они нужны.
---
## Содержание
- [Что такое кортежи](#что-такое-кортежи)
- [Как создавать и обращаться к кортежам](#как-создавать-и-обращаться-к-кортежам)
- [В чем отличие от списков](#в-чем-отличие-от-списков)
- [Какие операции и методы можно применять к кортежам](#какие-операции-и-методы-можно-применять-к-кортежам)
- [Где они реально применяются](#где-они-реально-применяются)
- [Аннотации типов](#аннотации-типов)
- [Задания](#задания)
---
## Что такое кортежи
В прошлой теме мы рассматривали списки, уже знаем, что с ними можно делать:
- _читать, изменять, добавлять, удалять и заменять элементы_.
С кортежами всё строже, они **неизменяемы**. Это значит, что после создания кортежа
мы не можем изменить его содержимое:
- _ни добавить, ни удалить, ни заменить элемент_.
То есть операции, которые были привычны для списков, вроде `.remove()`, `.insert()`, `.pop()`, `.append()`,
в случае с кортежами просто не работают. У кортежей этих методов нет вовсе.
Однако это не делает кортежи бесполезными, напротив, из-за _неизменяемости_ у них появляются сильные стороны,
о которых поговорим дальше.
---
## Как создавать и обращаться к кортежам
Кортежи можно создавать несколькими способами, с помощью круглых скобок или функции `tuple()`.
Также нужно помнить про особенности создания пустого кортежа и кортежа с одним элементом.
Рассмотрим все эти варианты на примерах.
**Создание пустого кортежа:**
* С помощью круглых скобок
```python
empty: tuple = ()
print(empty) # ()
```
* С использованием функции `tuple()`
```python
empty_2: tuple = tuple()
print(empty_2) # ()
```
**Создание кортежа с данными:**
* С помощью круглых скобок
```python
nums: tuple[int | float, ...] = (3.1415, 12, 25, 2.78, 0.000343)
```
* С использованием функции `tuple()`
```python
chars: tuple[str, ...] = tuple("abc")
print(chars) # ('a', 'b', 'c')
```
```python
odds: tuple[int, ...] = tuple(range(1, 18, 2)) # кортеж нечетных чисел от 1 до 17 включительно
print(odds) # (1, 3, 5, 7, 9, 11, 13, 15, 17)
```
```python
nums: list[int] = [0, -3, 10, 1, 1, -9, 10, -14, -13, 12]
nums.sort()
print(nums, type(nums)) # [-14, -13, -9, -3, 0, 1, 1, 10, 10, 12]
sorted_nums: tuple[int, ...] = tuple(nums)
print(sorted_nums, type(sorted_nums)) # (-14, -13, -9, -3, 0, 1, 1, 10, 10, 12)
```
* Создание кортежа с одним элементом
Если необходимо создать кортеж, состоящий из одного элемента, важно не забыть про запятую,
иначе это будет вовсе не кортеж, а просто скобки вокруг значения.
```python
its_wrong: str = ("Write once, run anywhere")
print(its_wrong, type(its_wrong)) # Write once, run anywhere
its_right: tuple[str] = ("The Zen of Python",)
print(its_right, type(its_right)) # ('The Zen of Python',)
```
* Обращение к элементам кортежа
Обращение к элементам кортежа работает точно так же, как и со списками:
- по индексу
- с использованием срезов
```python
fruits: tuple[str, ...] = ("яблоко", "банан", "груша", "айва", "абрикос",)
print(fruits[2]) # груша
print(fruits[-2]) # айва
print(fruits[1:5]) # ('банан', 'груша', 'айва', 'абрикос')
fruits[3] = "киви" # TypeError: 'tuple' object does not support item assignment
```
Как видно, в плане обращения к элементам кортеж ничем не отличается от списка. Основное отличие проявляется,
когда мы хотим что-то изменить, и тут кортеж говорит строгое **"нельзя"**.
---
## В чем отличие от списков
На первый взгляд, кортежи и списки выглядят очень похоже и там, и там можно хранить набор значений,
обращаться к элементам по индексу, использовать срезы и итерироваться в цикле.
Но ключевое различие между ними - **изменяемость**.
- **Список** - изменяемый (`mutable`) - Можно добавлять, удалять, заменять элементы.
- **Кортеж** - неизменяемый (`immutable`) - После создания ничего менять нельзя.
**Почему это важно?**
Иногда важно защитить данные от изменений - например, если у нас есть список месяцев, который в принципе меняться
не должен. Вот тут кортеж то, что нужно.
```python
months_unsafe: list[str] = [
"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
"Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
]
months_safe: tuple[str, ...] = (
"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
"Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
)
months_unsafe[2] = "Март 03"
print(
months_unsafe) # ['Январь', 'Февраль', 'Март 03', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
months_safe[4] = "Май 05" # TypeError: 'tuple' object does not support item assignment
print(months_safe)
```
**Сравнение по памяти**
Чтобы узнать, сколько памяти занимает список и кортеж, можно использовать атрибут `.__sizeof__()` или
через `sys.getsizeof()`:
```python
import sys
print(sys.getsizeof(months_unsafe)) # 152
print(sys.getsizeof(months_safe)) # 136
print(months_unsafe.__sizeof__()) # 136
print(months_safe.__sizeof__()) # 120
```
> - `__sizeof__()` показывает **чистый размер объекта в байтах**, без учёта вспомогательной информации интерпретатора.
> - `sys.getsizeof()` - обёртка над `__sizeof__()`, которая добавляет размер **служебной структуры Python**,
> обычно +16 байт. Поэтому `getsizeof()` почти всегда немного больше, чем `__sizeof__()`.
> - На практике лучше использовать `sys.getsizeof()`, особенно если нужно понять, сколько в итоге памяти всё это съест.
Закономерность одна, **кортежи почти всегда занимают меньше памяти**, чем списки, это происходит из-за того,
что у них меньше "служебных" возможностей.
**Сравнение по скорости**
Из-за своей неизменяемости кортежи обрабатываются быстрее, особенно при передаче в функции и при создании.
Можем убедиться в этом, сравнив время выполнения:
```python
import timeit
print(
"Список:", timeit.timeit("['Январь', 'Февраль', 'Март']", number=1_000_000), "мс"
) # Список: 0.03748495801119134 мс
print(
"Кортеж:", timeit.timeit("('Январь', 'Февраль', 'Март')", number=1_000_000), "мс"
) # Кортеж: 0.005023542005801573 мс
```
Практика показывает, что кортеж создаётся быстрее. Опять же, причина - внутренняя оптимизация под "неизменяемость".
### Таблица 11.1: Сравнение списков и кортежей
| Критерий | Список | Кортеж |
|---------------------------------------------|-------------------------|--------------------------|
| Изменяемость | Да | Нет |
| Скорость создания | Медленнее | Быстрее |
| Используемая память | Больше | Меньше |
| Методы: `append`, `pop`, `remove`, `insert` | Есть | Нет |
| Подходит для: | Часто меняющихся данных | Статичных наборов данных |
Если вам нужно **много разных операций с данными**, в том числе изменяющих структуру, лучше использовать список.
---
## Какие операции и методы можно применять к кортежам
Мы уже видели, что кортежи поддерживают такие же действия, как и списки индексацию и срезы.
Теперь рассмотрим примеры итерации в цикле, проверки принадлежности элементов и т.д. Но самое главное никаких методов,
которые изменяют содержимое.
**Операции над кортежами**
Хотя кортежи и **неизменяемы**, с ними всё же можно выполнять ряд операций. Эти операции не изменяют кортеж,
а создают новый объект.
- Создание с повторением
```python
nums: tuple[int, ...] = (8,) * 5
print(nums) # (8, 8, 8, 8, 8)
```
- Сложение (объединение) кортежей
Можно "сложить" два кортежа - результатом будет новый кортеж, содержащий элементы обоих.
```python
fruits: tuple[str, ...] = ("яблоко", "банан", "груша", "айва", "абрикос",)
berries: tuple[str, ...] = ("клубника", "ежевика", "малина", "виноград", "арбуз")
print(id(fruits), id(berries)) # 4368944400 4368946880
mix: tuple[str, ...] = fruits + berries
print(mix)
# ('яблоко', 'банан', 'груша', 'айва', 'абрикос', 'клубника', 'ежевика', 'малина', 'виноград', 'арбуз')
print(id(mix)) # 4368409536
```
- Операторы `in` и `not in`
Кортежи поддерживают проверку на наличие элемента.
```python
fruits: tuple[str, ...] = ("яблоко", "банан", "груша", "айва", "абрикос",)
print("банан" in fruits) # True
print("ананас" not in fruits) # True
```
- Итерирование в цикле
Можно перебирать кортеж в `for`, как и список.
```python
fruits: tuple[str, ...] = ("яблоко", "банан", "груша", "айва", "абрикос",)
for fruit in fruits:
print(fruit)
```
**Методы кортежей**
У кортежей всего два метода, они не меняют кортеж, а просто возвращают информацию.
`tuple.count(x)` - считает, сколько раз элемент `x` встречается в кортеже.
```python
names: tuple[str, ...] = ("Алиса", "Петя", "Вася", "Алиса", "Толя", "Ваня", "Алиса", "Саша", "Миша")
print(names.count("Алиса")) # 3
```
---
`tuple.index(x)` - возвращает индекс первого вхождения элемента `x`.
```python
names: tuple[str, ...] = ("Алиса", "Петя", "Вася", "Алиса", "Толя", "Ваня", "Алиса", "Саша", "Миша")
print(names.index("Алиса")) # 0
print(names.index("Толя")) # 4
```
Если элемент не найден - будет ошибка `ValueError`:
```python
names: tuple[str, ...] = ("Алиса", "Петя", "Вася", "Алиса", "Толя", "Ваня", "Алиса", "Саша", "Миша")
print(names.index("Макс")) # ValueError: tuple.index(x): x not in tuple
```
### Таблица 11.2: Основные операции и методы кортежей
| Операция / Метод | Описание |
|------------------|-----------------------------------------------|
| `in`, `not in` | Проверка наличия элемента в кортеже |
| `for x in tuple` | Перебор элементов |
| `tuple + tuple` | Сложение кортежей |
| `tuple * n` | Повторение кортежа `n` раз |
| `tuple.count(x)` | Сколько раз элемент `x` встречается в кортеже |
| `tuple.index(x)` | Индекс первого вхождения элемента `x` |
Если вы работаете с постоянным набором данных, к которому не нужно вносить изменения - кортеж подойдёт идеально.
---
## Где они реально применяются
Кортежи часто встречаются в реальных проектах, особенно там, где важно, чтобы данные не менялись.
Вот несколько типичных случаев:
- Функции, возвращающие несколько значений
Если функция возвращает сразу несколько результатов, их удобно упаковать в кортеж:
```python
def get_user() -> tuple[str, int]:
return "Анна", 30
res: tuple[str, int] = get_user()
print(res) # ('Анна', 30)
```
- Координаты, размеры, цвета
Часто используются в графике, играх, математике:
```python
point: tuple[int, int] = (10, 20)
size: tuple[int, int] = (1920, 1080)
color: tuple[int, int, int] = (255, 255, 0)
```
- Хешируемые ключи в словарях
Кортеж - неизменяемый, а значит его можно использовать как ключ в `dict`:
```python
phone_book: dict[tuple[str, str], str] = {
("Иванов", "Иван"): "8-900-123-45-67",
("Петрова", "Мария"): "8-908-765-43-21",
}
print(phone_book[("Петрова", "Мария")]) # 8-908-765-43-21
```
> На данный момент эти примеры могут показаться сложными, пожалуйста, наберитесь терпения, мы скоро рассмотрим эти темы.
---
## Аннотации типов
Если вы ещё не используете аннотации типов, то **используйте** - это хорошая практика.
- Подход до Python 3.9
Для более старых версий Python необходимо импортировать `Tuple` из модуля `typing`:
```python
from typing import Tuple
person: Tuple[str, int] = ("Алиса", 30)
```
Это говорит о том, что кортеж содержит два значения: "строка" и "целое число".
- Подход начиная с Python 3.9
Начиная с версии **3.9** можно использовать встроенный синтаксис, без импорта:
```python
person: tuple[str, int] = ("Алиса", 30)
```
Это то же самое, что было в примере выше.
- Что означает `...` в аннотациях?
Если вы работаете с кортежем произвольной длины, где все элементы одного типа, можно использовать многоточие `...`:
```python
numbers: tuple[int, ...] = (1, 2, 3, 4, 5, 6)
```
> `tuple[int, ...]` означает: "кортеж, содержащий только `int`, любой длины".
```python
coords_2d: tuple[float, float] = (12.5, -3.8) # длина кортежа постоянная - 2.
names: tuple[str, ...] = ("Алиса", "Боб", "Чарли") # кортеж строк произвольной длины.
users: list[tuple[str, int]] = [
("Вася", 25),
("Петя", 31),
("Саша", 18),
] # список кортежей из двух элементов (строка, целое число) произвольной длины.
```
Такой подход полезен и для читаемости, и при работе со статическими анализаторами кода
[mypy](https://pypi.org/project/mypy/), [pyright](https://pypi.org/project/pyright/), и т.д.
---
## Заключение
Мы рассмотрели кортежи достаточно подробно, но это ещё не всё. Всегда найдётся что-то новенькое.
Например, мы не стали подробно останавливаться на **распаковке значений**, **вложенных структурах**
и особенностях работы с изменяемыми объектами внутри кортежей. Всё лучшее приходит с опытом - тема за темой
вы будете получать всё больше знаний о структурах. Ниже несколько примеров для размышления.
**Распаковка значений**:
```python
first_name, last_name, age = "Иван", "Иванов", 25 # да, это кортеж - круглые скобки не обязательны,
# но всё же, лучше указывать их явно: "Явное лучше, чем неявное"
print(first_name) # Иван
print(last_name) # Иванов
print(age) # 25
```
**Вложенные структуры**:
- Кортеж кортежей:
```python
users: tuple[tuple[str, int], ...] = (
("Вася", 25),
("Петя", 31),
("Саша", 18),
)
```
- Кортеж списков
> Важно!: сам кортеж изменить нельзя, но если он содержит **изменяемые объекты**, например, список,
> то такие вложенные элементы - изменяемы.
```python
users: tuple[list[str | int], ...] = (
["Вася", 25],
["Петя", 31],
["Саша", 18],
)
users[1][0] = "Толя"
print(users) # (['Вася', 25], ['Толя', 31], ['Саша', 18])
```
Этих знаний уже достаточно, чтобы эффективно применять кортежи в своём коде. Дальше будет только практика.
В следующей теме мы научимся работать со множествами.
---
## [Задания](./tasks/TASKS.md)
Практические задания для самостоятельной работы.
- Постарайтесь использовать только те знания, которые были изучены в пройденных темах.
Не стоит использовать конструкции, которые мы ещё не разбирали.
- Убедитесь, что удалили все лишние комментарии из вашего кода.
- Постарайтесь указывать аннотации типов для переменных.
- Рекомендуем решить все задания самостоятельно.
Если возникнут трудности, не стесняйтесь обратиться за помощью в наш [Телеграм-чат](https://t.me/shox_py_discuss).
Чтобы перейти к заданиям, кликните на заголовок **Задания** или нажмите [сюда](./tasks/TASKS.md).
---