Занятие 2

Цель сегодняшнего семинара разобраться с некоторыми продвинутыми инструментами языка Python. А именно:

  1. Itertools
  2. Лямбда-функции
  3. Генераторы
  4. ООП

1. Itertools

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

  1. Бесконечные
  2. Конечные
  3. Комбинаторные

Мы можем проходиться по итератору с помощью:

  1. функции «next»
  2. конвертации в список с помощью list()
  3. цикла for

1.1 Itertools(бесконечные)

1. itertools.count(start=0, step=1)

Создаёт итератор, возвращающий равномерно распределенные значения, начиная с number start.

In [7]:
import itertools
c=itertools.count(5, 2)
print(next(c)) #выведем первый элемент
print(next(c)) #выведем второй элемент
print(next(c)) #выведем третий элемент
# можем делать так до бесконечности
5
7
9

2. itertools.cycle(iterable)

Создаёт итератор, возвращающий элементы из итерируемого объекта и сохраняющий копию каждого из них.

In [9]:
l = [1, 2, 3, 4] # создаём список
c=itertools.cycle(l)
print(next(c)) #выведем первый элемент
print(next(c)) #выведем второй элемент
print(next(c)) #выведем третий элемент
print(next(c)) #выведем четвёртый элемент
print(next(c)) #выведем следующий элемент(в данном случае первый)
# можем делать так до бесконечности
1
2
3
4
1

1.2 Itertools(конечные)

1. itertools.accumulate(iterable[, func, , initial=None])*

Создаёт итератор, возвращающий накопленные суммы, или накопленные результаты других бинарных функций(указываются через необязательный аргумент func).

In [14]:
l = itertools.accumulate([1,3,5,7,9])
print(list(l))
[1, 4, 9, 16, 25]

2. itertools.chain(iterables)*

In [15]:
l = itertools.chain(["a","b"],[1,2,3],"sfapr")
print(list(l))
['a', 'b', 1, 2, 3, 's', 'f', 'a', 'p', 'r']

Ещё больше о конечных итераторах и вообще об итераторах можно почитать здесь: https://docs.python.org/3/library/itertools.html

1.3 Itertools(комбинаторные)

1. itertools.product(iterables, repeat=1)*

Прямое или декартово произведе́ние двух непустых множеств — множество, элементами которого являются все возможные упорядоченные пары элементов исходных множеств.

Itertools.product реализует декартово произведение.

In [30]:
l = itertools.product("XY",[1,2])
print(list(l))
[('X', 1), ('X', 2), ('Y', 1), ('Y', 2)]

Задача 1

Пусть даны два множества. l1 = [1, 2, 3, 4, 5] и l2 = [2, 3, 4]. Найдите декартово произведение :

  1. l1 и l2
  2. пересечения(l1 и l2) и l2
  3. объединения(l1 и l2) и l1

Создайте свои два множества и найдите декартовы произведения(1-3) для них.

2. itertools.permutations(iterable, r=None)

Возвращает последовательные перестановки длины r в итерируемом объекте.

In [32]:
l = itertools.permutations('XYxy', r = 3)
print(list(l))
[('X', 'Y', 'x'), ('X', 'Y', 'y'), ('X', 'x', 'Y'), ('X', 'x', 'y'), ('X', 'y', 'Y'), ('X', 'y', 'x'), ('Y', 'X', 'x'), ('Y', 'X', 'y'), ('Y', 'x', 'X'), ('Y', 'x', 'y'), ('Y', 'y', 'X'), ('Y', 'y', 'x'), ('x', 'X', 'Y'), ('x', 'X', 'y'), ('x', 'Y', 'X'), ('x', 'Y', 'y'), ('x', 'y', 'X'), ('x', 'y', 'Y'), ('y', 'X', 'Y'), ('y', 'X', 'x'), ('y', 'Y', 'X'), ('y', 'Y', 'x'), ('y', 'x', 'X'), ('y', 'x', 'Y')]

Задача 2

Решите, используя permutations. Сколько существует различных трёхзначных чисел, в записи которых первая цифра больше третьей?

In [ ]:

3. itertools.combinations(iterable, r)

Возвращает подпоследовательности длины r из элементов итерируемого объекта, подаваемого на вход. Посмотрите пример, представленный далее. Поймите различия между combinations и permutations.

In [39]:
import itertools
l = itertools.combinations('12XY', r = 3)
print(list(l))
[('1', '2', 'X'), ('1', '2', 'Y'), ('1', 'X', 'Y'), ('2', 'X', 'Y')]

Задача 3

Решите, используя combinations. В хоровом кружке занимаются 15 человек. Необходимо выбрать трёх певцов. Сколькими способами это можно сделать?

In [ ]:

Задача 4

Решите, используя combinations. Сколько различных четырёхзначных чисел, делящихся на 3, можно составить из цифр 1, 2, 3, 4, если цифры в записи могут повторяться?

In [ ]:

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

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

In [40]:
def square(x):
    return x * x
print(square(6))
36
In [51]:
square_lamda = lambda x: x * x
print(square_lamda(6))
36

Лямбда-функции, порой, используют вместе с такими встроенными функциями как filter(), map(), reduce(), min(), max().

In [47]:
# Выводим все числа из спика кратные 3
first_l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l_with_3 = list(filter(lambda x: (x%3 == 0), first_l))
print(l_with_3)
[3, 6, 9]
In [49]:
# Возведём в квадрат каждое число списка
second_l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l_with_square = list(map(lambda x: x * x , second_l))
print(l_with_square)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Лямбда-функция может принимать несколько переменных на вход.

In [50]:
my_sum = lambda x, y: x + y
print(my_sum(3, 4))
7

Лямбда-функции и условные операторы

Следующий код выводит минимальное среди двух чисел:

In [54]:
min_number = lambda a, b : a if a > b else a
print(min_number(1, 5))
1

Всё-таки Лямбда-функции не стоит использовать с условными оператороми. В классическом виде код выглядит намного адекватнее.

In [1]:
def min_number(a, b):
    if a > b:
        return b
    else:
        return a
    
print(min_number(1, 5))
1

Лямбда-функции можно использовать с обычными функциями.

In [2]:
def my_mul(n):
    return lambda x : x * n

result = my_mul(2)
print(result(15))
30

Задача 5

Напишите лямбда-функцию, которая прибавляет 3 к заданному числу, переданному в качестве аргумента.

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

In [ ]:

Задача 6

In [6]:
l = [1, 3, 7, 4, 12, 8, 9, 0, 3, 3, 5, 6, 10]

Напишите лямбда-функцию, которая фильтрует список чисел, оставляя лишь чётные числа.

Подсчитайте их число.

In [8]:
l = ['a', 'e', 'f', 'g', 'p', 'b', 'i', 'y', 'u', 'd']

Напишите лямбда-функцию, которая фильтрует список символов, оставляя лишь буквы идущее в алфавите после j.

P.S подумайте, как работает сравнение символов в Python.

In [ ]:

Задача 7

Напишите лямбда-функцию, проверяющую - является ли исходное слово палиндромом.

In [ ]:

3. Генераторы

Генератор это подвид итерируемых объектов, таких как список или кортеж. Он генерирует для нас последовательность значений, которую мы можем перебрать. Однако их нельзя индексировать, т.е "пройтись" по генератору мы можем один раз. Для создания генератора в Python внутри функции вместо ключевого слова return используется ключевое слово yield.

In [10]:
def generate_ints(N):
    for i in range(N):
        yield i
In [13]:
gen = generate_ints(5)
print(next(gen))
print(next(gen))
0
1

Или же мы могли сделать так:

In [17]:
for i in generate_ints(5):
    print(i)
0
1
2
3
4

В чём же различия между ключивыми словами return и yield.

Когда интерпретатор доходит до слова return, то выполнение функции полностью прекращается.

Когда он доходит до ключевого слова yield, программа приостанавливает выполнение функции и возвращает значение в итерируемый объект. Затем интерпретатор возвращается к генератору, чтобы повторить процесс для нового значения. Кроме того, при прекращении выполнения функции ее локальные переменные стираются. С генераторами не так.

In [24]:
# функция
def fibonacci(n):
    fib1, fib2 = 0, 1
    for i in range(n):
        fib1, fib2 = fib2, fib1 + fib2
        return fib1
print(fibonacci(10))
1
In [25]:
# генератор
def fibonacci(n):
    fib1, fib2 = 0, 1
    for i in range(n):
        fib1, fib2 = fib2, fib1 + fib2
        yield fib1
print(*fibonacci(10))
1 1 2 3 5 8 13 21 34 55

Задача 8

Напишите генератор factorials(n), генерирующий последовательность факториалов натуральных чисел.

In [ ]:

Задача 9

Напишите генератор binomial_coefficients(n), генерирующий последовательность биномиальных коэффициентов $\binom{n}{0}$, $\binom{n}{1}$, ...

Используйте itertools.

In [ ]:

ООП

Подробнее об ООП можно почитать тут : https://smartiqa.ru/courses/python/lesson-6?ysclid=lnx08m5h3a397589593.

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

Создание класса

Объявить класс можно следующим образом:

class Example: 
    body

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

In [26]:
class Human:
    default = "human"
    def __init__(self, age):
        self.age = age
In [28]:
# не создавали экземпляр
Human.default
Out[28]:
'human'
In [31]:
# создали экземпляр
h = Human(25)
h.age
Out[31]:
25

Отметим, что можно легко менять атрибуты экземпляра класса.

Методы класса

Метод – это функция внутри класса. Все методы можно разделить на 2 группы:

  1. Встроенные(служебные) методы(атрибуты)
  2. Пользовательские методы(атрибуты)

Встроенные атрибуты

Ими являются:

  1. new(cls[, ...]) - Конструктор. Создает экземпляр(объект) класса. Сам класс передается в качестве аргумента.
  2. init(self[, ...]) - Инициализатор. Принимает свежесозданный объект класса из конструктора.
  3. del(self) - Деструктор. Вызывается при удалении объекта сборщиком мусора.
  4. str(self) - Возвращает строковое представление объекта.
  5. hash(self) - Возвращает хэш-сумму объекта.
  6. dict - Словарь, в котором хранится пространство имен класса.

Пользовательские атрибуты

Это атрибуты, которые составляют основной функционал класса. Пользовательские атрибуты пишутся программистом во время реализации класса.

Уровни доступа атрибутов в Python

  1. Private. Приватные члены класса недоступны извне - с ними можно работать только внутри класса.
  2. Public. Публичные методы наоборот - открыты для работы снаружи и, как правило, объявляются публичными сразу по-умолчанию.
  3. Protected. Доступ к защищенным ресурсам класса возможен только внутри этого класса и также внутри унаследованных от него классов (иными словами, внутри классов-потомков). Больше никто доступа к ним не имеет.
  1. Если переменная/метод начинается с одного нижнего подчеркивания (_protected_example), то она/он считается защищенным (protected).
  2. Если переменная/метод начинается с двух нижних подчеркиваний (__private_example), то она/он считается приватным (private).
In [44]:
# Создаём класс книга
class Book:

    def __init__(self, title, author, price):
        # Объявляем приватное поле title и author
        self.__title = title
        self.__author = author
        # Объявляем публичное поле
        self.price = price
    
    # метод узнать цену книги
    def what_is_the_price(self):
        print('price: ', self.price)
        
# создаём экземпляр класса
book1 = Book('War and Peace', 'Tolstoy L. N.', 1200)
# узнаём цену книги
book1.what_is_the_price()
price:  1200

Наследование в Python

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

In [49]:
# родительский класс
class Book:

    def __init__(self, title, author, price):
        # Объявляем приватное поле title и author
        self.__title = title
        self.__author = author
        # Объявляем публичное поле
        self.price = price
    
    # метод узнать цену книги
    def what_is_the_price(self):
        print('price: ', self.price)
        
# унаследованный класс(электронная книга)

class eBook(Book):
    # Добавляем новое свойство качество
    def __init__(self, quality):
        super().__init__()
        self.quality = quality
    
    # 
    def what_is_the_quality(self):
        super().__init__()
        print('price: ', self.quality)

Что это за метод super?

Главная задача этого метода - дать возможность наследнику обратиться к родительскому классу. В классе родителе Book свой инициализатор, и когда в потомке eBook мы так же создаем инициализатор, то мы его перегружаем. Иными словами, мы заменяем родительский метод init() собственным одноименным методом. Это чревато тем, что родительский метод просто в принципе не будет вызван, и мы потеряем его функционал в классе наследнике.

Решается эта проблема так - внутри инициализатора класса-наследника вызвать инициализатор родителя(для этого вызываем метод super().init())

А затем просто добавить новый функционал!

Задача 10(3 балла)

Создайте класс Alphabet.

1.1 Создайте метод init(), внутри которого будут определены три динамических параметра(свойства): 1) lang - язык и 2) letters_vowel - список гласных букв и 3) letters_consonant - список согласных букв.

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

1.2 Создайте методы print_vowel() и print_consonant(), который выведет в консоль гласные и согласные буквы алфавита.

1.3 Создайте методы vowel_num() и consonant_num(), который вернет количество гласных и согласных букв в алфавите.

1.4 Создайте класс RusAlphabet путем наследования от класса Alphabet.

1.5 Создайте метод init(), внутри которого будет вызываться родительский метод init(). В качестве параметров ему будут передаваться обозначение языка(например, 'Rus') и строка, состоящая из всех букв алфавита.

1.6 Создайте метод is_rus_letter(), который будет принимать букву в качестве параметра и определять, относится ли эта буква к английскому алфавиту.

1.7 Создайте статический метод example(), который будет возвращать пример текста на русском языке.

1.8 Создайте объект класса RusAlphabet, выведите буквы для этого алфавита, число гласных и согласных букв.

1.9 Выведите пример текста на русском языке, используя example().

1.10 Проверьте принадлежность нескольких букв к Алфавиту.

In [ ]:

Задача 11(3 балла)

Создайте класс, представляющий структуру данных стека. Включите методы:

  1. добавление элемента (иначе проталкивание, push)
  2. удаление элемента (pop)
  3. чтение головного элемента (peek)
  4. вычисления размера стека
  5. проверка стека на пустоту
In [ ]:

Задача 12(3 балла)

Реализуёте мини-книжный магазин. У книги есть название (title), цена (price), жанр (genre). Покупатель покупает книги, например передаёт список необходимых книг магазину, а магазин выставляет счёт. Магазин имеет скидки.

  1. за две разных книжки 5%
  2. за пять разных книг 10%
  3. скидки не суммируются, например, 11 разных книг - 10% на все.

Реализуйте функцию, которая на вход получает список книг, а на выходе выдаёт цену и название книг или говорит о том, что книг нет в наличие. Используёте ООП, создав необходимый класс или классы.

In [ ]: