In [ ]:
%%html
<style>
.text_cell_render * {
   font-family: OfficinaSansCTT;
}
.reveal code {
    font-family: OfficinaSansCTT;
}
.text_cell_render h3 {
   font-family: OfficinaSansCTT;
}
.reveal section img {
    max-height: 500px;
    margin-left: auto;
    margin-right: auto;
}
</style>

Вопросы по прошлому занятию

* Почему файлы лучше всего открывать через with?
* Зачем нужен Git?
* Как переместить файл из папки "/some/folder" в папку "/another/dir"?
* Зачем нужен subprocess.PIPE?
* Как вывести JSON-строку "красиво" c отступом в два пробела?
* Как сделать POST-запрос через requests?

In [ ]:

Разбор задачи 1

https://py.checkio.org/mission/non-unique-elements/

  • Как посчитать, сколько раз элемент встречается в списке?
  • Как отфильтровать список?

In [ ]:
from collections import Counter

def checkio(arr):
    counts = Counter(arr)
    return [
        w for w in arr if counts[w] > 1
    ]

In [ ]:

Разбор задачи 2

https://py.checkio.org/mission/absolute-sorting/

  • Как вообще отсортировать список?
  • Какой аргумент поможет брать при сортировке абсолютное значение?

In [ ]:
def checkio(nums):
    return sorted(nums, key=abs)

In [ ]:

Разбор задачи 3

https://py.checkio.org/mission/x-o-referee/

  • Можно сравнивать более двух элементов за раз:
    • if a == b == c != d

In [ ]:
def checkio(board):
    for i in range(3):
        if board[i][0] == board[i][1] == board[i][2] != ".":
            return board[i][0]
        if board[0][i] == board[1][i] == board[2][i] != ".":
            return board[0][i]
        
    if board[0][0] == board[1][1] == board[2][2] != ".":
        return board[0][0]
    if board[0][2] == board[1][1] == board[2][0] != ".":
        return board[0][2]
    return "D"

Регулярные выражения

  • Зачем они нужны?

In [ ]:
import re

re.match(r"^\+7\d{10}$", "+78005553535").group(0)  # проверить, соответствует ли строка целиком
a = "Пишите мне на адрес админ@суперхакер.рф или vasya@superhacker.me! Чмоки!"
re.search(r"\w+@\w+\.\w{2,5}", a).group(0)   # найти первое вхождение
re.findall(r"\w+@\w+\.\w{2,5}", a)  # найти все вхождения

my_re = re.compile(r"ya_regulyarko")  # позволит делать my_re.match(s), my_re.search(s), etc.

# Можно задавать диапазон: r"[A-Za-z]"

In [ ]:

Хороший сайт для практики

https://regexr.com/

Опять парсинг

Загрузите в переменную содержимое файла test.html и найдите в нем все уникальные имена, не используя bs4. Именем считаются любые два слова, оба начинающиеся с большой буквы и разделенные пробелом.

Как в регулярном выражении обозначается пробел?


In [ ]:

Итераторы и генераторы


In [ ]:
b = [
    a * 10 for a in range(10)
    if a % 2 == 0
]
b

In [ ]:
c = (
    a * 10 for a in range(10)
    if a % 2 == 0
)
c

In [ ]:
for num in c:
    print(num)
  • "Итератор" - это объект с интерфейсом перебора
  • "Генератор" - это итератор, в котором при переборе запускается дополнительный код

In [ ]:
a = [5, 6, 7, 8, 9, 10]
b = [3, 2, 1, 0, -1, -2]

def pow2(num):
    return num ** 2

def divisible_by_3(num):
    return num % 3 == 0

map(pow2, a) # “лениво” применить funс ко всем элементам списка
zip(a, b) # брать попарно элементы из двух списков
# а что вернет zip(a[::2], a[1::2])?
filter(divisible_by_3, a) # брать только те элементы, для которых func вернет True

