Домашнє завдання

Кортежі й розпакування

Три рівні. Кожен наступний спирається на попередній, але братися можна за будь-який. Пиши в окремому файлі homework_07.py або в новому зошиті — головне, щоб код запускався й друкував результат.

Дозволено з бібліотек: math, collections. Більше нічого не треба. Циклів, умов і власних функцій тема ще не проходила — але якщо ти вже знаєш їх з іншого джерела, користуйся спокійно: перевіряється результат, а не засоби.

Вихідні дані для всіх рівнів:

міста = (
    ("Київ",   50.45, 30.52),
    ("Львів",  49.84, 24.03),
    ("Одеса",  46.48, 30.73),
    ("Харків", 49.99, 36.23),
)

рівень 1Рівень 1 — База

1.1 · Кома, яка все ламає

Напиши чотири рядки, які створюють: число, кортеж з одного елемента, той самий кортеж без дужок і порожній кортеж. Надрукуй для кожного значення його type(...).__name__ і len(...) там, де довжина існує.

Зроблено, якщо: у виводі видно, що (7) — це int, а (7,) і 7, — однакові кортежі довжиною 1, і виконуються перевірки

assert type(без_коми) is int
assert з_комою == без_дужок and len(з_комою) == 1
assert len(порожній) == 0 and type(порожній) is tuple

1.2 · Розпакування запису міста

Візьми перший запис із міста і розпакуй його трьома різними шаблонами:

  1. на три окремі імена назва, широта, довгота;
  2. на назву й решту через зірочку: назва, *координати;
  3. вкладено, попередньо перепакувавши запис у форму ("Київ", (50.45, 30.52)).

Надрукуй результат кожного шаблону.

Зроблено, якщо: усі три шаблони дають ті самі числа, і це доведено:

assert (широта, довгота) == tuple(координати) == пара_з_вкладеного
assert isinstance(координати, list), "зірочка дає СПИСОК, а не кортеж"

1.3 · Спіймай обидві помилки

Спробуй змінити елемент кортежа й спробуй дописати в нього елемент. Обидві спроби мають впасти. Випиши словами (коментарем у коді), чим TypeError у першому випадку відрізняється від AttributeError у другому.

Зроблено, якщо: у виводі є обидва повні тексти помилок і твоє пояснення різниці між «операція заборонена для цього типу» і «такого методу не існує».


рівень 2Рівень 2 — Плюс

2.1 · Найпівнічніше місто без сортування на місці

Знайди місто з найбільшою широтою. Кортеж міста змінювати не можна — і не вийде: у нього немає sort(). Скористайся sorted(...), який повертає новий список, або max(...) з key.

Надрукуй назву міста-переможця та його координати парою.

Зроблено, якщо: твій код друкує Київ із парою (50.45, 30.52), а вихідний кортеж лишився недоторканим:

assert міста[0][0] == "Київ" and len(міста) == 4
assert найпівнічніше[0] == "Київ"

Додатково поясни двома реченнями, чому sorted(міста) спрацює, а міста.sort() — ні.

2.2 · Довжина замкненого маршруту

Порахуй довжину маршруту Київ → Львів → Одеса → Харків → Київ. Відстань між двома точками бери як math.dist(точка_1, точка_2), а самі точки діставай із записів розпакуванням, а не індексами (_, широта, довгота = запис).

Зроблено, якщо: результат збігається з ручним підсумовуванням чотирьох відрізків і виконується нерівність трикутника — замкнений маршрут не коротший за подвоєну пряму між найдальшою парою міст:

assert math.isclose(довжина, відрізок_1 + відрізок_2 + відрізок_3 + відрізок_4)
assert довжина >= 2 * max_пряма

2.3 · Пастка незмінності

Створи кортеж спостереження = ("Київ", [12.0, 13.5, 11.2]), де список — це виміри температури. Зроби так, щоб:

Зроблено, якщо: у виводі видно всі три результати, і ти показав числом, що адреса списку не змінилась:

адреса_до = id(спостереження[1])
спостереження[1].append(14.1)
assert id(спостереження[1]) == адреса_до, "слот кортежа мав лишитись тим самим"
assert спостереження[1] == [12.0, 13.5, 11.2, 14.1]

І поясни двома реченнями, чому hash падає саме через це.


рівень 3Рівень 3 — Виклик

3.1 · Симетричний ключ-пара

Побудуй словник відстаней, у якого ключ — пара міст, а значення — відстань між ними. Проблема: ("Київ", "Львів") і ("Львів", "Київ") — це два різні ключі, бо кортежі порівнюються поелементно, зліва направо.

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

Зроблено, якщо: у словнику рівно 6 записів, і виконується

assert len(відстані) == 6
assert ключ("Київ", "Львів") == ключ("Львів", "Київ")
assert math.isclose(відстані[ключ("Львів", "Київ")], math.dist(київ, львів))

Підказка вже була в практиці: tuple(sorted(...)). Подумай, чому саме tuple(...), а не просто sorted(...).

3.2 · Свій namedtuple на голому кортежі

Не використовуючи collections.namedtuple, зроби «іменований доступ» до записів: словник поля = {"назва": 0, "широта": 1, "довгота": 2} і функцію поле(запис, імʼя), яка повертає потрібний елемент.

Потім зроби те саме через справжній namedtuple і порівняй.

Зроблено, якщо: обидва підходи дають однакові значення на всіх чотирьох містах, і ти сформулював двома-трьома реченнями, що саме дає namedtuple понад твій словник полів:

Місто = namedtuple("Місто", "назва широта довгота")
київ_іменований = Місто(*міста[0])          # розпакування кортежа в аргументи

assert поле(міста[0], "широта") == київ_іменований.широта
assert isinstance(київ_іменований, tuple)
assert київ_іменований == міста[0]

3.3 · Де кортеж перестає бути надійним

Досліди межу гарантії незмінності. Побудуй два кортежі однакової форми: один із самих лише незмінних елементів, другий — зі списком усередині. Для кожного перевір три речі: чи береться hash, чи можна покласти в множину {...} і чи змінюється вміст після дії над внутрішнім обʼєктом.

Зроблено, якщо: результати зведені в таблицю (можна просто трьома print-ами по рядку), усі три перевірки для другого кортежа дають «ні / ні / так», і сформульоване правило одним реченням: за яких умов кортеж справді гарантує незмінність даних, а не лише незмінність набору посилань.


Підказки