Введение в программирование на Python. Занятие 2.

Контейнерные типы данных. Списки, кортежи, множества, словари и операции с ними

0. Концепция массива

Массив (array) — структура данных, хранящая набор значений (элементов массива), идентифицируемых по индексу или набору индексов, принимающих целые значения из некоторого заданного непрерывного диапазона.

https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%81%D1%81%D0%B8%D0%B2_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)

Размерность массива — это количество индексов, необходимое для однозначной адресации элемента в рамках массива. По количеству используемых индексов массивы делятся на одномерные, двумерные, трёхмерные и т. д. Аналогия из математики: одномерный массив - вектор, двумерный - матрица, трёх- и более -мерный — многомерная матрица или тензор.

Форма или структура массива — сведения о количестве размерностей и размере (протяжённости) массива по каждой из размерностей.

Пример одномерного массива. Image

Картинка с сайта https://www.yaklass.ru/p/informatika/9-klass/algoritmizatciia-i-programmirovanie-14692/odnomernye-massivy-tcelykh-chisel-14603/re-84b191be-cf7c-4763-abc2-9d338b6b5198.

Динамическими называются массивы, размер которых может изменяться во время выполнения программы. Нединамические массивы называют ещё фиксированными или статическими.

Гетерогенным называется массив, в разные элементы которого могут быть записаны значения, относящиеся к различным типам данных. Гетерогенные массивы удобны как универсальная структура для хранения наборов данных произвольных типов. Реализация гетерогенности требует усложнения механизма поддержки массивов в трансляторе языка.

Стандартная библиотека Python имеет контейнерные типы, схожие с массивом, — это список и кортеж. Список — гетерогенный динамический массив, а кортеж — гетерогенный статический массив.

Достоинства
  • лёгкость доступа к элементу по его индексу (поскольку элементы массива располагаются один за другим)
  • одинаковое время доступа ко всем элементам
Недостатки
  • для статического массива — отсутствие динамики, невозможность удаления или добавления элемента без сдвига других
  • для динамического и/или гетерогенного массива — более низкое (по сравнению со статическим) быстродействие и дополнительные накладные расходы на поддержку динамических свойств и гетерогенности
  • при отсутствии дополнительных средств контроля — угроза выхода за границы массива и повреждения данных.

1. Список / List

1.1. Создание списков и печать

Составные (контейнерные) типы данных используются для группировки значений вместе. Наиболее гибкий из них — список (list). Список задаётся через разделённые запятыми значения (элементы), заключённые в квадратные скобки. Элементы списка могут быть разных типов.

Список — ближайшая аналогия массива в Python.

In [2]:
# Create list by listing elements
# Создаём список, перечисляя элементы в нём
my_list = ['milk', 'sugar', 'kefir', 'butter']
print(my_list)

# Print some useful information about the list: len(), type()
# Печать полезной информации о списке: длина len(), тип данных type()
print('Length of my_list is ' + str(len(my_list)))
print('Type of list object is ' + str(type(my_list)))

# 2 ways of creating empty list
# 2 способа создания пустого списка
my_other_list = []
my_third_list = list()

# A list may contain objects of different types
# В списке могут содержаться объекты разных типов
my_many_types_list = ['word', 1, 'Python', 5]
print(my_many_types_list)

# A list may contain even other lists in it
# Список может быть составлен из других списков
list_of_2_lists = ['other_word', my_list, my_many_types_list]
print(list_of_2_lists)
['milk', 'sugar', 'kefir', 'butter']
Length of my_list is 4
Type of list object is <class 'list'>
['word', 1, 'Python', 5]
['other_word', ['milk', 'sugar', 'kefir', 'butter'], ['word', 1, 'Python', 5]]

1.2. Обращение к элементам списка

Индексы списков начинаются с нуля, как и индексы строк. Самая частая операция при работе со списками - обращение к элементу по индексу. Похожая операция - обращение к группе соседних элементов - срез списка.

Отрицательные индексы в Python удобны для обращения к элементам с конца.

Image

https://qph.fs.quoracdn.net/main-qimg-a380b1bc159589df5e0b9842e5b56b6d

In [3]:
# Accessing list element by index
# Обращение к элементу списка по индексу
print(my_list[0])
print(my_list[2])
print(my_list[-1])

# Slicing the list: get elements of my_list from 1 to 3, excluding 3d
# Обращение к части списка - срезка списка. Здесь элементы от первого до третьего, исключая третий
print(my_list[1:3])

# New list is created from elements 0, 1 of original list and appending another list
# Создание нового списка из части старого и присоединения нового
new_list = my_list[:2] + ['cheese', 'sugar']
print(new_list)
milk
kefir
butter
['sugar', 'kefir']
['milk', 'sugar', 'cheese', 'sugar']

1.3. Изменение элементов и всего списка

In [4]:
# Appending string to existing list element and updating the element with index 0
# Присоединение строки к элементу списка c индексом 2 (тоже строке)
# Обновление элемента с индексом 0
my_list[2] = my_list[2] + '_activia'
my_list[0] = 'cottage_cheese'
print(my_list)

# Updating part of list by assigning new values to list slice
# Можно обновить список, присвоив его срезу другой список
my_list[0:2] = ['salted_cheese', 'icecream']
print(my_list)

# Deleting part of list by assigning empty list to list slice
# Можно удалить часть списка, присвоив срезу пустой список
my_list[2:4] = []
print(my_list)

# Deleting all list by assigning empty list
# Присвоение полному срезу списка пустого списка очищает его
my_list[:] = []
print(my_list)
['cottage_cheese', 'sugar', 'kefir_activia', 'butter']
['salted_cheese', 'icecream', 'kefir_activia', 'butter']
['salted_cheese', 'icecream']
[]

1.2. Добавление элементов в список, удаление элементов

In [6]:
# Adding elements to list end
# Добавление элемента в конец списка
my_third_list.append(3)
my_third_list.append(4)
print(my_third_list)

# pop() always removes last element of the list 
# pop() возвращает и удаляет последний элемент списка
elem = my_third_list.pop()
print(elem)
print(my_third_list)

# Remove element by index
# Можно удалять через pop() по индексу элемента
# Есть и другие операции по добавлению и удалению элементов списка, но они менее эффективны.
# my_third_list.pop(2)
print(my_third_list)
[3, 3, 4]
4
[3, 3]
[3, 3]

1.3. Итерирование по списку

In [13]:
print("To Buy:")

# Simple iterating over list (does not use or imply indexes)
# Итерирование по списку (не использует индексы)
for item in new_list:
    print('- ' + item)

print()

# Loop over a list while keeping track of indexes with enumerate()
# Встроенная функция enumerate() позволяет следить за индексами при итерировании
for num, item in enumerate(new_list):
    print(str(num)  + '. ' + item)
    
print()

# Loop over a list with index in some range
# Цикл по списку с внешним для списка индексом из некоторого промежутка range()
for index in range(1, 3):
    print('list[' + str(index) + '] = ' + new_list[index])
To Buy:
- milk
- sugar
- cheese
- sugar

0. milk
1. sugar
2. cheese
3. sugar

list[1] = sugar
list[2] = cheese
In [14]:
# Example from data analysis: calculating mean with iteration over list
# Пример из анализа данных: подсчёт среднего возраста в списке
titanic_survived_ages = [16, 20, 3, 5, 11, 66, 40, 32, 38, 22, 10]
mean = 0
count = len(titanic_survived_ages)

for age in titanic_survived_ages:
    mean = mean + age
    
mean = mean / count
print('Average age of survived on Titanic: %.1f' % mean)
Average age of survived on Titanic: 23.9

1.4. Другие методы списка

insert(), remove(), extend(), index(), reverse(), copy(), sort(), any(), all(), sum()...

Можно изучить подробнее здесь: https://www.programiz.com/python-programming/methods/list

1.5. Практика

In [16]:
# Make this a human-readable list starting with 1.
# Напечатать список покупок, чтобы номера начинались с 1.
print("To Buy:")
for num, item in enumerate(new_list):
    print(str(num)  + '. ' + item)
To Buy:
0. milk
1. sugar
2. cheese
3. sugar
In [7]:
# Solution / решение: num + 1
# Make this a human-readable list starting with 1.
# Напечатать список покупок, чтобы номера начинались с 1.
print("To Buy:")
for num, item in enumerate(new_list):
    print(str(num + 1)  + '. ' + item)
To Buy:
1. milk
2. sugar
3. cheese
4. sugar
In [10]:
# Calculate how many persons survived 's' = survived, 'd' = died
# Посчитать, сколько людей выжило, по списку. Здесь 's' = survived (выжил), 'd' = died (погиб)
titanic_survival_data = ['s','d','d','d','s','s','d','s','s','s','d','s','d','d','d','d','s','s','s','s','s','s','s']

# Solution / решение
def count_elem_in_list(element, list):
    count = 0
    for item in list:
        if(item == element):
            count += 1
    return count

print(count_elem_in_list('s', titanic_survival_data))
print(count_elem_in_list('d', titanic_survival_data))
14
9
In [18]:
# Find strings starting with 'A' in the list
# Найти все строки, начинающиеся на 'A' (латинское) в списке и записать их в новый список
titanic_some_surnames = ['Arriba', 'Antello', 'Bentham', 'Gordon', 'Daniels', 'Angellini', 'Guaro', 'Heins', 'Valley', 'Amaro']
surnames_on_a = []

MSE = Mean Squared Error - среднеквадратичная ошибка. Сумма квадратов отклонений реального значения функции от смоделированного значения, делённая на количество значений. Часто применяемая метрика для моделей машинного обучения и анализа данных.

image.png

https://www.researchgate.net/figure/Mean-Squared-Error-formula-used-to-evaluate-the-user-model_fig1_221515860

In [21]:
# Homework 1: calculate median of titanic_survived_ages list
# Дома 1: посчитать медиану возраста в titanic_survived_ages

# Homework 2: create a function for calculating median of any list passed as parameter
# Дома 2: написать функцию, которая считает медианное значение списка

# Homework 3 from data analysis: calculate mean square error MSE of two lists of the same length if they are list of predictions and actual values.
# Дома 3 - из анализа данных: посчитать среднюю квадратичную ошибку между списком предсказаний модели и реальных значений функции.
# https://en.wikipedia.org/wiki/Mean_squared_error
# http://statistica.ru/glossary/general/srednekvadraticheskaya-oshibka/
list_predictions = [10, 100, 15.2, 4, 86, 134, 3.2, 3, 12]
list_actuals = [10, 90, 14, 3, 80, 100, 3, 4, 12.5]

def calculate_mse(list1, list2):
    # Your code here
    # Ваш код вычислений будет здесь
    pass

print(calculate_mse(list_predictions, list_actuals))
None

2. Кортеж (неизменяемый список) / Tuple

Это упорядоченный неизменный набор элементов. У элементов кортежа есть индексы. Создаётся tuple (кортеж) круглыми скобками. Элементы перечисляются через запятую.

Неизменность tuple (кортежа) помогает сохранять целостность данных, избегать ошибок и также позволяет использовать его как ключ в словарях.

Tuples are for heterogeneous data, list are for homogeneous data. Tuples are not read-only lists. —Guido van Rossum

2.1. Операции с кортежами

In [22]:
winter_months = ('December','January', 'February')

print(winter_months)
print('Length of tuple is ' + str(len(winter_months)))
print('Type of list object is ' + str(type(winter_months)))

# Access to tuple elements works the same as for list: with index, with slice
# Доступ к элементам кортежа работает так же, как и для списка: доступ по индексу или по срезу
print('Winter month at index -1 ' + winter_months[-1])
print('Winter months slice 0:2 is ' + str(winter_months[0:2]))
('December', 'January', 'February')
Length of tuple is 3
Type of list object is <class 'tuple'>
Winter month at index -1 February
Winter months slice 0:2 is ('December', 'January')
In [23]:
# It is not possible to change element in tuple. This throws TypeError exception
# Изменять элементы кортежа запрещено. Это бросает исключение TypeError
# winter_months[0] = 100;

spring_months = ('March', 'April', 'May');

# We can create new tuples as a concatenation of others
# Можно создавать новые кортежи как объединения старых
winter_and_spring = winter_months + spring_months;
print (winter_and_spring);
('December', 'January', 'February', 'March', 'April', 'May')
In [24]:
# Iterating in tuple
# Итерирование по кортежу - аналогично списку
for x in spring_months:
  print(x)

print()

for i, x in enumerate(spring_months):
  print(i, x)
March
April
May

0 March
1 April
2 May
In [28]:
# Checking if element exists in tuple (or list)
# Проверка, что элемент есть в кортеже (или в списке :))
if "apple" in spring_months:
    print("Yes, 'apple' is in the tuple")
else:
    print("No, 'apple' is not in months tuple")
No, 'apple' is not in months tuple

2.2. Другие функции работы с кортежами

С кортежем работают многие встроенные функции Python, такие как all(), any(), enumerate(), len(), max(), min(), sorted(), tuple().

Функция Описание
all() Вернёт True если все элементы кортежа приводятся к булевому True и также если кортеж пустой.
any() Вернёт True если хотя бы один элемент кортежа приводится к True. Вернёт False для пустого кортежа.
len() Вернёт количество элементов кортежа.
max() Вернёт максимальный элемент кортежа.
min() Вернёт минимальный элемент кортежа.
sorted() Возвращает новый отсортированный список из элементов кортежа.
sum() Вернёт сумму всех элементов кортежа.
tuple() Создаёт кортеж tuple из контейнера (список, строка, множество или словарь).

2.3. Практика с кортежами (tuple)

In [11]:
# What age has the youngest Titanic survival?
# Сколько лет самому молодому выжившему на Титанике?
titanic_survived_ages = (16, 20, 32, 5, 11, 66, 40, 32, 38, 22, 10, 62, 4, 8, 33, 11, 12, 12, 18, 31, 34, 45, 6)

# Hint / подсказка:
# Use standard functions for tuples like max(), min()
# Используйте стандартные фунции вроде max(), min()
In [29]:
# Get average age of survivals using sum() and len() functions for tuple
# Узнать средний возраст выживших, используя функции кортежа sum() и len()
In [30]:
# Homework: Write a Python function that takes two tuples and returns True if they have at least one common member
# There are 4 tests that should pass for this function
# Дома: Написать функцию, принимающую два кортежа и возвращающую True, если у них есть хотя бы один совпадающий элемент
# В коде ниже созданы 4 теста, которые должны проходить (печатать Test N pass), когда функция будет написана верно

def tuples_have_common_element(tuple1, tuple2):
    # Your code here
    # Добавьте ваш код здесь
    return True

tup1 = (0, 1)
tup2 = (2, 2)
tup3 = (1, '2')
tup4 = ('0', '1')
tup5 = ()

# Tests for this fucntion tuples_have_common_element()
# Тесты для новой функции tuples_have_common_element()
if(tuples_have_common_element(tup1, tup2)):
    print ('Error: function returned common element for different tuples.')
else:
    print ('Test 1 pass.')
    
if(tuples_have_common_element(tup1, tup3)):
    print ('Test 2 pass.')
else:
    print ('Error: function did not find common element in tuples.')
    
if(tuples_have_common_element(tup3, tup4)):
    print ('Error: function returned common element for different tuples.')
else:
    print ('Test 3 pass.')
    
if(tuples_have_common_element(tup3, tup5)):
    print ('Error: function returned common element for different tuples.')
else:
    print ('Test 4 pass.')
Error: function returned common element for different tuples.
Test 2 pass.
Error: function returned common element for different tuples.
Error: function returned common element for different tuples.

3. Множество / Set

В математике множество - это набор разных элементов, не обязательно упорядоченных. Множество (set) в Python отражает это определение. Элементы в нём не повторяются. Их нельзя изменять, но можно изменять само множество. У элементов нет индексов (номеров) и нельзя обращаться к ним по индексам и делать срезы (slice).

3.1. Создание множества (set)

In [4]:
list_of_words = "my name is Kate and Kate is my name".split()
print(list_of_words)

# Creation of set from a list 
# Создание множества из списка
set_of_words = set(list_of_words)
print(set_of_words)

# Creation of set of integer values with curly brackets
# Создание множества из элементов - через фигурные скобки
my_set = {1, 10, 20}
print(my_set)

# Creation of set with mixed datatypes
# Создание множества с элементами разных типов данных
new_mix_type_set = {"Test", 2.2, (1, 2, 3)}
print(new_mix_type_set)

# Elements of set should be immutable. Adding mutable elements is prohibited and throws error.
# Элементы множества должны быть неизменяемыми. Добавление изменяемого элемента, например, списка, запрещено и даёт ошибку.
# new_mix_type_set = {"Test", [10, 20]}
['my', 'name', 'is', 'Kate', 'and', 'Kate', 'is', 'my', 'name']
{'is', 'my', 'name', 'and', 'Kate'}
{1, 10, 20}
{'Test', 2.2, (1, 2, 3)}

3.2. Операции с множествами

In [13]:
# Add elemenets: add() method
# Добавление элементов: метод add()
Days=set(["Mon","Tue","Wed","Thu","Fri","Sat"])
Days.add("Sun")
print(Days)

# Remove elements: remove() and discard()
# Удаление элементов: remove() и discard()
# remove() will throw an error if element does not exist in set
# remove() выдаст ошибку, если удалять несуществующий элемент
Days.remove("Sun")

# This will throw an error
# Такой вызов породит ошибку
# Days.remove("Sun")
 
# discard() will not throw an error for nonexistent element
# discard() не выдаст ошибку на удаление несуществующего элемента - можно удалять что угодно
Days.discard("OOO")
print(Days)

# Iterating in set is done similarly to the list and tuple
# Итерирование по множеству - аналогично списку и кортежу
for elem in new_mix_type_set:
    print(elem)
    
# Check if element is present in set
# Проверка наличия элемента в множестве
if("Sun" in Days):
    print ("Sunday is included in set")
else:
    print ("Sunday is not in set")
{'Fri', 'Wed', 'Thu', 'Tue', 'Mon', 'Sun', 'Sat'}
{'Fri', 'Wed', 'Thu', 'Tue', 'Mon', 'Sat'}
Test
2.2
(1, 2, 3)
Sunday is not in set
In [20]:
meetup1_attendees = set(["Alice", "Kate", "James"])
meetup2_attendees  = set(["Victor", "Kate", "James"])

# intersection() function finds elements present in both sets
# Функция intersection() находит пересечение множеств - набор элементов, входящих в оба множества.
intersect = meetup1_attendees.intersection(meetup2_attendees)
print(intersect)
print(type(intersect))

# Other method of making intersection: & operator 
# Другой способ построить пересечение - оператор &
both_meetup_attendees = meetup1_attendees&meetup2_attendees
print(both_meetup_attendees)
print(type(both_meetup_attendees))
{'James', 'Kate'}
<class 'set'>
{'James', 'Kate'}
{'James', 'Kate'}
<class 'set'>
In [23]:
# Union of sets - method union()
# Объединение множеств - возвращает тоже множество
weekdays = {"Mon","Tue","Wed","Thu","Fri"}
weekends = {"Sat","Sun"}
all_days = weekdays.union(weekends)
print(weekdays)
print(all_days)

other_all_days = weekdays|weekends
print(other_all_days)
{'Fri', 'Wed', 'Thu', 'Tue', 'Mon'}
{'Fri', 'Mon', 'Sun', 'Sat', 'Wed', 'Thu', 'Tue'}
{'Fri', 'Mon', 'Sun', 'Sat', 'Wed', 'Thu', 'Tue'}

3.2.3. Разность множеств (Set Difference)

image.png http://fastread.aitechtonic.com/submittutorial/tutorial.php?selecttutormenu=905

In [24]:
# difference() method on a set
# Метод difference() применим к множеству и "вычитает из него" другое множество
print(all_days.difference(weekdays))

# Operator - also makes a difference of sets
# Аналогично разность делается оператором -
print(all_days - weekdays)
{'Sun', 'Sat'}
{'Sun', 'Sat'}

3.3. Другие полезные методы множеств в Python

https://www.programiz.com/python-programming/set#methods

3.4. Практика

In [ ]:
# Python set has a method issubset() checking that one set is completely a subset of another. 
# The task is to implement this method by hands.
# В Python есть метод множества issubset(), проверяющий, что одно множество - подмножество другого.
# Задание - написать этот метод самостоятельно.
In [ ]:
# Find max and min element in set
# Найти максимальный и минимальный элемент в множестве
In [33]:
# Homework. With help of strings and sets, print all unique surnames of titanic passengers.
# Suppose they all have only one-word first name and one-word surname without middle name.
# Дома. Работая со строками и множествами, вывести список уникальных фамилий пассажиров титаника.
# Предполагаем, что у каждого из них имя и фамилия из одного слова и нет вторых имён.
titanic_passengers = ['Win Adams', 'James Adams', 'May Connor', 'Al Sinn', 'Beth Sinn', 'Jay Hughes', 'Jenny Adams', 'Wes Garrett',
                'William Young', 'Howard Connor', 'Din Hughes', 'Mary Jane']

Картинки взяты из онлайн-обучающей страницы https://www.programiz.com/python-programming/set

Images used are courtesy of https://www.programiz.com/python-programming/set

4. Словарь

Типы в Python можно разделить на изменяемые(mutable) и неизменные(immutable). Словарь, множество и список - изменяемые типы. Простые типы, строки и tuple - неизменные.

Словарь в Python — это неупорядоченный набор элементов. У них нет индексов, но есть ключи. Особенности словарей:

  • Ключи в словаре уникальны, а значения могут повторяться.
  • Доступ к элементу осуществляется по ключу, а не по индексу.
  • Значения словаря хранятся в неотсортированном порядке, ключи могут храниться не в том порядке, в котором они добавляются.
  • По аналогии со списками, словарь может хранить вложенные словари. Словарь может хранить в качестве значений объекты любого типа. Ключ в словаре — immutable (неизменный) тип, может быть строкой, целым числом, float либо кортежем, состоящим из указанных типов.
  • Словари реализованы как хеш-таблицы с быстрым доступом.
  • Словари, как и списки, хранят ссылки на объекты, а не сами объекты.

Основные операции со словарями: создание, удаление, доступ к элементам, вставка, проверка на вхождение, итерирование по словарю.

Описание словаря в Python (источник: https://younglinux.info/python/dictionary.php)

image.png

In [31]:
dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
print ("dict['Name']: ", dict['Name'])
print ("dict['Age']: ", dict['Age'])

# Update existing entry in dict
# Обновление существующего элемента
dict['Age'] = 8; # update existing entry

# Add new entry
# Добавление нового элемента
dict['School'] = "My School 40";
dict['Name']:  Zara
dict['Age']:  7
Name key exists in this dictionary
In [38]:
# Other operations.Looping and iterating
# Другие операция со словарями, итерирование

# Проверка ключа на вхождение в словарь
if('Name' in dict.keys()):
    print ("Name key exists in this dictionary")

# Проверка значения на вхождение в словарь
if(8 in dict.values()):
    print ("There is something with number 8 there")

# Цикл по ключам словаря
for key in dict.keys():
    print(key)

# Цикл по значениям
for val in dict.values():
    print(type(val))
    
# Цикл по ключам и значениям одновременно
for key, val in dict.items():
    print(f'The {key} data set has value "{val}".')
Name key exists in this dictionary
There is something with number 8 there
Name
Age
Class
School
<class 'str'>
<class 'int'>
<class 'str'>
<class 'str'>
The Name data set has value "Zara".
The Age data set has value "8".
The Class data set has value "First".
The School data set has value "My School 40".

4.4. Практика

In [ ]:
# 1. Dictionary with list of groceries with prices
# The task is to calculate sum price of all groceries planned
# 1. Дан словарь со списком покупок и их стоимостью
# Задача: посчитать суммарную стоимость всех покупок
to_buy_prices = {apples: 150, oranges: 100, bananas: 80, kiwi: 120, plum: 200}
In [ ]:
# 2. This dictionary contains people names and their mobile phones brand
# The task is to get a set of all differen phones brand in this dict
# 2. Задан словарь с именами людей и марками их телефонов
# Задача: получить множество (set) всех марок телефонов у этих людей
to_buy_prices = {'Kate': 'iphone', 'Alex': 'samsung', 'Mike': 'iphone', 'Val': 'lg', 'Vic':'iphone', 'Nataly':'asus'}
In [24]:
# Homework / Дома: Задан словарь имён пользователей и их паролей
users = { "user1" : "password1", "user2" : "password2", "user3" : "password3" }

# Написать функцию accept_login() с параметрами – словарь пользоватлей и паролей, 
# и то, что пользователь ввёл: имя пользователя и пароль.
def accept_login(dict, username, password):
    pass

# Пример, как функция будет вызваться. 
# Проверьте такие варианты: существующий пользователь с верным паролем, с неверным, несуществующий пользователь.
if accept_login(users, "wronguser", "wrongpassword"):
    print("login successful!")
else:
    print("login failed...")
login failed...

5. Что поучить, почитать и послушать, чтобы научиться программировать

  1. Пройти от начала до конца один курс. Например, на Stepik или Coursera Если затянет, пройти специализацию из 4 курсов.

  2. Для само-мотивации послушать подкаст Python Junior

  3. Процти целиком онлайн-обучающие программы вроде PythonTutor.ru

  4. Пойти на курсы offline или найти преподавателя (ментора)

  5. Выучить английский