In [ ]:

Безымянные функции


In [ ]:
# короткие безымянные функции
a = [5, 6, 7]
map(lambda num: num ** 2, a)
filter(lambda num: num % 3 == 0, a)

In [ ]:

Функция вызывается ТОЛЬКО на итерации цикла

  • Внутри Python на каждой итерации вызывает next(our_gen)

In [ ]:

Как такое написать?


In [ ]:
# то же самое, что 
# gen_squares = (i * i for i in range(n))

def gen_squares(n):
    for i in range(n):
        yield i * i

In [ ]:
mygen = gen_squares(5)

In [ ]:
next(mygen)

Числа Фибоначчи!


In [ ]:
def gen(n):
    if n <= 1:
        return 0
    elif n == 2:
        return 1
    
    a = 0
    b = 1
    for _ in range(2, n):
        a, b = b, a + b
        
    return b

In [ ]:


In [ ]:
# Минутка "вопросов на собеседовании" - а если через рекурсию?
# Ответ - красиво, но в этом случае так делать нельзя!

def gen2(n):
    if n <= 1:
        return 0
    elif n == 2:
        return 1
    return gen2(n - 2) + gen2(n - 1)

In [ ]:


In [ ]:
def gen():    
    a = 0
    b = 1
    yield a
    yield b
    while True:
        a, b = b, a + b
        yield b
  • "Генератор" - это функция с запоминанием последнего результата и несколькими точками входа-выхода

In [ ]:
# единственный способ получить значение из генератора - это проитерироваться по нему!
# "[1]" не поможет, но поможет next()
# можно явно привести к листу, но так делать не стоит - все окажется в памяти

a = gen()
for i, num in enumerate(a):
    if i == 5:
        print(num)
        break

In [ ]:

Пример пайплайна с генераторами


In [ ]:
from collections import Counter

with open("USlocalopendataportals.csv", "r") as testfile:
    recs = (l.split(",") for l in testfile)
    next(recs)  # пропускаем заголовок
    owners = (rec[3] for rec in recs)
    print(Counter(owners)["Government"])

Еще почитать про генераторы:

Еще об аргументах функций


In [ ]:
def my_function(*args, **kwargs):
    print(args)
    print(kwargs)
    
my_function(1, "foo", nick="Mushtandoid", arg=123)

In [ ]:

Передаем аргументы пачкой


In [ ]:
def summator(a, b):
    return a + b

a = [45, 78]
print(summator(*a))
print(summator(**{"a": 11, "b": 34}))

In [ ]:

Декораторы

  • Декоратор - это функция, которая принимает функцию и возвращает функцию

In [ ]:
import time

def my_function(a, b):
    time.sleep(2)
    return a + b

In [ ]:
def timer(func):
    def decoy_func(*args, **kwargs): 
        t = time.time()
        res = func(*args, **kwargs)
        print("Execution time: {0}".format(time.time() - t))
        return res
    return decoy_func

In [ ]:
@timer
def my_function(a, b):
    time.sleep(2)
    return a + b

my_function(5, 6)

Упражнение на декораторы

Напишите декоратор, который принимает функцию с одним аргументом и кэширует результат ее выполнения, то есть хранит словарь "аргумент->результат".


In [ ]:

Telegram!

  • Качаем, регистрируемся: https://telegram.org/
  • Главный бот для всех ботов: https://telegram.me/botfather
  • Любой бот требует токен для работы. Токен получается через главного бота.
  1. /start # начало диалога
  2. /newbot # создаем нового бота
  3. Набираем имя бота (латиницей)
  4. Набираем логин для бота (можно такой же, как имя, но должен заканчиваться на bot)
  5. Копируем себе токен

pip install telepot


In [ ]:
import telepot
bot = telepot.Bot('422088359:AAHgC2o92CHWrMfP8pRnsFAFcqy6epY5wuk')
bot.getMe()

Задача

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


In [ ]: