6. Основы Python. Часть II - qa-guru/knowledge-base GitHub Wiki

Данный раздел разделен на несколько частей(каждый пункт - это ссылка на соответствующий раздел):

Переход по внутренним ссылкам происходит только при открытом разделе блока в котором находится ссылка.



Условия (if/else/elif)

Нажать, чтобы раскрыть

Условия в Python, как и в любом другом языке программирования, нужны для ветвления логики программы и помогают в зависимости от условий выбирать действия.

В результате работы условий всегда возвращается True или False. Эти значения называются boolean типом.

Также есть тип данных None, который означает отсутствие значения. В Python None интерпретируется как False.

>>> type(None)
<class 'NoneType'>

Boolean тип

boolean тип - это тип данных, который может принимать только два значения: True или False. В Python True и False являются ключевыми словами.

>>> type(True)
<class 'bool'>

>>> type(False)
<class 'bool'>

Приведение типов к boolean

В Python есть функция bool(), которая преобразует объект в boolean тип. Все объекты в Python имеют значение True, если они не равны False, None, 0 или пустой строке "". Все остальные объекты имеют значение False.

>>> bool(0) # 0 - это всегда False
False
>>> bool(1) # любое число, кроме 0, это True
True
>>> bool(-1) # любое число(в том числе отрицательное), кроме 0, это True
True
>>> bool("Hello") # любая непустая строка это True
True
>>> bool("") # пустая строка это False
False
>>> bool(None) # None это всегда False
False
>>> bool([]) # пустой список это False
False
>>> bool([1, 2, 3]) # непустой список это True
True
>>> bool({}) # пустой словарь это False
False
>>> bool({1: 2}) # непустой словарь это True
True
>>> bool(0.0) # 0.0 это False
False
>>> bool(0.1) # любое число, кроме 0.0, это True
True
>>> bool([False]) # список с одним элементом False это True
True
>>> bool([0]) # список с одним элементом 0 это True
True
>>> bool([None]) # список с одним элементом None это True
True

If/else/elif

Если условие истинно(True), то выполняется блок кода, который находится после if. Если условие ложно(False), то не будет выполнено ничего. В случае если есть несколько условий, выполняется блок кода, который находится после else или elif(else if). Блоки кода else и elif не обязательны.

Отступами (4 пробела) в Python обозначается вложенность блоков кода.

Примеры использования условий:

>>> if True:
...     print("True")  # данное условие истинно, поэтому будет напечатано "True"

>>> if False:  
...     print("False") # данное условие ложно, поэтому ничего не будет напечатано

Чтобы выполнить часть кода если условие ложно, можно использовать часть not. Оператор not изменяет значение на противоположное:

>>> if not False:
...     print("False")  # данное условие истинно, поэтому будет напечатано "False"

Вместе с условиями можно использовать логические операторы:

>>> if True or False:  # оператор "или". Если хотя бы одно из условий истинно, то будет выполнен блок кода
...     print("something")

>>> if True and False:  # оператор "и". Если оба условия истинны, то будет выполнен блок кода
...     print("something")

Можно использовать else и задавать более сложные условия:

>>> if True:
...     print("True")
... else:                 # если условие ложно, то будет выполнен блок кода после else
...     print("False")
>>> code = 200

>>> if 200 <= code < 400:
...     print("Проверка пройдена, хороший ответ")
... else:
...     print("Плохой код ответа")

Если надо проверить больше условий, то можно использовать elif и else:

>>> code = 1000

>>> if 200 <= code < 400:
...     print("Проверка пройдена, хороший ответ")
... elif 400 <= code < 500:
...     print("Плохой код ответа")
... else:
...     print("Какой-то странный код ответа")  # будет выполнен блок кода после else так как ни одно из условий выше не истинно

Может быть сколько угодно действий если условие истинно, и сколько угодно условий в elif и else.

>>> user_list = []

>>> if user_list == []:
...     pass # если список пустой, то ничего не делать

>>> items_count = 0

>>> if items_count == 0:
...     pass

>>> text = 'abc'

>>> if 'abc' == text:
...     pass

В Python есть 'синтаксический сахар' для проверки наличия элемента в списке, словаре, множестве и т.д.:

>>> user_list = []

>>> if user_list:
...     pass # если список пустой, то ничего не делать

>>> items_count = 0

>>> if items_count:
...     pass

>>> text = 'abc'

>>> if 'abc':
...     pass

Дебаггинг(Debugging)

Нажать, чтобы раскрыть

Дебаггинг - это процесс поиска и устранения ошибок в программе. В Python для дебаггинга используются различные инструменты, такие как точки остановки, отладка по шагам и отладка по выражениям. Таким образом вы можете запустить определенный участок кода и изучить состояние программы в этот момент времени.

Точки остановки(breakpoints)

Точки остановки - это места в коде, где выполнение программы будет приостановлено, чтобы вы могли изучить состояние программы в этот момент времени. Чтобы установить точку остановки в PyCharm, нажмите на левую часть окна рядом с номером строки кода. После этого выполнение программы будет приостановлено на этой строке.

Далее необходимо запустить программу в режиме отладки. Для этого нажмите на зеленый треугольник в верхней части окна(если файл уже был ранее запущен) или на названии файла правой кнопкой мыши и выберите Debug.

После запуска программы в режиме отладки выполнение программы будет приостановлено на установленной точке остановки. Место остановки будет обозначено красным кружком и строка будет подсвечена. Ниже будет панель отладки, где вы сможете изучить состояние программы, что именно было выполнено до точки остановки и что будет выполнено дальше.

Отладка по шагам

В панели отладки вы можете изучить состояние переменных(их типы и значение), выполнить код по шагам, перейти к следующей точке остановки, изменить значение переменных и многое другое.

Блок дебаггинга в PyCharm разделен на несколько блоков:

  • вкладки (Threads & Variables, Console)
  • кнопки управления выполнением программы( rerun, stop, resume, pause program, step over, step into, step into my code, step out, view breakpoints, mute breakpoints)

На вкладке Threads & Variables вы можете изучить состояние переменных и выполнение программы. На вкладке Console вы можете выполнить код в текущем контексте или увидеть вывод программы(все что выводится в консоль(print()))

Кнопки управления выполнением программы(слева направо):

  • rerun - перезапустить программу(полностью перезапустит вашу программу)
  • stop - остановить выполнение программы
  • resume - продолжить выполнение программы
  • pause program - приостановить выполнение программы
  • step over - выполнить следующую строку кода(только одну строку)
  • step into - выполнить следующую строку кода, если это функция, то перейти внутрь функции
  • step into my code - выполнить следующую строку кода, если это функция, то перейти внутрь функции, но не переходить в сторонние библиотеки
  • step out - выполнить код до конца текущей функции
  • view breakpoints - посмотреть все точки остановки
  • mute breakpoints - отключить все точки остановки

Отладка по шагам - это процесс выполнения программы по одной строке кода за раз. Во время отладки вы можете изучить состояние программы на каждом шаге. Для выполнения программы по шагам используются кнопки управления выполнением программы(подробнее описано выше).

Отладка по выражениям

Отладка по выражениям - это процесс когда вы можете изучить значение выражения в текущем контексте программы.

В режиме дебаггинга можно изменить значение переменных и выражений, для этого есть 2 способа:

Первый способ изменения значения переменной:

Нажать правой кнопкой мыши на переменную и выбрать Set Value:

Далее указать новое значение переменной и нажать Enter. Значение переменной будет изменено на новое.


Второй способ изменения значения переменной:

Если у вас нет блока separate watches, его необходимо включить. Для этого нажмите на значок layout settings в правом верхнем углу окна отладки и выберите separate watches. После активации у вас отобразится блок separate watches с правой стороны окна отладки.

Далее необходимо в поле ввода ввести значение для переменной и нажать Enter. Значение переменной будет изменено на новое.

В поле ввода можно подставлять условия для его проверки и увидеть результат.

Циклы (loops)

Нажать, чтобы раскрыть

Циклы в Python нужны для повторения одних и тех же действий несколько раз. В Python есть два основных вида циклов: while и for.

While

Цикл while выполняет содержимое тела цикла до тех пор, пока его условие истинно:

i = 10

>>> while i < 15:
...     print(i)
...     i = i + 1  # увеличиваем значение переменной i на 1. Краткая запись i += 1

10
11
12
13
14

Цикл из примера будет выполняться до тех пор, пока переменная i меньше 15

Важно учесть: Нужно быть осторожным с циклами while, так как они могут выполняться бесконечно, если условие никогда не станет ложным.

Пример бесконечного цикла:

i = 10

>>> while i < 15:
...     print(i)

Цикл будет бесконечно печатать число 10, так как переменная i никогда не изменится и условие никогда не станет ложным.

Классический цикл while с итератором(счетчиком):

>>> iterations_count = 10
>>> i = 0  # итератор
>>> while i < iterations_count:
...     print(f"Текущая итерация: {i}")
...     i += 1

Текущая итерация: 0
Текущая итерация: 1
....
Текущая итерация: 9

For

Цикл for проходится по итерируемому объекту и выполняет заданные действия с каждым его элементом:

>>> users = [
        {"name": "Oleg", "age": 32},
        {"name": "Sergey", "age": 24},
        {"name": "Stanislav", "age": 15},
        {"name": "Olga", "age": 45},
        {"name": "Maria", "age": 18}
    ]


>>> for user in users:
...     print(f"Пользователю {user['name']} {user['age']} лет")

Цикл из примера будет выполняться для каждого элемента списка users

Пример применения цикла for для перебора числовых последовательностей:

>>> for i in range(5):
...     print(i)

0
1
2
3
4

Пример применения цикла for для перебора строк:

>>> for letter in "Hello":
...     print(letter)

H
e
l
l
o

При использовании цикла for для перебора словаря, цикл будет перебирать ключи словаря:

>>> d = {
        "first": 1,
        "second": 2,
        "third": 3
    }

>>> for item in d:
...     print(item)
    
first
second
third

Данная запись идентична записи ниже:

>>> for item in d.keys():
...     print(item)

Если надо перебрать значения словаря, то можно использовать метод values():

>>> for item in d.values():
...     print(item)

1
2
3

Чтобы получить и ключи и значения словаря, можно использовать метод items():

>>> for key, value in d.items():
...     print(key, value)

('first', 1)
('second', 2)
('third', 3)

Чтобы разбить словарь на ключи и значения, можно использовать метод items():

>>> for key, value in d.items():
...     print(f"Ключ: {key}, Значение: {value}")

Ключ: first, Значение: 1
Ключ: second, Значение: 2
Ключ: third, Значение: 3

Вложенные циклы

Вложенные циклы часто используются для перебора элементов многомерных списков и словарей.

Циклы могут быть вложенными, т.е. один цикл может находиться внутри другого цикла:

>>> for i in range(2):   # range - это функция, которая возвращает последовательность чисел
...     for j in range(3):
...         print(i, j) # будет напечатано 6 пар чисел, потому что внутренний цикл будет выполнен 3 раза для каждой итерации внешнего цикла

0 0
0 1
0 2
1 0
1 1
1 2

Вложенный цикл while может иметь следующий вид:

>>> i = 0
>>> j = 0

>>> while i < 2:
...     while j < 3:
...        print(i, j)
...        j += 1
...     i += 1
...     j = 0

0 0
0 1
0 2
1 0
1 1
1 2

Пример цикла с шагом между итерациями:

>>> iterations_count = 10


>>> for i in range(3, iterations_count, 2):
...     print(f"Текущая итерация: {i}")

Текущая итерация: 3
Текущая итерация: 5
Текущая итерация: 7
Текущая итерация: 9

Данный цикл будет выполняться с 3 до 10 с шагом 2

break/continue/else

В циклах можно использовать операторы break и continue для управления выполнением цикла.

Оператор break прерывает выполнение цикла:

>>> for i in range(10):
...     if i == 5:
...         break
...     print(i)

0
1
2
3
4

Оператор continue прерывает текущую итерацию цикла и переходит к следующей итерации:

>>> for i in range(10):
...     if i == 5:
...         continue
...         print(i) # данная строка не будет выполнена
...     print(i)

0
1
2
3
4
6
7
8
9

Оператор else в циклах используется для выполнения блока кода после завершения цикла:

>>> for i in range(5):
...     print(i)
... else:
...     print("Цикл завершен")

0
1
2
3
4
Цикл завершен

range

range - это функция, которая возвращает последовательность чисел. Она может принимать один, два или три аргумента.

>>> for i in range(5): # если передан один аргумент, то range возвращает последовательность чисел от 0 до n-1
...     print(i)

0
1
2
3
4
>>> for i in range(2, 5):  # если передано два аргумента, то range возвращает последовательность чисел от a до b-1
...     print(i)

2
3
4
>>> for i in range(2, 10, 2):  # если передано три аргумента, то range возвращает последовательность чисел от a до b-1 с шагом c
...     print(i)

2
4
6
8

enumerate

enumerate - это функция, которая возвращает индекс и значение элемента итерируемого объекта.

>>> for i, letter in enumerate("Hello"):
...     print(i, letter)

0 H
1 e
2 l
3 l
4 o
>>> cities = ["Екатеринбург", "Москва", "Сочи"]

>>> for i, city in enumerate(cities):
...     print(f"{city} на {i + 1} месте по загрязнению воздуха")

Екатеринбург на 1 месте по загрязнению воздуха
Москва на 2 месте по загрязнению воздуха
Сочи на 3 месте по загрязнению воздуха

Функции (functions)

Нажать, чтобы раскрыть

Определение функции

Функция это блок кода, который выполняет определенную задачу. Функции используются для группировки кода, который выполняет определенную задачу, и для повторного использования этого кода.

>>> def my_func():
...     print("Мы вызвали функцию!")

>>> my_func()  # вызов функции
Мы вызвали функцию!

Функция может быть любой сложности, может принимать любое количество аргументов и возвращать любые объекты.

Параметры функции

Функция с позиционными аргументами

Функция может принимать аргументы:

>>> def sum_numbers(a, b):
        print(a + b)

>>> sum_numbers(10, 15)
25

>>> sum_numbers(20, 30)
50

>>> sum_numbers(-8912479812674981274, 1)
-8912479812674981273

>>> sum_numbers("abc", "def")
abcdef
>>> def greeting(name):
...     print(f"Hello, {name}!")

>>> greeting("Oleg")
Hello, Oleg!

Функция с именованными аргументами

Это функция, в которой аргументы передаются с указанием их имени:

>>> def sum_numbers(a, b):
        print(a + b)

>>> sum_numbers(a=10, b=15)
25

>>> sum_numbers(b=10, a=15)
25

Функция с аргументами по умолчанию

В функции можно использовать аргументы по умолчанию:

>>> def greeting(name="User"):
...     print("Hello, {name}!")

>>> greeting() # Если не передать аргумент, то будет использовано значение по умолчанию
Hello, User!
>>> print(greeting("Oleg")) # Если передать аргумент, то будет использовано переданное значение
Hello, Oleg!

Типизация аргументов

Типизация аргументов это указание типа данных, которые должны быть переданы в функцию. Это позволяет улучшить читаемость кода и уменьшить количество ошибок.

В Python функции пишутся с применением типизации аргументов:

>>> def greeting(name: str) -> str:  # после имени аргумента указывается тип данных, который должен быть передан в функцию. После знака "->" указывается тип данных, который функция возвращает
...     print("Hello, {name}!")

>>> greeting("Oleg")
Hello, Oleg!

Возвращаемое значение

Функция может возвращать несколько значений:

>>> def sum(a: int, b: int):
        return a + b


>>> n = sum(10, 15)
>>> print(n)
25
>>> def get_user_info(name, age):
...     return name, age

>>> name, age = get_user_info("Oleg", 32) # Функция возвращает кортеж, который можно распаковать в переменные
>>> print(name, age)
Oleg 32

Переменное количество аргументов

Функция может принимать переменное количество аргументов:

>>> def sum_numbers(*args): # *args - это кортеж, который содержит все переданные аргументы
...     return sum(args)

>>> print(sum_numbers(1, 2, 3, 4, 5)) # Функция принимает любое количество аргументов
15

Функция может принимать любые объекты в качестве аргументов и возвращать любые объекты:

>>> def get_user_info(name, age, city, *args, **kwargs):
...     return name, age, city, args, kwargs

>>> name, age, city, args, kwargs = get_user_info("Oleg", 32, "Moscow", "Python", "QA", "Automation", experience="5 years", salary="100k")
>>> print(name, age, city, args, kwargs)
Oleg 32 Moscow ('Python', 'QA', 'Automation') {'experience': '5 years', 'salary': '100k'}

Область видимости

Область видимости переменной - это место, где переменная доступна. В Python есть глобальная и локальная область видимости.

Глобальная область видимости - это место, где переменная доступна во всем файле.

>>> a = 10  # переменная 'a' доступна во всем файле

>>> def my_func():
...     print(a)

>>> my_func()
10
>>> print(a)  # переменная a доступна вне функции
10

Локальная область видимости - это место, где переменная доступна только внутри функции.

>>> def my_func():
...     a = 10  # переменная a доступна только внутри функции
...     print(a)

>>> my_func()
10
>>> print(a)  # переменная a не доступна вне функции
NameError: name 'a' is not defined

Лямбда-функции

Лямбда-функции - это анонимные функции, которые могут содержать только одно выражение. Они используются для создания простых функций.

>>> f = lambda x: x * 2  # создание лямбда-функции, которая умножает переданное число на 2
>>> print(f(10))
20
>>> f = lambda x, y: x + y  # создание лямбда-функции, которая складывает два переданных числа
>>> print(f(10, 15))
25
>>> f = lambda x, y, z: x + y + z  # создание лямбда-функции, которая складывает три переданных числа
>>> print(f(10, 15, 20))
45

Функция - тоже объект

Функция в Python это объект, который можно передавать в качестве аргумента в другие функции, возвращать из функции, присваивать переменной и т.д.

>>> users = [
        {"name": "Oleg", "age": 32},
        {"name": "Sergey", "age": 24},
        {"name": "Stanislav", "age": 15},
        {"name": "Olga", "age": 45},
        {"name": "Maria", "age": 18},
    ]

>>> def get_age(user):
...     return user["age"]

>>> users.sort(key=get_age)  # функция get_age передается в качестве аргумента в функцию sort
>>> print(users)
[{'name': 'Stanislav', 'age': 15}, {'name': 'Maria', 'age': 18}, {'name': 'Sergey', 'age': 24}, {'name': 'Oleg', 'age': 32}, {'name': 'Olga', 'age': 45}]

Чтобы написать функцию get_age в виде лямбда-функции:

>>> users.sort(key=lambda user: user["age"])  # лямбда-функция передается в качестве аргумента в функцию sort

>>> print(users)
[{'name': 'Stanislav', 'age': 15}, {'name': 'Maria', 'age': 18}, {'name': 'Sergey', 'age': 24}, {'name': 'Oleg', 'age': 32}, {'name': 'Olga', 'age': 45}]

Также можно использовать синтаксический сахар для создания лямбда-функции:

Функция itemgetter из модуля operator возвращает функцию, которая извлекает элемент из объекта по индексу.

>>> from operator import itemgetter

>>> users.sort(key=itemgetter("age"))  # функция itemgetter передается в качестве аргумента в функцию sort

>>> print(users)
[{'name': 'Stanislav', 'age': 15}, {'name': 'Maria', 'age': 18}, {'name': 'Sergey', 'age': 24}, {'name': 'Oleg', 'age': 32}, {'name': 'Olga', 'age': 45}]

Подсказка к домашнему заданию

Смотрите только когда не можете понять что делать(нажать, чтобы раскрыть)

В тесте test_dark_theme_by_time_and_user_choice нужно переключить темную тему в зависимости от времени суток и выбора пользователя.

Согласно условию ранее мы знаем что темная тема с 22 до 6 часов утра.

Также у нас есть переменная dark_theme_enabled_by_user, которая может принимать 3 значения:

  • True - Темная тема включена
  • False - Темная тема выключена
  • None - Пользователь не сделал выбор (используется переключение по времени системы)

Таким образом, если пользователь сделал выбор, то мы должны использовать его выбор, если нет, то мы должны использовать переключение по времени системы.

Чтобы реализовать это, можно использовать условный оператор if/elif/else:

current_time = time(hour=16)
dark_theme_enabled_by_user = True

if dark_theme_enabled_by_user: # если пользователь сделал выбор
    # использовать выбор пользователя
elif dark_theme_enabled_by_user is None: # если пользователь не сделал выбор
    if # проверить время суток если время с 22 до 6:
        # включить темную тему(True)
    else: 
        # выключить темную тему(False) если время не в диапазоне от 22 до 6
else:
    # темная тема выключена
assert is_dark_theme is True

Вы можете проверить себя используя разное время для current_time и разные значения для dark_theme_enabled_by_user.


В задании

# Сделайте функцию, которая будет печатать
# читаемое имя переданной ей функции и значений аргументов.
# Вызовите ее внутри функций, описанных ниже
# Подсказка: Имя функции можно получить с помощью func.__name__
# Например, вызов следующей функции должен преобразовать имя функции
# в более читаемый вариант (заменить символ подчеркивания на пробел,
# сделать буквы заглавными (или первую букву), затем вывести значения всех аргументов этой функции:
# >>> open_browser(browser_name="Chrome")
# "Open Browser [Chrome]"

Нужно создать функцию, которая будет принимать имя функции и значения аргументов и печатать их в читаемом виде.

def print_function_name_and_args(func, *args): # функция принимает имя функции и значения аргументов
    func_name = func.__name__.replace('_', ' ').title()  # получаем имя функции и преобразуем его в читаемый вид (заменяем символ подчеркивания на пробел, делаем первую букву заглавной)
    args_name = ", ".join([*args]) # преобразуем значения аргументов в строку
    print(f"{func_name} [{args_name}]") # печатаем имя функции и значения аргументов
    return f"{func_name} [{args_name}]" # возвращаем строку с именем функции и значениями аргументов
    
def open_browser(browser_name):
    print_function_name_and_args(open_browser, browser_name)
    
⚠️ **GitHub.com Fallback** ⚠️