Занятие 4

Лабораторное занятие 4

Логический тип данных (тип bool)

Логический тип данных (bool) относится к неизменяемым типам данных и переменная этого типа может принимать только два значения - True и False. Это еще один базовый тип данных.

In [ ]:
print(1 > 0)
True
In [ ]:
a = 1 > 0
print(a)
True
In [ ]:
b = 1 < 0
print(b)
False
In [ ]:
print(a * 5)
print(b * 3)
5
0

Логические условия можно объединять с помощью операторов & ('логическое И'), | ('логическое ИЛИ') и ~ ('логическое отрицание').

In [ ]:
a = 1
b = 10
(a < 5) & (a<=b)
Out[ ]:
True

Именно тип bool используется в каскадной конструкции if elif ... else:

In [ ]:
if 1 > 0:
    print(2)
2

Существует приведение типов. Любое число, отличное от нуля с логической точки зрения — True, нуль воспринимается как False

In [ ]:
if 5:
    print(2)
else:
    print("!")
2
In [ ]:
if 0:
    print(4)
else:
    print("!")
!
In [ ]:
for i in [1, 2, 3, 4, 5]:
    print(i)
1
2
3
4
5

Оператор in

Лексема in может быть использована не только как часть синтаксической конструкции цикла for, но и как оператор для проверки принадлежности элемента итерируемому объекту:

In [ ]:
print(4 in [1, 2, 3, 4, 5])
print(6 in [1, 2, 3, 4, 5])
True
False
In [ ]:
print(4 in range(1, 6))
print(6 in range(1, 6))
True
False
In [ ]:
print("b" in "abc")
print("B" in "abc")
True
False

Двумерные списки

"Двумерный список" — это обычный список, элементами которого являются списки. Такой термин удобен для нас, но для Python нет разницы между одномерными и двумерными списками. В частности, нечто такое вообще будет являться промежуточным: [1, [2, 3, 4], 5]

Списки элементами, которых являются другие списки называются вложенными. Например, вложенными будут списки [1, [2, 3, 4], 5] и [[1, 2, 3, 4], [5, 6], [7, 8, 9]].

Создадим список и рассмотрим обращение к элементам.

In [ ]:
a = [[1, 2, 3], [4, 5, 6]]
print(*a)
print(a[1])
print(a[1][0])
print(a[-1][-1])
[1, 2, 3] [4, 5, 6]
[4, 5, 6]
4
6
In [ ]:
for i in range(len(a)):
    print(*a[i])
1 2 3
4 5 6

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

In [ ]:
a = [[1, 2, 3] for i in range(4)]
for i in range(len(a)):
    print(*a[i])
1 2 3
1 2 3
1 2 3
1 2 3
In [ ]:
a = [[i, i+1, i+2] for i in range(4)]
for i in range(len(a)):
    print(*a[i])
0 1 2
1 2 3
2 3 4
3 4 5
In [ ]:
for x in a:
    print(*x)
0 1 2
1 2 3
2 3 4
3 4 5
In [ ]:
print(a)
[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]

Вот пример создания списка списков при помощи генератора списка, вложенного в генератор списка:

In [ ]:
a = [[j for j in range(5)] for i in range(5)]
for x in a:
    print(*x)
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4

Упражнение 1: создайте двумерный список. Заполните его следующим образом: значение элемента - номер его строки.

In [ ]:
 

Пример. Напечатаем таблицу умножения $10*10$:

In [ ]:
n, m = 10, 10
a = [[i * j for j in range(1, m + 1)] for i in range(1, n + 1)]
for x in a:
    print(*x)
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100

Сортировка списков

Не углубляясь в алгоритмы, разберем, как можно сортировать списки в Python. Для этого есть функции sort и sorted. Первая изменяет сортируемый объект, вторая создает новый, а исходный остается:

In [ ]:
a = [3, 1, 5, 4, 2]
a.sort()
print(a)
[1, 2, 3, 4, 5]
In [ ]:
a = [3, 1, 5, 4, 2]
print(sorted(a))
print(a)
[1, 2, 3, 4, 5]
[3, 1, 5, 4, 2]

Но можем использовать вторую вместо первой:

In [ ]:
a = [3, 1, 5, 4, 2]
a = sorted(a)
print(a)
[1, 2, 3, 4, 5]

Сортировка по ключу. key - это второй параметр функции sorted и он часто записывает в виде лямбда-функции, которая возвращает функции от объектов, которые мы используем в качестве меры этих объектов для сортировки

In [ ]:
a = [3, 1, 5, 4, 2]
a = sorted(a, key = lambda x: -x)
print(a)
[5, 4, 3, 2, 1]
In [ ]:
a = [[1, 5], [3, 3], [5, 5], [3, 0]]
print(sorted(a, key = lambda x: sum(x)))
[[3, 0], [1, 5], [3, 3], [5, 5]]
In [ ]:
a = [[1, 5], [3, 3], [5, 5], [3, 0]]
print(sorted(a, key = lambda x: x[0] ** 2 + x[1] ** 2))
[[3, 0], [3, 3], [1, 5], [5, 5]]

Можем сортировать по нескольким параметрам в порядке убывания приоритета:

In [ ]:
a = [[1, 6], [2, 5], [3, 4]]
print(sorted(a, key = lambda x: (sum(x), x[1])))
[[3, 4], [2, 5], [1, 6]]

(sum(x), x[1]) - это кортеж. Кортежи похожи на списки, но они неизменяемы (нельзя присваивать по индексу и добавлять элементы)

In [ ]:
a = [1, 2]
a[1] = 3
print(a)
[1, 3]
In [ ]:
a = (1, 2)
a[1] = 3
print(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_41472\2988772165.py in <module>
      1 a = (1, 2)
----> 2 a[1] = 3
      3 print(a)

TypeError: 'tuple' object does not support item assignment
In [ ]:
a = [1, 2]
a.append(3)
print(a)
[1, 2, 3]
In [ ]:
a = (1, 2)
a.append(3)
print(a)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_41472\2197499556.py in <module>
      1 a = (1, 2)
----> 2 a.append(3)
      3 print(a)

AttributeError: 'tuple' object has no attribute 'append'
In [ ]:
a = [1, 2]
a = [3, 4]
print(a)
[3, 4]
In [ ]:
a = (1, 2)
a = (3, 4)
print(a)
(3, 4)

Строки сортируются в лексикографическом порядке:

In [ ]:
a = ["100", "11", "12", "120", "119"]
print(sorted(a))
['100', '11', '119', '12', '120']
In [ ]:
a = ["100", "11", "12", "120", "119"]
print(sorted(a, key = lambda x: int(x)))
['11', '12', '100', '119', '120']
In [ ]:
 

Функции, возвращающие значение

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

По сути функции - это вспомогательные алгоритмы.

Любая функция/метод (про методы Вы узнаете позже) всегда возвращает какое-либо значение. Если возвращаемого значения нет, то возвращается None. Выбор между использованием return, return None или отсутствием return в функции зависит от личного стиля кодирования и требований к читаемости кода.

Логическое значение как результат работы функции

Ниже описаны три аналогичных функции. Прочитайте их тела и объясните почему они работают одинаково. Какая из них вам больше нравится?

In [ ]:
def is_positive(x):
    if x > 0:
        return True
    else:
        return False

print(is_positive(5))
print(is_positive(-5))
True
False
In [ ]:
def is_positive(x):
    if x > 0:
        return True
    return False

print(is_positive(5))
print(is_positive(-5))
True
False
In [ ]:
def is_positive(x):
    return x > 0

print(is_positive(5))
print(is_positive(-5))
True
False

Анонимные функции (lambda-функции)

Лямбда-функции в Python являются анонимными. Это означает, что функция безымянна. Как известно, ключевое слов def используется в Python для определения обычной функции. В свою очередь, ключевое слово lambda используется для определения анонимной функции.

Лямбда-функции задаются следующим образом:

Lambda аргументы: выражение

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

In [ ]:
double = lambda x: x*2
print(double(5))
10

Функции как аргументы функций map, reduce и filter

Для поэлементной обработки любого итерируемого объекта можно использовать функции map и filter.

Функция map() используется для применения функции к каждому элементу итерируемого объекта (например, списка или словаря) и возврата нового итератора для получения результатов.

Синтаксис функция map() следующий:

map(function, iterable, [iterable 2, iterable 3, ...])

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

Функция reduce() принимает в качестве аргументов функцию и список. Функция вызывается с помощью лямбда-функции и итерируемого объекта и возвращается новый уменьшенный результат. Так выполняется повторяющаяся операцию над парами итерируемых объектов. Функция reduce() входит в состав модуля functools.

In [ ]:
a = "1 2 -3 4 -5".split()
print(a)
a = list(map(int, a))
print(a)
['1', '2', '-3', '4', '-5']
[1, 2, -3, 4, -5]
In [ ]:
a = "1 2 -3 4 -5".split()
print(a)
a = list(map(float, a))
print(a)
['1', '2', '-3', '4', '-5']
[1.0, 2.0, -3.0, 4.0, -5.0]
In [ ]:
a = "1 2 -3 4 -5 0".split()
print(a)
a = list(map(bool, a))
print(a)
['1', '2', '-3', '4', '-5', '0']
[True, True, True, True, True, True]
In [ ]:
a = "1 2 -3 4 -5 0".split()
print(a)
a = list(map(int, a))
print(a)
a = list(map(bool, a))
print(a)
['1', '2', '-3', '4', '-5', '0']
[1, 2, -3, 4, -5, 0]
[True, True, True, True, True, False]

Как параметр для map можно передавать не только стандартные функции, но и свои собственные:

In [ ]:
def my_function(x):
    return x * 2

a = "1 2 -3 4 -5".split()
print(a)
a = list(map(my_function, a))
print(a)
['1', '2', '-3', '4', '-5']
['11', '22', '-3-3', '44', '-5-5']

Такие "одноразовые функции" загрязняют пространство имён, поэтому есть возможность создать безымянную функцию при помощи слова lambda. Вычисляясь, лямбда-выражение "изготавливает" новую функцию и передаёт её как объект туда, где она нужна — в функцию map. Затем она сразу забывается.

In [ ]:
a = "1 2 -3 4 -5".split()
print(a)
a = list(map(lambda x: x * 2, a))
print(a)
['1', '2', '-3', '4', '-5']
['11', '22', '-3-3', '44', '-5-5']
In [ ]:
from functools import reduce

current_list = [5, 15, 20, 30, 50, 55, 75, 60, 70]
summa = reduce((lambda x, y: x + y), current_list)
print(summa)
380
In [ ]:
a = [1, 2, -3, 4, -5]
b = list(map(lambda x: x > 0, a))
c = list(filter(lambda x: x > 0, a))
print(b)
print(c)
[True, True, False, True, False]
[1, 2, 4]

Можем делать "сложную" лямбда-функцию:

In [ ]:
a = "1 2 -3 4 -5".split()
print(a)
a = list(map(lambda x: abs(int(x)), a))
print(a)
['1', '2', '-3', '4', '-5']
[1, 2, 3, 4, 5]
In [ ]:
# Дан список чисел
numbers = [10, 2, 3, 55, 70, 8, 10, 1]

# функция, которая проверяет числа
def filter_odd_num(in_num):
    if(in_num % 2) == 0:
        return True
    else:
        return False

# Применение filter() для удаления нечетных чисел
out_filter = filter(filter_odd_num, numbers)

print("Тип объекта out_filter: ", type(out_filter))
print("Отфильтрованный список: ", list(out_filter))
Тип объекта out_filter:  <class 'filter'>
Отфильтрованный список:  [10, 2, 70, 8, 10]

Упражнение 2: считайте список с клавиатуры. С помощью функции filter удалите из него те элементы, которые не являются квадратами однозначных натуральных чисел.

In [ ]:
 

Можно использовать map для обработки считанного списка. Считываем с помощью функции input() объект типа str, к нему применяем метод split() и получаем list, ко всем его элементам функция map применяет функцию int, а конструктор списка list сохраняет результат в список:

In [ ]:
a = list(map(int, input().split()))
print(a)
1 2 3
[1, 2, 3]
In [ ]:
a = [list(map(int, input().split())) for i in range(5)]
2 3 1
12 1 1
1 3
4
5
In [ ]:
for x in a:
    print(*x)
2 3 1
12 1 1
1 3
4
5

Пример: удалить из матрицы строки с отрицательной суммой

In [ ]:
a = [[5, -2, 3], [-1, 2, -3], [-8, 4, 5]]
for x in a:
    print(*x)
5 -2 3
-1 2 -3
-8 4 5
In [ ]:
a = list(filter(lambda x: sum(x) >= 0, a))
print(a)
[[5, -2, 3], [-8, 4, 5]]
In [ ]:
print(sum(a[0]), sum(a[1]))
6 1

И еще примеры:

In [ ]:
a = [1, 2, 3, 4, 5]
print(list(map(lambda x: x ** 2, a)))
[1, 4, 9, 16, 25]
In [ ]:
a = [1, 2, -3, 4, -5]
print(list(map(lambda x: x if x >= 0 else 0, a)))
[1, 2, 0, 4, 0]

Цикл while. Целочисленная арифметика

Кроме цикла for в Python используется цикл while. Например, он удобен, если нужно указать условие выхода через условие типа bool

In [ ]:
i = 0
while i < 5:
    print(i ** 2)
    i += 1
0
1
4
9
16
In [ ]:
for i in range(5):
    print(i ** 2)
0
1
4
9
16

Еще две арифметические операции, которые потребуются в примере ниже: деление с остатком и целочисленное деление.

In [ ]:
print(16 % 3)
print(16 // 3)
1
5

С помощью цикла while удобно обращаться к отдельным цифрам числа. Например, можно сохранить их в массив:

In [ ]:
a = []
n = 2345
while n > 0:
    a.append(n % 10)
    n = n // 10
print(a)
[5, 4, 3, 2]

Упражнение 3: напишите функцию, которая вычисляет сумму цифр числа, считайте число с клавиатуры и примените эту функцию.

In [ ]:
 

Можно запустить "бесконечный цикл" (он ничем не отличается от обычного). Внутри циклов можно использовать конструкции break и continue:

In [ ]:
while True:
    a = int(input())
    print(a)
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_41472\3075097021.py in <module>
      1 while True:
----> 2     a = int(input())
      3     print(a)

~\anaconda\lib\site-packages\ipykernel\kernelbase.py in raw_input(self, prompt)
   1175                 "raw_input was called, but this frontend does not support input requests."
   1176             )
-> 1177         return self._input_request(
   1178             str(prompt),
   1179             self._parent_ident["shell"],

~\anaconda\lib\site-packages\ipykernel\kernelbase.py in _input_request(self, prompt, ident, parent, password)
   1217             except KeyboardInterrupt:
   1218                 # re-raise KeyboardInterrupt, to truncate traceback
-> 1219                 raise KeyboardInterrupt("Interrupted by user") from None
   1220             except Exception:
   1221                 self.log.warning("Invalid Message:", exc_info=True)

KeyboardInterrupt: Interrupted by user

Для прерывания работы ячейки можно нажать на квадратик.

In [ ]:
while True:
    a = int(input())
    if a > 0:
        print(a)
    else:
        break
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_41472\2473802587.py in <module>
      1 while True:
----> 2     a = int(input())
      3     if a > 0:
      4         print(a)
      5     else:

~\anaconda\lib\site-packages\ipykernel\kernelbase.py in raw_input(self, prompt)
   1175                 "raw_input was called, but this frontend does not support input requests."
   1176             )
-> 1177         return self._input_request(
   1178             str(prompt),
   1179             self._parent_ident["shell"],

~\anaconda\lib\site-packages\ipykernel\kernelbase.py in _input_request(self, prompt, ident, parent, password)
   1217             except KeyboardInterrupt:
   1218                 # re-raise KeyboardInterrupt, to truncate traceback
-> 1219                 raise KeyboardInterrupt("Interrupted by user") from None
   1220             except Exception:
   1221                 self.log.warning("Invalid Message:", exc_info=True)

KeyboardInterrupt: Interrupted by user
In [ ]:
while True:
    a = int(input())
    if a > 0:
        continue
    else:
        break

Генераторы и отложенные вычисления

In [ ]:
def foo(x):
    print(f"foo called for {x}")
    return x * 10

for x in map(foo, [1, 2, 3]):
    print(x)
In [ ]:
def foo(x):
    print(f"foo called for {x}")
    return x * 10

def boo(x):
    print(f"foo called for {x}")
    return x + 1000

A = [1, 2, 3]
B = map(foo, A)
C = map(boo, B)
print("Iterations start:")
for x in C:
    print(x)

Итерируемые объекты

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

In [ ]:
a, b = 1, 2
print(a)
print(b)
In [ ]:
a, b, c = [1, 2, 3]
print(a)
print(b)
print(c)
In [ ]:
a, b, c = [1, 2, 3, 4, 5]
In [ ]:
*a, b, c = [1, 2, 3, 4, 5]
print(a, b, c)
In [ ]:
a, *b, c = [1, 2, 3, 4, 5]
print(a, b, c)
In [ ]:
a, b, *c = [1, 2, 3, 4, 5]
print(a, b, c)
In [ ]:
for a, b, c in [(1, 2, 3), (4, 5, 6), (7, 8, 9)]:
    print(a, b, c)
In [ ]:
for a, *b in [(1, 2, 3, 4), (10, 20), (100, 200, 300)]:
    print(a, b)
In [ ]:
a, (b, c) = [1, (2, 3)]
print(a, b, c)

enumerate, zip, for с распаковкой

В цикле for также можно использовать распаковку:

In [ ]:
a = [[1, 2], [3, 4], [5, 6]]
for x, y in a:
    print(x, y, x + y)

Часто такая распаковка используется не сама по себе, а вместе с использованием удобных функций zip и enumerate, которые позволяют удобнее работать со списками:

In [ ]:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [0, 0, 0]]
for i, x in enumerate(a):
    print(i, x, sum(x))
In [ ]:
with open("1.txt", "w") as f:
    f.writelines([word + "\n" for word in ["dog", "cat", "mouse", "duck", "goose"]])
for line in open("1.txt"):
    print(line.strip())
In [ ]:
for i, x in enumerate(open("1.txt")):
    print(i, x.rstrip())
In [ ]:
a = ["ab", "cd", "ef"]
b = [1, 2, 3]
print(list(zip(a, b)))
In [ ]:
for a, b in zip("abcde", range(5)):
    print(a, b)
In [ ]:
for a, b in zip("abcde", range(10)):
    print(a, b)

Модуль itertools

Для более удобной работы с итерируемыми объектами мы можем использовать библиотеку itertools. Для этого нужно ее импортировать:

In [ ]:
import itertools as it
In [ ]:
for x, y, z in it.permutations("ABC"):
    print(x, y, z, sep='')
In [ ]:
for x, y, z in it.permutations("ABCD", 3):
    print(x, y, z, sep='', end=' ')

Дополнительно по itertoolsможно посмотреть: https://proglib.io/p/iteriruemsya-pravilno-20-priemov-ispolzovaniya-v-python-modulya-itertools-2020-01-03

Упражнение 4: Вычислите с помощью it.permutation $C_{12}^{5}$

In [ ]:
 
In [ ]:
for x, y in it.combinations("ABCDE", 2):
    print(x, y, sep='', end=' ')
In [ ]:
for x, y in it.combinations_with_replacement("ABCDE", 2):
    print(x, y, sep='', end=' ')
In [ ]:
for a in it.chain("мама", "мыла", "раму"):
    print(a, end=' ')
In [ ]:
for a in it.chain([5, 4, 3], [3, 4, 5], [[3, 4, 5]]):
    print(a, end=' ')
In [ ]:
for n, s in it.product("wxyz", "ABC"):
    print(n, s)

Упражнение 5: Введите 2 списка (координаты вектора a и координаты вектора b) и найдите скалярное произведение этих векторов, используя zip или что-нибудь из itertools.

In [ ]:
 

Упражнение 6: Студент составляет 4-буквенные слова из букв A, B, E, F, L, U. Каждую букву можно использовать не более одного раза, при этом в слове нельзя использовать более одной гласной. Сколько различных кодов может составить студент?При решении используйте itertools.

In [ ]:
 

Упражнение 7: Составьте все возможные пары из элементов строк '123456' и 'abcdef' такие, что первый элемент пары из числовой строки, а второй из текстовой. При решении используйте itertools.

In [ ]:
 

Упражнение 8: Если выписать все натуральные числа меньше 10, кратные 3 или 5, то получим 3, 5, 6 и 9. Сумма этих чисел равна 23.

Найдите сумму всех чисел меньше 1000, кратных 3 или 5. Предложите не менее 3-х вариантов решения.

In [ ]:
 

Упражнение 9: Сгенерируйте список используя чисел от -10 до 1000 с шагом 2. Выберите только элементы с чётными индексами. После этого используя функцию reduce и lambda-функцию найдите произведение элементов полученного списка.

In [ ]:
 

Упражнение 10: Сгенерируйте список используя чисел от -10 до 1000 с шагом 2. Выберите только элементы с нечётными индексами. После этого используя функцию map возведите в квдрат каждый элемент полученноо списка.

In [ ]: