diff --git a/.gitignore b/.gitignore index 380a213..8ea1dd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /talks/*/.ipynb_checkpoints +/live/*/.venv/ __pycache__ venv .idea +.DS_Store diff --git a/hackathons/BaseGame.py b/hackathons/BaseGame.py new file mode 100644 index 0000000..83a2cb5 --- /dev/null +++ b/hackathons/BaseGame.py @@ -0,0 +1,26 @@ +from abc import ABCMeta, abstractmethod +from collections import defaultdict + +class Scorer: + def __init__(self): + self._data = defaultdict(list) + + def add_scores(self, name, point): + self._data[name].append(point) + + def get_points(self, name): + return self._data[name] + + +class BaseGame(metaclass=ABCMeta): + def __init__(self, name, scorer): + self.__name = name + self.__scorer = scorer + + def add_scores(self, point): + self.__scorer.add_scores(self.__name, point) + + + @abstractmethod + def run(self): + pass diff --git a/hackathons/README.md b/hackathons/README.md new file mode 100644 index 0000000..65bbf62 --- /dev/null +++ b/hackathons/README.md @@ -0,0 +1,8 @@ +Заливать игры в папку games +=============== + +Вам нужно отнаследовать класс вашей игры от класса BaseGame, лежащего в py-файлике BaseGame, а класс хранилища от Storage +В BaseGame нужно реализовать метод run, запускающий вашу игру +Чтобы в течении игры писать ваши очки в хранилище, нужно вызывать у игры метод add_scores(self, point), где point - число очков +Так же в файле(не в классе) должна быть константа NAME с именем вашей игры + diff --git a/hackathons/bot/README.md b/hackathons/bot/README.md new file mode 100644 index 0000000..cd178b8 --- /dev/null +++ b/hackathons/bot/README.md @@ -0,0 +1,36 @@ +Бот для Slack +============= + +Это код простейшего бота для слака, которого нужного научить обрабатывать различные команды. + +Чтобы добавить свой обработчик, нужно добавить свой файл в `command_handler/`, +зарегистрировать класс в пуле соответствующим декоратором, +а также добавить в bot.py импорт соответствующего хендлера. + +Идеи команд +----------- + +* Калькулятор с поддержкой простейших арфиметических операций и скобок. +* Бросатель кубиков в днд-нотацции: 2d20, 3d6. Можно объединить с калькулятором. +* Поиска ответа по StackOverflow. +* Любой другой поиска, например, dndbeyond.com. +* Крестики-нолики: сделать ход, сбросить партию, показать статистику матчей*. +* Погода в указанном городе. +* Сохранить сообщение, выдать последнее сохраненное, выдать случайное сохраненное. +* Задать вопрос из базы, похвалить ответившего правильно, показать лучших трех отвечавших*. +* Информация о музыкальных группах из инфокарт с Википедии. +* Игра "виселица": запустить, принять ответ, перезапустить. Можно сделать поддержку нескольких партий одновременно. +* Работа с системой: ls, cat, man. Вывести первые N символов, по отдельной команде следующие N. + +`*` чтобы узнать имя пользователя, понадобятся изменения в клиенте. + +Системные задачи +---------------- + +* Автоматически импортировать все хендлеры из `command_handler/`. +* Подгружать новые хендлеры из `command_handler/` без перезапуска клиента (если они появились или обновлись). +* Расширить протокол общения с хендлером. Посылать в него дополнительные данные о сообщении. + Разрешить ему говорить "не обрабатывай эту команду другими хендлерами", + возвращать массив сообщений (в том числе пустой), возвращать колбек и т. д. Обратную совместимость нужно сохранить. +* Банить хендлеры после N ошибок. Если реализована динамическая подгрузка, то надо разбанивать при обновлении. +* Запускать хендлеры в тредах. diff --git a/hackathons/bot/bot.py b/hackathons/bot/bot.py new file mode 100644 index 0000000..ae6f82d --- /dev/null +++ b/hackathons/bot/bot.py @@ -0,0 +1,70 @@ +import os +import time +import re +from slackclient import SlackClient +# from command_handler.message import MessageCommandHandler +# import command_handler.message +from command_pool import CommandPool +# import command_handler.sample +# import command_handler.calories_calculator +# import command_handler.stackoverflow +# import command_handler.weather +# import command_handler.roll +dir_to_import = os.path.dirname(os.path.realpath(__file__)) + '/command_handler/' +all_files = os.listdir(dir_to_import) +for name in all_files: + if name.endswith('.py') and not name.startswith('_'): + __import__(name.split('.')[0]) + + +# instantiate Slack client +slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN')) +# starterbot's user ID in Slack: value is assigned after the bot starts up +starterbot_id = None + +# constants +RTM_READ_DELAY = 1 # 1 second delay between reading from RTM +EXAMPLE_COMMAND = "do" +MENTION_REGEX = "^<@(|[WU].+?)>(.*)" + + +def parse_bot_commands(slack_events): + for event in slack_events: + if event["type"] == "message" and not "subtype" in event: + user_id, message = parse_direct_mention(event["text"]) + if user_id == starterbot_id: + return message, event["channel"] + return None, None + + +def parse_direct_mention(message_text): + matches = re.search(MENTION_REGEX, message_text) + return (matches.group(1), matches.group(2).strip()) if matches else (None, None) + + +def handle_command(command_pool, command_text): + result = command_pool.handle(command_text) + + if result: + slack_client.api_call( + "chat.postMessage", + channel=channel, + text=result, + ) + + +if __name__ == "__main__": + command_pool = CommandPool() + + if slack_client.rtm_connect(with_team_state=False): + print("Starter Bot connected and running!") + # Read bot's user ID by calling Web API method `auth.test` + starterbot_id = slack_client.api_call("auth.test")["user_id"] + while True: + command_text, channel = parse_bot_commands(slack_client.rtm_read()) + if command_text: + handle_command(command_pool, command_text) + time.sleep(RTM_READ_DELAY) + else: + print("Connection failed. Exception traceback printed above.") + pass diff --git a/hackathons/bot/command_handler/__init__.py b/hackathons/bot/command_handler/__init__.py new file mode 100644 index 0000000..42fd3b9 --- /dev/null +++ b/hackathons/bot/command_handler/__init__.py @@ -0,0 +1,7 @@ +from abc import abstractmethod, ABCMeta + + +class CommandHandler(metaclass=ABCMeta): + @abstractmethod + def handle(self, command_text): + pass diff --git a/hackathons/bot/command_handler/calc.py b/hackathons/bot/command_handler/calc.py new file mode 100644 index 0000000..6c7ea93 --- /dev/null +++ b/hackathons/bot/command_handler/calc.py @@ -0,0 +1,46 @@ +from command_pool import CommandPool +from command_handler import CommandHandler + + +@CommandPool.register_command_class +class CalcCommandHandler(CommandHandler): + def handle(self, text): + command_name = 'calc ' + if text.startswith(command_name): + expression = text[len(command_name):].replace(' ', '') + priorities = {'*': 1, '/': 1, '-': 0, '+': 0} + element = '0' + operands = [] + operators = [] + + def calculate(): + operator = operators.pop() + right_operand = operands.pop() + left_operand = operands.pop() + if operator == '+': + result = left_operand + right_operand + elif operator == '-': + result = left_operand - right_operand + elif operator == '*': + result = left_operand * right_operand + elif operator == '/': + result = left_operand / right_operand + operands.append(result) + + for symbol in expression: + if symbol in r'+-*()/': + if element != '0': + operands.append(float(element)) + if (len(operators) > 0) and (priorities[operators[-1]] > priorities[symbol]): + calculate() + element = '0' + operators.append(symbol) + elif ('0' <= symbol <= '9') or (symbol == '.'): + element += symbol + else: + raise SyntaxError + if element != '0': + operands.append(float(element)) + while(len(operators) > 0): + calculate() + return operands[0] diff --git a/hackathons/bot/command_handler/calories_calculator.py b/hackathons/bot/command_handler/calories_calculator.py new file mode 100644 index 0000000..f37a09a --- /dev/null +++ b/hackathons/bot/command_handler/calories_calculator.py @@ -0,0 +1,48 @@ +import json +import re +from command_pool import CommandPool +from command_handler import CommandHandler + + +@CommandPool.register_command_class +class CaloriesCalculator(CommandHandler): + def __init__(self): + self.food_base = self.load_data() + + def handle(self, text): + if text.startswith("calories_calc "): + text = text.replace("calories_calc ", "").strip() + commands = text.split(';') + pattern = re.compile(r"(?P[А-Яа-я ]*) ?-? ?(?P[\d,]*)") + for command in commands: + command_dict = pattern.match(command).groupdict() + commands[commands.index(command)] = command_dict + not_found_food = [] + found_food = {} + for command in commands: + params = self.food_base.get(command['name'].strip()) + if params: + if command['weight']: + found_food[command['name']] = {name: float(value) * float(command['weight'])/100 for name, value in params.items()} + else: + found_food[command['name']] = params + else: + not_found_food.append(command['name']) + results = [] + if not_found_food: + for not_found in not_found_food: + results.append("Food '{}' is not found in base".format(not_found.strip())) + if found_food: + results.append("{:^30}|{:^10}|{:^10}|{:^10}|{:^10}|{:^10}" + .format('Продукт', 'Вода', 'Белки', 'Жиры', 'Углеводы', 'Калории')) + results.append("_"*90) + for name, parameter in found_food.items(): + results.append("{:^30}|{:^10.2f}|{:^10.2f}|{:^10.2f}|{:^10.2f}|{:^10.2f}" + .format(name, parameter['water'], parameter['proteins'], parameter['fats'], parameter['carbohydrates'], parameter['kcal'])) + results.append("_" * 90) + return '```' + '\n'.join(results) + '```' + + @staticmethod + def load_data(): + with open("food_base.json", "r", encoding="utf8") as fb: + return json.loads(fb.read()) diff --git a/hackathons/bot/command_handler/message.py b/hackathons/bot/command_handler/message.py new file mode 100644 index 0000000..8ee155a --- /dev/null +++ b/hackathons/bot/command_handler/message.py @@ -0,0 +1,28 @@ +from command_pool import CommandPool +from command_handler import CommandHandler +from random import randint + + +@CommandPool.register_command_class +class MessageCommandHandler(CommandHandler): + def __init__(self): + self.messages = [] + + def handle(self, text, rand_func=None): + if rand_func is None: + rand_func = randint + if not self.messages and (text.startswith('messages get') or text.startswith('messages random')): + return 'Сообщений нет' + if text.startswith('messages start '): + self.messages.append(text[15:]) + elif text.startswith('messages get'): + result = self.messages[-1] + del (self.messages[-1]) + return result + elif text.startswith('messages random'): + index = rand_func(0, len(self.messages) - 1) + result = self.messages[index] + return result + + if text.startswith('@'): + raise RuntimeError(text) diff --git a/hackathons/bot/command_handler/roll.py b/hackathons/bot/command_handler/roll.py new file mode 100644 index 0000000..fa1403b --- /dev/null +++ b/hackathons/bot/command_handler/roll.py @@ -0,0 +1,20 @@ +from command_pool import CommandPool +from command_handler import CommandHandler +import random + +@CommandPool.register_command_class +class RollHandler(CommandHandler): + def handle(self, text): + if text.startswith('roll'): + new_text=text.strip('roll ') + args = new_text.split('d') + count = int(args[0]) + edges = int(args[1]) + result = [] + + for i in range(count): + result.append(random.randint(1, edges)) + return '{} ({})'.format(sum(result), result) + + if text.startswith('@'): + raise RuntimeError(text) \ No newline at end of file diff --git a/hackathons/bot/command_handler/sample.py b/hackathons/bot/command_handler/sample.py new file mode 100644 index 0000000..7f003a1 --- /dev/null +++ b/hackathons/bot/command_handler/sample.py @@ -0,0 +1,12 @@ +from command_pool import CommandPool +from command_handler import CommandHandler + + +@CommandPool.register_command_class +class SampleCommandHandler(CommandHandler): + def handle(self, text): + if text.startswith('_'): + return text + + if text.startswith('@'): + raise RuntimeError(text) diff --git a/hackathons/bot/command_handler/stackoverflow.py b/hackathons/bot/command_handler/stackoverflow.py new file mode 100644 index 0000000..6bd3f6e --- /dev/null +++ b/hackathons/bot/command_handler/stackoverflow.py @@ -0,0 +1,31 @@ +from command_pool import CommandPool +from command_handler import CommandHandler +from bs4 import BeautifulSoup +import requests + + + +@CommandPool.register_command_class +class StackOverFlow(CommandHandler): + def handle(self, text): + if text.startswith('Stack '): + ask = text[6:].replace(' ', '+') + base_url = 'https://ru.stackoverflow.com/search?q={}'.format(ask) + html = requests.get(base_url).text + soup = BeautifulSoup(html, 'lxml') + answers = soup.find('div', id='mainbar').find_all('div', class_='question-summary') + result = 'Not found' + true_url = 'Not found' + for ans in answers: + if ans.find('div', class_='status answered-accepted'): + result = ans.find('div', class_='excerpt').text + result = ' '.join(result.split()) + url = ans.find('a') + true_url = 'https://ru.stackoverflow.com/' + url.get('href') + break + return str(result).strip() + '\n' + str(true_url) + +if __name__ == '__main__': + s = StackOverFlow() + result = s.handle('Stack python virtualenv') + print(result) diff --git a/hackathons/bot/command_handler/viselica.py b/hackathons/bot/command_handler/viselica.py new file mode 100644 index 0000000..c7762a3 --- /dev/null +++ b/hackathons/bot/command_handler/viselica.py @@ -0,0 +1,69 @@ +from command_pool import CommandPool +from command_handler import CommandHandler +import requests + +@CommandPool.register_command_class +class ViselicaCommandHandler(CommandHandler): + def __init__(self): + self.clear() + + def clear(self): + self.word = '' + self.commands = ['check', 'start'] + self.commands_dict = {'check': self.check, 'start': self.start} + self.is_started = False + self.result = [] + self.know = False + self.number_trying = 6 + + def handle(self, text): + print("adfsdf") + parsed_text = text.strip().split() + print(parsed_text) + if parsed_text[0] == 'viselica': + + if parsed_text[1] in self.commands: + print(self.commands_dict[parsed_text[1]](parsed_text)) + + def check(self, parsed_text): + if not self.is_started: + return "The game is not started" + if len(parsed_text[2]) == 1: + char = parsed_text[2] + self.know = False + for i in range(len(self.word)): + if self.word[i] == char: + self.know = True + self.result[i] = char + + if ''.join([*self.result]) == self.word and self.word != '': + self.clear() + return 'Congratulation!!! You win' + + if self.know: + return 'Success, ' + ' '.join([*self.result]) + else: + if self.number_trying == 0: + self.clear() + return 'Defeat!' + self.number_trying -= 1 + return 'Wrong!\nNumber of trying: {}'.format(self.number_trying) + else: + return 'Wrong! Only single char!' + + def start(self, parsed_text): + if self.is_started: + return 'The game is already started' + self.is_started = True + self.word = self.get_word() + self.result = ['_'] * len(self.word) + return 'The game is started, length: {}'.format(len(self.word)) + + def get_word(self): + response = requests.get('https://castlots.org/generator-slov/generate.php', headers={ + 'X-Requested-With': 'XMLHttpRequest', + }) + return response.json()['va'] + + + diff --git a/hackathons/bot/command_handler/weather.py b/hackathons/bot/command_handler/weather.py new file mode 100644 index 0000000..143032c --- /dev/null +++ b/hackathons/bot/command_handler/weather.py @@ -0,0 +1,49 @@ +from command_pool import CommandPool +from command_handler import CommandHandler + +import requests +import json +import urllib + +@CommandPool.register_command_class +class YahooWeatherForecast(CommandHandler): + def handle(self, city): + if not city.startswith('Weather'): + return None + + city = city[7:].strip() + + if city.startswith('id'): + response = requests.get( + 'https://api.vk.com/method/users.get?user_ids={}&fields=city&lang=en&v=5.74'.format(city.split()[1])) + + data = json.loads(response.text) + + city = data['response'][0]['city']['title'] + + url = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather." \ + "forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%" \ + "20text%3D%22{}%22)%20and%20u%20%3D%20%22c%22&format=json&env=store%3A%2F%2Fdatatables." \ + "org%2Falltableswithkeys".format(city) + data = requests.get(url).json() + try: + forecast_data = data['query']['results']['channel']['item']['forecast'] + except TypeError: + return 'Incorrect city name' + day_data = forecast_data[0] + forecast = { + 'City': city, + 'Date': day_data['date'], + 'Low_temp': day_data['low'], + 'High_temp': day_data['high'], + 'Comment': day_data['text'] + } + + forecast_string = 'Date: {0}\nLow temp.: {1}\nHigh temp.: {2}\nType: {3}\n'.format( + day_data['date'], + day_data['low'], + day_data['high'], + day_data['text'] + ) + + return forecast_string \ No newline at end of file diff --git a/hackathons/bot/command_pool.py b/hackathons/bot/command_pool.py new file mode 100644 index 0000000..401ade2 --- /dev/null +++ b/hackathons/bot/command_pool.py @@ -0,0 +1,22 @@ +import sys + +class CommandPool: + COMMAND_CLASSES = [] + + @classmethod + def register_command_class(cls, klass): + cls.COMMAND_CLASSES.append(klass) + return klass + + def __init__(self): + self._command_handlers = [klass() for klass in self.COMMAND_CLASSES] + + def handle(self, command_text): + for handler in self._command_handlers: + try: + result = handler.handle(command_text) + if result is not None: + return result + except Exception as e: + print(sys.exc_info()) + diff --git a/hackathons/bot/food_base.json b/hackathons/bot/food_base.json new file mode 100644 index 0000000..f98d3c2 --- /dev/null +++ b/hackathons/bot/food_base.json @@ -0,0 +1,1962 @@ +{ + "Брынза из коровьего молока": { + "water": 52.0, + "proteins": 17.9, + "fats": 20.1, + "carbohydrates": 0.0, + "kcal": 260.0 + }, + "Йогурт 1.5% жирности": { + "water": 88.0, + "proteins": 5.0, + "fats": 1.5, + "carbohydrates": 3.5, + "kcal": 51.0 + }, + "Кефир обезжиренный": { + "water": 91.4, + "proteins": 3.0, + "fats": 0.1, + "carbohydrates": 3.8, + "kcal": 30.0 + }, + "Кефир жирный": { + "water": 88.3, + "proteins": 2.8, + "fats": 3.2, + "carbohydrates": 4.1, + "kcal": 59.0 + }, + "Молоко": { + "water": 88.5, + "proteins": 2.8, + "fats": 3.2, + "carbohydrates": 4.7, + "kcal": 58.0 + }, + "Молоко сухое цельное": { + "water": 4.0, + "proteins": 25.6, + "fats": 25.0, + "carbohydrates": 39.4, + "kcal": 475.0 + }, + "Молоко сгущеное": { + "water": 74.1, + "proteins": 7.0, + "fats": 7.9, + "carbohydrates": 9.5, + "kcal": 135.0 + }, + "Простокваша": { + "water": 88.4, + "proteins": 2.8, + "fats": 3.2, + "carbohydrates": 4.1, + "kcal": 58.0 + }, + "Ряженка": { + "water": 85.3, + "proteins": 3.0, + "fats": 6.0, + "carbohydrates": 4.1, + "kcal": 85.0 + }, + "Сливки 10%": { + "water": 82.2, + "proteins": 3.0, + "fats": 10.0, + "carbohydrates": 4.0, + "kcal": 118.0 + }, + "Сливки 20%": { + "water": 72.9, + "proteins": 2.8, + "fats": 20.0, + "carbohydrates": 3.6, + "kcal": 205.0 + }, + "Сметана 10%": { + "water": 82.7, + "proteins": 3.0, + "fats": 10.0, + "carbohydrates": 2.9, + "kcal": 116.0 + }, + "Сметана 20%": { + "water": 72.7, + "proteins": 2.8, + "fats": 20.0, + "carbohydrates": 3.2, + "kcal": 206.0 + }, + "Сырки и масса творожная": { + "water": 41.0, + "proteins": 7.1, + "fats": 23.0, + "carbohydrates": 27.5, + "kcal": 340.0 + }, + "Сыр российский": { + "water": 40.0, + "proteins": 23.4, + "fats": 30.0, + "carbohydrates": 0.0, + "kcal": 371.0 + }, + "Сыр голландский": { + "water": 38.8, + "proteins": 26.8, + "fats": 27.3, + "carbohydrates": 0.0, + "kcal": 361.0 + }, + "Сыр швейцарский": { + "water": 36.4, + "proteins": 24.9, + "fats": 31.8, + "carbohydrates": 0.0, + "kcal": 396.0 + }, + "Сыр пошехонский": { + "water": 41.0, + "proteins": 26.0, + "fats": 26.5, + "carbohydrates": 0.0, + "kcal": 334.0 + }, + "Сыр плавленный": { + "water": 55.0, + "proteins": 24.0, + "fats": 13.5, + "carbohydrates": 0.0, + "kcal": 226.0 + }, + "Творог жирный": { + "water": 64.7, + "proteins": 14.0, + "fats": 18.0, + "carbohydrates": 1.3, + "kcal": 226.0 + }, + "Творог полужирный": { + "water": 71.0, + "proteins": 16.7, + "fats": 9.0, + "carbohydrates": 1.3, + "kcal": 156.0 + }, + "Творог обезжиренный": { + "water": 77.7, + "proteins": 18.0, + "fats": 0.6, + "carbohydrates": 1.5, + "kcal": 86.0 + }, + "Шпик свиной": { + "water": 5.7, + "proteins": 1.4, + "fats": 92.8, + "carbohydrates": 0.0, + "kcal": 816.0 + }, + "Маргарин молочный": { + "water": 15.9, + "proteins": 0.3, + "fats": 82.3, + "carbohydrates": 1.0, + "kcal": 746.0 + }, + "Маргарин бутербродный": { + "water": 15.8, + "proteins": 0.5, + "fats": 82.0, + "carbohydrates": 1.2, + "kcal": 744.0 + }, + "Майонез": { + "water": 25.0, + "proteins": 3.1, + "fats": 67.0, + "carbohydrates": 2.6, + "kcal": 627.0 + }, + "Масло оливковое": { + "water": 0.2, + "proteins": 0.0, + "fats": 99.8, + "carbohydrates": 0.0, + "kcal": 898.0 + }, + "Масло кукурузное": { + "water": 0.1, + "proteins": 0.0, + "fats": 99.9, + "carbohydrates": 0.0, + "kcal": 899.0 + }, + "Масло подсолнечное": { + "water": 0.1, + "proteins": 0.0, + "fats": 99.9, + "carbohydrates": 0.0, + "kcal": 899.0 + }, + "Масло сливочное": { + "water": 15.8, + "proteins": 0.6, + "fats": 82.5, + "carbohydrates": 0.9, + "kcal": 748.0 + }, + "Масло топленое": { + "water": 1.0, + "proteins": 0.3, + "fats": 98.0, + "carbohydrates": 0.6, + "kcal": 887.0 + }, + "Хлеб ржаной": { + "water": 42.4, + "proteins": 4.7, + "fats": 0.7, + "carbohydrates": 49.8, + "kcal": 214.0 + }, + "Хлеб пшеничный": { + "water": 34.3, + "proteins": 7.7, + "fats": 2.4, + "carbohydrates": 53.4, + "kcal": 254.0 + }, + "Сдобная выпечка": { + "water": 26.1, + "proteins": 7.6, + "fats": 4.5, + "carbohydrates": 60.0, + "kcal": 297.0 + }, + "Баранки": { + "water": 17.0, + "proteins": 10.4, + "fats": 1.3, + "carbohydrates": 68.7, + "kcal": 312.0 + }, + "Сушки": { + "water": 12.0, + "proteins": 11.0, + "fats": 1.3, + "carbohydrates": 73.0, + "kcal": 330.0 + }, + "Сухари пшеничные": { + "water": 12.0, + "proteins": 11.2, + "fats": 1.4, + "carbohydrates": 72.4, + "kcal": 331.0 + }, + "Сухари сливочные": { + "water": 8.0, + "proteins": 8.5, + "fats": 10.6, + "carbohydrates": 71.3, + "kcal": 397.0 + }, + "Мука пшеничная высш. сорта": { + "water": 14.0, + "proteins": 10.3, + "fats": 0.9, + "carbohydrates": 74.2, + "kcal": 327.0 + }, + "Мука пшеничная I сорт": { + "water": 14.0, + "proteins": 10.6, + "fats": 1.3, + "carbohydrates": 73.2, + "kcal": 329.0 + }, + "Мука пшеничная II сорт": { + "water": 14.0, + "proteins": 11.7, + "fats": 1.8, + "carbohydrates": 70.8, + "kcal": 328.0 + }, + "Мука ржаная": { + "water": 14.0, + "proteins": 6.9, + "fats": 1.1, + "carbohydrates": 76.9, + "kcal": 326.0 + }, + "Гречневая ядрица": { + "water": 14.0, + "proteins": 12.6, + "fats": 2.6, + "carbohydrates": 68.0, + "kcal": 329.0 + }, + "Гречневая продел": { + "water": 14.0, + "proteins": 9.5, + "fats": 1.9, + "carbohydrates": 72.2, + "kcal": 326.0 + }, + "Манная": { + "water": 14.0, + "proteins": 11.3, + "fats": 0.7, + "carbohydrates": 73.3, + "kcal": 326.0 + }, + "Овсяная": { + "water": 12.0, + "proteins": 11.9, + "fats": 5.8, + "carbohydrates": 65.4, + "kcal": 345.0 + }, + "Перловая": { + "water": 14.0, + "proteins": 9.3, + "fats": 1.1, + "carbohydrates": 73.7, + "kcal": 324.0 + }, + "Пшено": { + "water": 14.0, + "proteins": 12.0, + "fats": 2.9, + "carbohydrates": 69.3, + "kcal": 334.0 + }, + "Рисовая": { + "water": 14.0, + "proteins": 7.0, + "fats": 0.6, + "carbohydrates": 73.7, + "kcal": 323.0 + }, + "Пшеничная “Полтавская”": { + "water": 14.0, + "proteins": 12.7, + "fats": 1.1, + "carbohydrates": 70.6, + "kcal": 325.0 + }, + "Толокно": { + "water": 10.0, + "proteins": 12.2, + "fats": 5.8, + "carbohydrates": 68.3, + "kcal": 357.0 + }, + "Ячневая": { + "water": 14.0, + "proteins": 10.4, + "fats": 1.3, + "carbohydrates": 71.7, + "kcal": 322.0 + }, + "Геркулес": { + "water": 12.0, + "proteins": 13.1, + "fats": 6.2, + "carbohydrates": 65.7, + "kcal": 355.0 + }, + "Кукурузная": { + "water": 14.0, + "proteins": 8.3, + "fats": 1.2, + "carbohydrates": 75.0, + "kcal": 325.0 + }, + "Баклажаны": { + "water": 91.0, + "proteins": 0.6, + "fats": 0.1, + "carbohydrates": 5.5, + "kcal": 24.0 + }, + "Брюква": { + "water": 87.5, + "proteins": 1.2, + "fats": 0.1, + "carbohydrates": 8.1, + "kcal": 37.0 + }, + "Горошек зеленый": { + "water": 80.0, + "proteins": 5.0, + "fats": 0.2, + "carbohydrates": 13.3, + "kcal": 72.0 + }, + "Кабачки": { + "water": 93.0, + "proteins": 0.6, + "fats": 0.3, + "carbohydrates": 5.7, + "kcal": 27.0 + }, + "Капуста белокочанная": { + "water": 90.0, + "proteins": 1.8, + "fats": 0, + "carbohydrates": 5.4, + "kcal": 28.0 + }, + "Капуста краснокочанная": { + "water": 90.0, + "proteins": 1.8, + "fats": 0, + "carbohydrates": 6.1, + "kcal": 31.0 + }, + "Капуста цветная": { + "water": 90.9, + "proteins": 2.5, + "fats": 0, + "carbohydrates": 4.9, + "kcal": 29.0 + }, + "Картофель": { + "water": 76.0, + "proteins": 2.0, + "fats": 0.1, + "carbohydrates": 19.7, + "kcal": 83.0 + }, + "Лук зеленый (перо)": { + "water": 92.5, + "proteins": 1.3, + "fats": 0, + "carbohydrates": 4.3, + "kcal": 22.0 + }, + "Лук порей": { + "water": 87.0, + "proteins": 3.0, + "fats": 0, + "carbohydrates": 7.3, + "kcal": 40.0 + }, + "Лук репчатый": { + "water": 86.0, + "proteins": 1.7, + "fats": 0, + "carbohydrates": 9.5, + "kcal": 43.0 + }, + "Морковь красная": { + "water": 88.5, + "proteins": 1.3, + "fats": 0.1, + "carbohydrates": 7.0, + "kcal": 33.0 + }, + "Огурцы грунтовые": { + "water": 95.0, + "proteins": 0.8, + "fats": 0, + "carbohydrates": 3.0, + "kcal": 15.0 + }, + "Огурцы парниковые": { + "water": 96.5, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 1.8, + "kcal": 10.0 + }, + "Перец зеленый сладкий": { + "water": 92.0, + "proteins": 1.3, + "fats": 0, + "carbohydrates": 4.7, + "kcal": 23.0 + }, + "Перец красный сладкий": { + "water": 91.0, + "proteins": 1.3, + "fats": 0, + "carbohydrates": 5.7, + "kcal": 27.0 + }, + "Петрушка (зелень)": { + "water": 85.0, + "proteins": 3.7, + "fats": 0, + "carbohydrates": 8.1, + "kcal": 45.0 + }, + "Петрушка (корень)": { + "water": 85.0, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 11.0, + "kcal": 47.0 + }, + "Ревень (черешковый)": { + "water": 94.5, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 2.9, + "kcal": 16.0 + }, + "Редис": { + "water": 93.0, + "proteins": 1.2, + "fats": 0, + "carbohydrates": 4.1, + "kcal": 20.0 + }, + "Редька": { + "water": 88.6, + "proteins": 1.9, + "fats": 0, + "carbohydrates": 7.0, + "kcal": 34.0 + }, + "Репа": { + "water": 90.5, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 5.9, + "kcal": 28.0 + }, + "Салат": { + "water": 95.0, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 2.2, + "kcal": 14.0 + }, + "Свекла": { + "water": 86.5, + "proteins": 1.7, + "fats": 0, + "carbohydrates": 10.8, + "kcal": 48.0 + }, + "Томаты (грунтовые)": { + "water": 93.5, + "proteins": 0.6, + "fats": 0, + "carbohydrates": 4.2, + "kcal": 19.0 + }, + "Томаты (парниковые)": { + "water": 94.6, + "proteins": 0.6, + "fats": 0, + "carbohydrates": 2.9, + "kcal": 14.0 + }, + "Зеленая фасоль": { + "water": 90.0, + "proteins": 4.0, + "fats": 0, + "carbohydrates": 4.3, + "kcal": 32.0 + }, + "Хрен": { + "water": 77.0, + "proteins": 2.5, + "fats": 0, + "carbohydrates": 16.3, + "kcal": 71.0 + }, + "Черемша": { + "water": 89.0, + "proteins": 2.4, + "fats": 0, + "carbohydrates": 6.5, + "kcal": 34.0 + }, + "Чеснок": { + "water": 70.0, + "proteins": 6.5, + "fats": 0, + "carbohydrates": 21.2, + "kcal": 106.0 + }, + "Шпинат": { + "water": 91.2, + "proteins": 2.9, + "fats": 0, + "carbohydrates": 2.3, + "kcal": 21.0 + }, + "Щавель": { + "water": 90.0, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 5.3, + "kcal": 28.0 + }, + "Абрикосы": { + "water": 86.0, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 10.5, + "kcal": 46.0 + }, + "Айва": { + "water": 87.5, + "proteins": 0.6, + "fats": 0, + "carbohydrates": 8.9, + "kcal": 38.0 + }, + "Алыча": { + "water": 89.0, + "proteins": 0.2, + "fats": 0, + "carbohydrates": 7.4, + "kcal": 34.0 + }, + "Ананас": { + "water": 86.0, + "proteins": 0.4, + "fats": 0, + "carbohydrates": 11.8, + "kcal": 48.0 + }, + "Бананы": { + "water": 74.0, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 22.4, + "kcal": 91.0 + }, + "Вишня": { + "water": 18.0, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 73.0, + "kcal": 292.0 + }, + "Гранат": { + "water": 85.0, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 11.8, + "kcal": 52.0 + }, + "Груша": { + "water": 24.0, + "proteins": 2.3, + "fats": 0, + "carbohydrates": 62.1, + "kcal": 246.0 + }, + "Инжир": { + "water": 83.0, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 13.9, + "kcal": 56.0 + }, + "Кизил": { + "water": 85.0, + "proteins": 1.0, + "fats": 0, + "carbohydrates": 9.7, + "kcal": 45.0 + }, + "Персики": { + "water": 18.0, + "proteins": 3.0, + "fats": 0, + "carbohydrates": 68.5, + "kcal": 275.0 + }, + "Рябина садовая": { + "water": 81.0, + "proteins": 1.4, + "fats": 0, + "carbohydrates": 12.5, + "kcal": 58.0 + }, + "Рябина черноплодная": { + "water": 80.5, + "proteins": 1.5, + "fats": 0, + "carbohydrates": 12.0, + "kcal": 54.0 + }, + "Слива садовая": { + "water": 87.0, + "proteins": 0.8, + "fats": 0, + "carbohydrates": 9.9, + "kcal": 43.0 + }, + "Финики": { + "water": 20.0, + "proteins": 2.5, + "fats": 0, + "carbohydrates": 72.1, + "kcal": 281.0 + }, + "Хурма": { + "water": 81.5, + "proteins": 0.5, + "fats": 0, + "carbohydrates": 15.9, + "kcal": 62.0 + }, + "Авокадо": { + "water": 73.2, + "proteins": 2.0, + "fats": 14.7, + "carbohydrates": 8.5, + "kcal": 160.0 + }, + "Черешня": { + "water": 85.0, + "proteins": 1.1, + "fats": 0, + "carbohydrates": 12.3, + "kcal": 52.0 + }, + "Шелковица": { + "water": 82.7, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 12.7, + "kcal": 53.0 + }, + "Яблоки": { + "water": 20.0, + "proteins": 3.2, + "fats": 0, + "carbohydrates": 68.0, + "kcal": 273.0 + }, + "Апельсин": { + "water": 87.5, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 8.4, + "kcal": 38.0 + }, + "Грейпфрут": { + "water": 89.0, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 7.3, + "kcal": 35.0 + }, + "Лимон": { + "water": 87.7, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 3.6, + "kcal": 31.0 + }, + "Мандарин": { + "water": 88.5, + "proteins": 0.8, + "fats": 0, + "carbohydrates": 8.6, + "kcal": 38.0 + }, + "Брусника": { + "water": 87.0, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 8.6, + "kcal": 40.0 + }, + "Виноград": { + "water": 80.2, + "proteins": 0.4, + "fats": 0, + "carbohydrates": 17.5, + "kcal": 69.0 + }, + "Голубика": { + "water": 88.2, + "proteins": 1.0, + "fats": 0, + "carbohydrates": 7.7, + "kcal": 37.0 + }, + "Ежевика": { + "water": 88.0, + "proteins": 2.0, + "fats": 0, + "carbohydrates": 5.3, + "kcal": 33.0 + }, + "Земляника": { + "water": 84.5, + "proteins": 1.8, + "fats": 0, + "carbohydrates": 8.1, + "kcal": 41.0 + }, + "Клюква": { + "water": 89.5, + "proteins": 0.5, + "fats": 0, + "carbohydrates": 4.8, + "kcal": 28.0 + }, + "Крыжовник": { + "water": 85.0, + "proteins": 0.7, + "fats": 0, + "carbohydrates": 9.9, + "kcal": 44.0 + }, + "Малина": { + "water": 87.0, + "proteins": 0.8, + "fats": 0, + "carbohydrates": 9.0, + "kcal": 41.0 + }, + "Морошка": { + "water": 83.3, + "proteins": 0.8, + "fats": 0, + "carbohydrates": 6.8, + "kcal": 31.0 + }, + "Облепиха": { + "water": 75.0, + "proteins": 0.9, + "fats": 0, + "carbohydrates": 5.5, + "kcal": 30.0 + }, + "Смородина белая": { + "water": 86.0, + "proteins": 0.3, + "fats": 0, + "carbohydrates": 8.7, + "kcal": 39.0 + }, + "Смородина красная": { + "water": 85.4, + "proteins": 0.6, + "fats": 0, + "carbohydrates": 8.0, + "kcal": 38.0 + }, + "Смородина черная": { + "water": 85.0, + "proteins": 1.0, + "fats": 0, + "carbohydrates": 8.0, + "kcal": 40.0 + }, + "Черника": { + "water": 86.5, + "proteins": 1.1, + "fats": 0, + "carbohydrates": 8.6, + "kcal": 40.0 + }, + "Шиповник свежий": { + "water": 66.0, + "proteins": 1.6, + "fats": 0, + "carbohydrates": 24.0, + "kcal": 101.0 + }, + "Шиповник сушеный": { + "water": 14.0, + "proteins": 4.0, + "fats": 0, + "carbohydrates": 60.0, + "kcal": 253.0 + }, + "Урюк": { + "water": 18.0, + "proteins": 5.0, + "fats": 0, + "carbohydrates": 67.5, + "kcal": 278.0 + }, + "Курага": { + "water": 20.2, + "proteins": 5.2, + "fats": 0, + "carbohydrates": 65.9, + "kcal": 272.0 + }, + "Изюм с косточкой": { + "water": 19.0, + "proteins": 1.8, + "fats": 0, + "carbohydrates": 70.9, + "kcal": 276.0 + }, + "Изюм кишмиш": { + "water": 18.0, + "proteins": 2.3, + "fats": 0, + "carbohydrates": 71.2, + "kcal": 279.0 + }, + "Чернослив": { + "water": 25.0, + "proteins": 2.3, + "fats": 0, + "carbohydrates": 65.6, + "kcal": 264.0 + }, + "Фундук": { + "water": 4.8, + "proteins": 16.1, + "fats": 66.9, + "carbohydrates": 9.9, + "kcal": 704.0 + }, + "Миндаль": { + "water": 4.0, + "proteins": 18.6, + "fats": 57.7, + "carbohydrates": 13.6, + "kcal": 645.0 + }, + "Грецкий орех": { + "water": 5.0, + "proteins": 13.8, + "fats": 61.3, + "carbohydrates": 10.2, + "kcal": 648.0 + }, + "Арахис": { + "water": 10.0, + "proteins": 26.3, + "fats": 45.2, + "carbohydrates": 9.7, + "kcal": 548.0 + }, + "Семя подсолнечника": { + "water": 8.0, + "proteins": 20.7, + "fats": 52.9, + "carbohydrates": 5.0, + "kcal": 578.0 + }, + "Кедровые орехи": { + "water": 2.3, + "proteins": 13.7, + "fats": 68.4, + "carbohydrates": 13.1, + "kcal": 673.0 + }, + "Фисташки, без соли": { + "water": 2.0, + "proteins": 21.4, + "fats": 46.0, + "carbohydrates": 27.7, + "kcal": 571.0 + }, + "Баранина": { + "water": 67.6, + "proteins": 16.3, + "fats": 15.3, + "carbohydrates": 0.0, + "kcal": 203.0 + }, + "Говядина": { + "water": 67.7, + "proteins": 18.9, + "fats": 12.4, + "carbohydrates": 0.0, + "kcal": 187.0 + }, + "Конина": { + "water": 72.5, + "proteins": 20.2, + "fats": 7.0, + "carbohydrates": 0.0, + "kcal": 143.0 + }, + "Кролик": { + "water": 65.3, + "proteins": 20.7, + "fats": 12.9, + "carbohydrates": 0.0, + "kcal": 199.0 + }, + "Свинина нежирная": { + "water": 54.8, + "proteins": 16.4, + "fats": 27.8, + "carbohydrates": 0.0, + "kcal": 316.0 + }, + "Свинина жирная": { + "water": 38.7, + "proteins": 11.4, + "fats": 49.3, + "carbohydrates": 0.0, + "kcal": 489.0 + }, + "Телятина": { + "water": 78.0, + "proteins": 19.7, + "fats": 1.2, + "carbohydrates": 0.0, + "kcal": 90.0 + }, + "Бараньи Почки": { + "water": 79.7, + "proteins": 13.6, + "fats": 2.5, + "carbohydrates": 0.0, + "kcal": 77.0 + }, + "Баранья Печень": { + "water": 71.2, + "proteins": 18.7, + "fats": 2.9, + "carbohydrates": 0.0, + "kcal": 101.0 + }, + "Баранье Сердце": { + "water": 78.5, + "proteins": 13.5, + "fats": 2.5, + "carbohydrates": 0.0, + "kcal": 82.0 + }, + "Говяжьи Мозги": { + "water": 78.9, + "proteins": 9.5, + "fats": 9.5, + "carbohydrates": 0.0, + "kcal": 124.0 + }, + "Говяжья Печень": { + "water": 72.9, + "proteins": 17.4, + "fats": 3.1, + "carbohydrates": 0.0, + "kcal": 98.0 + }, + "Говяжьи Почки": { + "water": 82.7, + "proteins": 12.5, + "fats": 1.8, + "carbohydrates": 0.0, + "kcal": 66.0 + }, + "Говяжье Вымя": { + "water": 72.6, + "proteins": 12.3, + "fats": 13.7, + "carbohydrates": 0.0, + "kcal": 173.0 + }, + "Говяжье Сердце": { + "water": 79.0, + "proteins": 15.0, + "fats": 3.0, + "carbohydrates": 0.0, + "kcal": 87.0 + }, + "Говяжий Язык": { + "water": 71.2, + "proteins": 13.6, + "fats": 12.1, + "carbohydrates": 0.0, + "kcal": 163.0 + }, + "Почки свинные": { + "water": 80.1, + "proteins": 13.0, + "fats": 3.1, + "carbohydrates": 0.0, + "kcal": 80.0 + }, + "Печень свинная": { + "water": 71.4, + "proteins": 18.8, + "fats": 3.6, + "carbohydrates": 0.0, + "kcal": 108.0 + }, + "Сердце свинное": { + "water": 78.0, + "proteins": 15.1, + "fats": 3.2, + "carbohydrates": 0.0, + "kcal": 89.0 + }, + "Язык свинной": { + "water": 66.1, + "proteins": 14.2, + "fats": 16.8, + "carbohydrates": 0.0, + "kcal": 208.0 + }, + "Гуси": { + "water": 49.7, + "proteins": 16.1, + "fats": 33.3, + "carbohydrates": 0.0, + "kcal": 364.0 + }, + "Индейка": { + "water": 64.5, + "proteins": 21.6, + "fats": 12.0, + "carbohydrates": 0.8, + "kcal": 197.0 + }, + "Цыплята": { + "water": 71.3, + "proteins": 18.7, + "fats": 7.8, + "carbohydrates": 0.4, + "kcal": 156.0 + }, + "Утки": { + "water": 51.5, + "proteins": 16.5, + "fats": 61.2, + "carbohydrates": 0.0, + "kcal": 346.0 + }, + "Курица, белое мясо": { + "water": 74.9, + "proteins": 23.2, + "fats": 1.7, + "carbohydrates": 0.0, + "kcal": 114.0 + }, + "Курица, голень": { + "water": 72.5, + "proteins": 19.3, + "fats": 8.7, + "carbohydrates": 0.0, + "kcal": 161.0 + }, + "Курица, бедро": { + "water": 67.7, + "proteins": 17.3, + "fats": 15.3, + "carbohydrates": 0.0, + "kcal": 211.0 + }, + "Курица, красное мясо": { + "water": 65.4, + "proteins": 16.7, + "fats": 18.3, + "carbohydrates": 0.0, + "kcal": 237.0 + }, + "Вареная колбаса Диабетическая": { + "water": 62.4, + "proteins": 12.1, + "fats": 22.8, + "carbohydrates": 0.0, + "kcal": 254.0 + }, + "Вареная колбаса Диетическая": { + "water": 71.6, + "proteins": 12.1, + "fats": 13.5, + "carbohydrates": 0.0, + "kcal": 170.0 + }, + "Колбаса Докторская": { + "water": 60.8, + "proteins": 13.7, + "fats": 22.8, + "carbohydrates": 0.0, + "kcal": 260.0 + }, + "Колбаса Любительская": { + "water": 39.1, + "proteins": 17.3, + "fats": 39.0, + "carbohydrates": 0.0, + "kcal": 420.0 + }, + "Колбаса Молочная": { + "water": 62.8, + "proteins": 11.7, + "fats": 22.8, + "carbohydrates": 0.0, + "kcal": 252.0 + }, + "Колбаса Отдельная": { + "water": 64.8, + "proteins": 10.1, + "fats": 20.1, + "carbohydrates": 1.8, + "kcal": 228.0 + }, + "Колбаса Телячья": { + "water": 55.0, + "proteins": 12.5, + "fats": 29.6, + "carbohydrates": 0.0, + "kcal": 316.0 + }, + "Сардельки Свиные": { + "water": 53.7, + "proteins": 10.1, + "fats": 31.6, + "carbohydrates": 1.9, + "kcal": 332.0 + }, + "Сосиски Молочные": { + "water": 60.0, + "proteins": 12.3, + "fats": 25.3, + "carbohydrates": 0.0, + "kcal": 277.0 + }, + "Сосиски Русские": { + "water": 66.2, + "proteins": 12.0, + "fats": 19.1, + "carbohydrates": 0.0, + "kcal": 220.0 + }, + "Сосиски Свиные": { + "water": 54.8, + "proteins": 11.8, + "fats": 30.8, + "carbohydrates": 0.0, + "kcal": 324.0 + }, + "Колбаса Сервелат": { + "water": 39.6, + "proteins": 28.2, + "fats": 27.5, + "carbohydrates": 0.0, + "kcal": 360.0 + }, + "Полукопченая Краковская": { + "water": 34.6, + "proteins": 16.2, + "fats": 44.6, + "carbohydrates": 0.0, + "kcal": 466.0 + }, + "Полукопченая Минская": { + "water": 52.0, + "proteins": 23.0, + "fats": 17.4, + "carbohydrates": 2.7, + "kcal": 259.0 + }, + "Полукопченая Полтавская": { + "water": 39.8, + "proteins": 16.4, + "fats": 39.0, + "carbohydrates": 0.0, + "kcal": 417.0 + }, + "Полукопченая Украинская": { + "water": 44.4, + "proteins": 16.5, + "fats": 34.4, + "carbohydrates": 0.0, + "kcal": 376.0 + }, + "Сырокопченая Любительская": { + "water": 25.2, + "proteins": 20.9, + "fats": 47.8, + "carbohydrates": 0.0, + "kcal": 514.0 + }, + "Сырокопченая Московская": { + "water": 27.6, + "proteins": 24.8, + "fats": 41.5, + "carbohydrates": 0.0, + "kcal": 473.0 + }, + "Грудинка сырокопченая": { + "water": 21.0, + "proteins": 7.6, + "fats": 66.8, + "carbohydrates": 0.0, + "kcal": 632.0 + }, + "Корейка сырокопченая": { + "water": 37.3, + "proteins": 10.5, + "fats": 47.2, + "carbohydrates": 0.0, + "kcal": 467.0 + }, + "Ветчина": { + "water": 53.5, + "proteins": 22.6, + "fats": 20.9, + "carbohydrates": 0.0, + "kcal": 279.0 + }, + "Бычки": { + "water": 70.8, + "proteins": 12.8, + "fats": 8.1, + "carbohydrates": 5.2, + "kcal": 145.0 + }, + "Горбуша": { + "water": 70.5, + "proteins": 21.0, + "fats": 7.0, + "carbohydrates": 0.0, + "kcal": 147.0 + }, + "Камбала": { + "water": 79.5, + "proteins": 16.1, + "fats": 2.6, + "carbohydrates": 0.0, + "kcal": 88.0 + }, + "Карась": { + "water": 78.9, + "proteins": 17.7, + "fats": 1.8, + "carbohydrates": 0.0, + "kcal": 87.0 + }, + "Карп": { + "water": 79.1, + "proteins": 16.0, + "fats": 3.6, + "carbohydrates": 0.0, + "kcal": 96.0 + }, + "Кета": { + "water": 71.3, + "proteins": 22.0, + "fats": 5.6, + "carbohydrates": 0.0, + "kcal": 138.0 + }, + "Корюшка": { + "water": 79.8, + "proteins": 15.5, + "fats": 3.2, + "carbohydrates": 0.0, + "kcal": 91.0 + }, + "Ледяная": { + "water": 81.8, + "proteins": 15.5, + "fats": 1.4, + "carbohydrates": 0.0, + "kcal": 75.0 + }, + "Лещ": { + "water": 77.7, + "proteins": 17.1, + "fats": 4.1, + "carbohydrates": 0.0, + "kcal": 105.0 + }, + "Семга": { + "water": 62.9, + "proteins": 20.8, + "fats": 15.1, + "carbohydrates": 0.0, + "kcal": 219.0 + }, + "Макрурус": { + "water": 85.0, + "proteins": 13.2, + "fats": 0.8, + "carbohydrates": 0.0, + "kcal": 60.0 + }, + "Минога": { + "water": 75.0, + "proteins": 14.7, + "fats": 11.9, + "carbohydrates": 0.0, + "kcal": 166.0 + }, + "Минтай": { + "water": 80.1, + "proteins": 15.9, + "fats": 0.7, + "carbohydrates": 0.0, + "kcal": 70.0 + }, + "Мойва": { + "water": 75.0, + "proteins": 13.4, + "fats": 11.5, + "carbohydrates": 0.0, + "kcal": 157.0 + }, + "Навага": { + "water": 81.1, + "proteins": 16.1, + "fats": 1.0, + "carbohydrates": 0.0, + "kcal": 73.0 + }, + "Налим": { + "water": 79.3, + "proteins": 18.8, + "fats": 0.6, + "carbohydrates": 0.0, + "kcal": 81.0 + }, + "Нототения мраморная": { + "water": 73.4, + "proteins": 14.8, + "fats": 10.7, + "carbohydrates": 0.0, + "kcal": 156.0 + }, + "Окунь морской": { + "water": 75.4, + "proteins": 17.6, + "fats": 5.2, + "carbohydrates": 0.0, + "kcal": 117.0 + }, + "Окунь речной": { + "water": 79.2, + "proteins": 18.5, + "fats": 0.9, + "carbohydrates": 0.0, + "kcal": 82.0 + }, + "Осетр": { + "water": 71.4, + "proteins": 16.4, + "fats": 10.9, + "carbohydrates": 0.0, + "kcal": 164.0 + }, + "Палтус": { + "water": 76.9, + "proteins": 18.9, + "fats": 3.0, + "carbohydrates": 0.0, + "kcal": 103.0 + }, + "Путассу": { + "water": 81.3, + "proteins": 16.1, + "fats": 0.9, + "carbohydrates": 0.0, + "kcal": 72.0 + }, + "Рыба-сабля": { + "water": 75.2, + "proteins": 20.3, + "fats": 3.2, + "carbohydrates": 0.0, + "kcal": 110.0 + }, + "Рыбец каспийский": { + "water": 77.0, + "proteins": 19.2, + "fats": 2.4, + "carbohydrates": 0.0, + "kcal": 98.0 + }, + "Сазан": { + "water": 75.3, + "proteins": 18.4, + "fats": 5.3, + "carbohydrates": 0.0, + "kcal": 121.0 + }, + "Сайра крупная": { + "water": 59.8, + "proteins": 18.6, + "fats": 20.8, + "carbohydrates": 0.0, + "kcal": 262.0 + }, + "Сайра мелкая": { + "water": 71.3, + "proteins": 20.4, + "fats": 0.8, + "carbohydrates": 0.0, + "kcal": 143.0 + }, + "Салака": { + "water": 75.4, + "proteins": 17.3, + "fats": 5.6, + "carbohydrates": 0.0, + "kcal": 121.0 + }, + "Сельдь": { + "water": 62.7, + "proteins": 17.7, + "fats": 19.5, + "carbohydrates": 0.0, + "kcal": 242.0 + }, + "Сиг": { + "water": 72.3, + "proteins": 19.0, + "fats": 7.5, + "carbohydrates": 0.0, + "kcal": 144.0 + }, + "Скумбрия": { + "water": 71.8, + "proteins": 18.0, + "fats": 9.0, + "carbohydrates": 0.0, + "kcal": 153.0 + }, + "Сом": { + "water": 75.0, + "proteins": 16.8, + "fats": 8.5, + "carbohydrates": 0.0, + "kcal": 144.0 + }, + "Ставрида": { + "water": 74.9, + "proteins": 18.5, + "fats": 5.0, + "carbohydrates": 0.0, + "kcal": 119.0 + }, + "Стерлядь": { + "water": 74.9, + "proteins": 17.0, + "fats": 6.1, + "carbohydrates": 0.0, + "kcal": 320.0 + }, + "Судак": { + "water": 78.9, + "proteins": 19.0, + "fats": 0.8, + "carbohydrates": 0.0, + "kcal": 83.0 + }, + "Треска": { + "water": 80.7, + "proteins": 17.5, + "fats": 0.6, + "carbohydrates": 0.0, + "kcal": 75.0 + }, + "Тунец": { + "water": 74.0, + "proteins": 22.7, + "fats": 0.7, + "carbohydrates": 0.0, + "kcal": 96.0 + }, + "Угольная рыба": { + "water": 71.5, + "proteins": 13.2, + "fats": 11.6, + "carbohydrates": 0.0, + "kcal": 158.0 + }, + "Угорь морской": { + "water": 77.5, + "proteins": 19.1, + "fats": 1.9, + "carbohydrates": 0.0, + "kcal": 94.0 + }, + "Угорь": { + "water": 53.5, + "proteins": 14.5, + "fats": 30.5, + "carbohydrates": 0.0, + "kcal": 333.0 + }, + "Хек": { + "water": 79.9, + "proteins": 16.6, + "fats": 2.2, + "carbohydrates": 0.0, + "kcal": 86.0 + }, + "Щука": { + "water": 70.4, + "proteins": 18.8, + "fats": 0.7, + "carbohydrates": 0.0, + "kcal": 82.0 + }, + "Язь": { + "water": 80.1, + "proteins": 18.2, + "fats": 0.3, + "carbohydrates": 0.0, + "kcal": 117.0 + }, + "Креветка дальневосточная": { + "water": 64.8, + "proteins": 28.7, + "fats": 1.2, + "carbohydrates": 0.0, + "kcal": 134.0 + }, + "Печень трески": { + "water": 26.4, + "proteins": 4.2, + "fats": 65.7, + "carbohydrates": 0.0, + "kcal": 613.0 + }, + "Кальмар": { + "water": 80.3, + "proteins": 18.0, + "fats": 0.3, + "carbohydrates": 0.0, + "kcal": 75.0 + }, + "Краб": { + "water": 81.5, + "proteins": 16.0, + "fats": 0.5, + "carbohydrates": 0.0, + "kcal": 69.0 + }, + "Креветка": { + "water": 77.5, + "proteins": 18.0, + "fats": 0.8, + "carbohydrates": 0.0, + "kcal": 83.0 + }, + "Морская капуста": { + "water": 88.0, + "proteins": 0.9, + "fats": 0.2, + "carbohydrates": 3.0, + "kcal": 5.0 + }, + "Паста “Океан”": { + "water": 72.2, + "proteins": 18.9, + "fats": 6.8, + "carbohydrates": 0.0, + "kcal": 137.0 + }, + "Трепанг": { + "water": 89.4, + "proteins": 7.3, + "fats": 0.6, + "carbohydrates": 0.0, + "kcal": 35.0 + }, + "Кеты зернистая": { + "water": 46.9, + "proteins": 31.6, + "fats": 13.8, + "carbohydrates": 0.0, + "kcal": 251.0 + }, + "Лещевая пробойная": { + "water": 58.0, + "proteins": 24.7, + "fats": 4.8, + "carbohydrates": 0.0, + "kcal": 142.0 + }, + "Минтаевая пробойная": { + "water": 63.2, + "proteins": 28.4, + "fats": 1.9, + "carbohydrates": 0.0, + "kcal": 131.0 + }, + "Осетровая зернистая": { + "water": 58.0, + "proteins": 28.9, + "fats": 9.7, + "carbohydrates": 0.0, + "kcal": 203.0 + }, + "Осетровая пробойная": { + "water": 39.5, + "proteins": 36.0, + "fats": 10.2, + "carbohydrates": 0.0, + "kcal": 123.0 + }, + "Мед": { + "water": 17.2, + "proteins": 0.8, + "fats": 0.0, + "carbohydrates": 80.3, + "kcal": 308.0 + }, + "Драже фруктовое": { + "water": 7.0, + "proteins": 3.7, + "fats": 10.2, + "carbohydrates": 73.1, + "kcal": 384.0 + }, + "Зефир": { + "water": 20.0, + "proteins": 0.8, + "fats": 0.0, + "carbohydrates": 78.3, + "kcal": 299.0 + }, + "Ирис": { + "water": 6.5, + "proteins": 3.3, + "fats": 7.5, + "carbohydrates": 81.8, + "kcal": 387.0 + }, + "Мармелад": { + "water": 21.0, + "proteins": 0.0, + "fats": 0.1, + "carbohydrates": 77.7, + "kcal": 296.0 + }, + "Карамель": { + "water": 4.4, + "proteins": 0.0, + "fats": 0.1, + "carbohydrates": 77.7, + "kcal": 296.0 + }, + "Конфеты, глаз-ые шоколадом": { + "water": 7.9, + "proteins": 2.9, + "fats": 10.7, + "carbohydrates": 76.6, + "kcal": 396.0 + }, + "Пастила": { + "water": 18.0, + "proteins": 0.5, + "fats": 0.0, + "carbohydrates": 80.4, + "kcal": 305.0 + }, + "Сахар": { + "water": 0.2, + "proteins": 0.3, + "fats": 0.0, + "carbohydrates": 99.5, + "kcal": 374.0 + }, + "Халва тахинная": { + "water": 3.9, + "proteins": 12.7, + "fats": 29.9, + "carbohydrates": 50.6, + "kcal": 510.0 + }, + "Халва подсолнечная": { + "water": 2.9, + "proteins": 11.6, + "fats": 29.7, + "carbohydrates": 54.0, + "kcal": 516.0 + }, + "Шоколад темный": { + "water": 0.8, + "proteins": 5.4, + "fats": 35.3, + "carbohydrates": 52.6, + "kcal": 540.0 + }, + "Шоколад молочный": { + "water": 0.9, + "proteins": 6.9, + "fats": 35.7, + "carbohydrates": 52.4, + "kcal": 547.0 + }, + "Вафли с фр-ми начинками": { + "water": 12.0, + "proteins": 3.2, + "fats": 2.8, + "carbohydrates": 80.1, + "kcal": 342.0 + }, + "Вафли с жировыми начинками": { + "water": 1.0, + "proteins": 3.4, + "fats": 30.2, + "carbohydrates": 64.7, + "kcal": 530.0 + }, + "Пирожное слоеное с кремом": { + "water": 9.0, + "proteins": 5.4, + "fats": 38.6, + "carbohydrates": 46.4, + "kcal": 544.0 + }, + "Пирожное слоеное с яблоком": { + "water": 13.0, + "proteins": 5.7, + "fats": 25.6, + "carbohydrates": 52.7, + "kcal": 454.0 + }, + "Пирожное бисквитное": { + "water": 21.0, + "proteins": 4.7, + "fats": 9.3, + "carbohydrates": 84.4, + "kcal": 344.0 + }, + "Пряники": { + "water": 14.5, + "proteins": 4.8, + "fats": 2.8, + "carbohydrates": 77.7, + "kcal": 336.0 + }, + "Торт бисквитный": { + "water": 25.0, + "proteins": 4.7, + "fats": 20.0, + "carbohydrates": 49.8, + "kcal": 386.0 + }, + "Торт миндальный": { + "water": 9.3, + "proteins": 6.6, + "fats": 35.8, + "carbohydrates": 46.8, + "kcal": 524.0 + }, + "Яйцо куриное": { + "water": 74.0, + "proteins": 12.7, + "fats": 11.5, + "carbohydrates": 0.7, + "kcal": 157.0 + }, + "Яичный порошок": { + "water": 6.8, + "proteins": 45.0, + "fats": 37.3, + "carbohydrates": 7.1, + "kcal": 542.0 + }, + "Сухой белок": { + "water": 12.1, + "proteins": 73.3, + "fats": 1.8, + "carbohydrates": 7.0, + "kcal": 336.0 + }, + "Сухой желток": { + "water": 5.4, + "proteins": 34.2, + "fats": 52.2, + "carbohydrates": 4.4, + "kcal": 623.0 + }, + "Яйцо перепелиное": { + "water": 73.3, + "proteins": 11.9, + "fats": 13.1, + "carbohydrates": 0.6, + "kcal": 168.0 + }, + "Белые свежие": { + "water": 89.9, + "proteins": 3.2, + "fats": 0.7, + "carbohydrates": 1.6, + "kcal": 25.0 + }, + "Белые сушеные": { + "water": 13.0, + "proteins": 27.6, + "fats": 6.8, + "carbohydrates": 10.0, + "kcal": 209.0 + }, + "Подберезовики свежие": { + "water": 91.6, + "proteins": 2.3, + "fats": 0.9, + "carbohydrates": 3.7, + "kcal": 31.0 + }, + "Подосиновики свежие": { + "water": 91.1, + "proteins": 3.3, + "fats": 0.5, + "carbohydrates": 3.4, + "kcal": 31.0 + }, + "Сыроежи свежие": { + "water": 83.0, + "proteins": 1.7, + "fats": 0.3, + "carbohydrates": 1.4, + "kcal": 17.0 + }, + "Бобы": { + "water": 83.0, + "proteins": 6.0, + "fats": 0.1, + "carbohydrates": 8.3, + "kcal": 58.0 + }, + "Горох лущеный": { + "water": 14.0, + "proteins": 23.0, + "fats": 1.6, + "carbohydrates": 57.7, + "kcal": 323.0 + }, + "Горох цельный": { + "water": 14.0, + "proteins": 23.0, + "fats": 1.2, + "carbohydrates": 53.3, + "kcal": 303.0 + }, + "Соя": { + "water": 12.0, + "proteins": 34.9, + "fats": 17.3, + "carbohydrates": 26.5, + "kcal": 395.0 + }, + "Фасоль": { + "water": 14.0, + "proteins": 22.3, + "fats": 1.7, + "carbohydrates": 54.5, + "kcal": 309.0 + }, + "Чечевица": { + "water": 14.0, + "proteins": 24.8, + "fats": 1.1, + "carbohydrates": 53.7, + "kcal": 310.0 + } +} \ No newline at end of file diff --git a/hackathons/bot/tests/__init__.py b/hackathons/bot/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hackathons/bot/tests/command_handler/Test_message.py b/hackathons/bot/tests/command_handler/Test_message.py new file mode 100644 index 0000000..6449010 --- /dev/null +++ b/hackathons/bot/tests/command_handler/Test_message.py @@ -0,0 +1,25 @@ +import unittest +from command_handler.message import MessageCommandHandler + + +class MyTestCase(unittest.TestCase): + + def Obj(self): + obj = MessageCommandHandler() + + for i in range(5): + obj.handle('messages start yeah' + str(i)) + return obj + + def test_get(self): + obj = self.Obj() + self.assertEqual('yeah4', obj.handle('messages get')) + + def test_random(self): + obj = self.Obj() + print('asd') + self.assertEqual('yeah3', obj.handle('messages random', rand_func=lambda x, b: 3)) + + +if __name__ == '__main__': + unittest.main() diff --git a/hackathons/bot/tests/command_handler/test_sample.py b/hackathons/bot/tests/command_handler/test_sample.py new file mode 100644 index 0000000..5ae3d79 --- /dev/null +++ b/hackathons/bot/tests/command_handler/test_sample.py @@ -0,0 +1,14 @@ +from command_handler.sample import SampleCommandHandler +from unittest import TestCase + + +class SampleCommandHandlerTestCase(TestCase): + def setUp(self): + print(SampleCommandHandler) + self._handler = SampleCommandHandler() + + def test_handle(self): + self.assertEqual('_test', self._handler.handle('_test')) + self.assertIsNone(self._handler.handle('test')) + with self.assertRaises(RuntimeError): + self._handler.handle('@test') diff --git a/hackathons/games/README.md b/hackathons/games/README.md new file mode 100644 index 0000000..2a48e1e --- /dev/null +++ b/hackathons/games/README.md @@ -0,0 +1,30 @@ +Пишем игры +================= + +Хакатон про написание игр + +В репозитории приведен пример змейки написаной на _curses_, библиотеки для работы с консольными приложениями. Описание библиотеки https://docs.python.org/3.6/library/curses.html + +Задание +-------- + +Необходимо улучшить игру и добавить в нее новые фичи разбившись на команды и создавая merge requests. + +Используя змейку как каркас написать свою игру, в которую мы могли бы поиграть. Примеры: +- Довести до ума змейку (поправить баги, сделать смерть змейки) +- Space Invaders +- Arcanoid +- Скроллер +- Flappy bird +- Текстовый квест +- Sokoban + +Фичи можно придумывать самостоятельно, но нужно координировать действия с другими командами.Примеры фич: +- Добавить возможность проиграть и жизни +- Добавить таблицу рекордов +- Добавить паузу и сохранение игры +- Добавить сложность и ускорение со временем, препятствия на большой сложности +- Игра вдвоем - на одном поле (кто быстрее съест яблоко) +- Игра вдвоем - на двух полях (кто дольше продержится) +- Режим "лабиринт" - змейка движется по лабиринту и там собирает яблоки +- Улучшить графику diff --git a/hackathons/games/example.py b/hackathons/games/example.py new file mode 100644 index 0000000..7ba7660 --- /dev/null +++ b/hackathons/games/example.py @@ -0,0 +1,101 @@ +import curses +from curses import KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN +from random import randint + + +class SnakeGame(): + + def __init__(self): + self.win_borders = (30, 60, 0, 0) + self.snake = [(4,10), (4,9), (4,8)] + self.key = KEY_RIGHT + self.keys = (KEY_RIGHT, KEY_LEFT, KEY_UP, KEY_DOWN) + self.snake_ch = '*' + score = 0 + + + def drop_apple(self, last_apple): + while True: + apple = randint(1, 18), randint(1, 58) + if apple in self.snake: + continue + return apple + + @property + def score(self): + return len(self.snake) - 3 + + def new_head(self): + head = self.snake[0] + + # check key + if self.key == KEY_RIGHT: + new_head = head[0], head[1] + 1 + elif self.key == KEY_LEFT: + new_head = head[0], head[1] - 1 + elif self.key == KEY_UP: + new_head = head[0] - 1, head[1] + elif self.key == KEY_DOWN: + new_head = head[0] + 1, head[1] + + # check borders + if new_head[0] == 0: + new_head = self.win_borders[0] - 2, new_head[1] + if new_head[0] == self.win_borders[0] - 1: + new_head = 1, new_head[1] + if new_head[1] == 0: + new_head = new_head[0], self.win_borders[1] - 2 + if new_head[1] == self.win_borders[1] - 1: + new_head = new_head[0], 1 + return new_head + + def loop(self, win): + apple = self.drop_apple(None) + win.addch(apple[0], apple[1], '#') + while True: + win.border(0) + win.addstr(0, 2, 'Score : ' + str(self.score) + ' ') + win.addstr(0, 27, ' SNAKE ') + win.timeout(100) + + event = win.getch() + if event == 27: # Escape + break + if event in self.keys: + self.key = event + + head = self.new_head() + win.addch(head[0], head[1], self.snake_ch) + self.snake.insert(0, head) + if apple in self.snake: + apple = self.drop_apple(apple) + win.addch(apple[0], apple[1], '#') + else: + tail = self.snake.pop() + win.addch(tail[0], tail[1], ' ') + + def run(self): + curses.initscr() + win = curses.newwin(*self.win_borders) + win.keypad(1) + curses.noecho() + curses.curs_set(0) + win.border(0) + win.nodelay(1) + + try: + self.loop(win) + except KeyboardInterrupt: + pass + except Exception as e: + print(e) + + win.nodelay(0) + win.keypad(0) + curses.echo() + curses.endwin() + print("\nScore - " + str(self.score)) + + +if __name__ == '__main__': + SnakeGame().run() \ No newline at end of file diff --git a/hackathons/tester.py b/hackathons/tester.py new file mode 100644 index 0000000..e7abec2 --- /dev/null +++ b/hackathons/tester.py @@ -0,0 +1,44 @@ +import sys +import os +import re +import argparse +from BaseGame import * + +FILE_LIST = os.listdir('./games') +GAMES_LIST = [] +for FILE in FILE_LIST: + if re.fullmatch(r'.*.py', FILE): + GAMES_LIST.append(FILE) + +try: + command_module = __import__("games", fromlist=GAMES_LIST) +except ImportError: + pass + +def parse_args(args): + parser = argparse.ArgumentParser(description='This is a simple tester\ + for your code') + parser.add_argument('name', + action="store", + help='Name of module') + return parser.parse_args(args) + +if __name__ == "__main__": + scorer = Scorer() + parser = parse_args(sys.argv[1:]) + module_dict = command_module.__dict__[parser.name].__dict__ + for module in module_dict: + if module == 'NAME': + name = module_dict[module] + try: + if issubclass(module_dict[module], BaseGame): + game_class = module_dict[module] + except TypeError: + pass + + game = game_class(name, scorer) + try: + game.run() + except KeyboardInterrupt: + pass + print("Your scores after game - {}".format(scorer.get_points(name))) \ No newline at end of file diff --git a/homeworks/README.md b/homeworks/README.md index b3bccd6..66379d0 100644 --- a/homeworks/README.md +++ b/homeworks/README.md @@ -3,6 +3,14 @@ * Решение нужно выложить на гитхаб или другой аналогичный сервис. Если есть возможность, лучше использовать приватный репозиторий. * Не надо присылать решение мердж-реквестом. -* Ссылку на решение нужно скинуть препоадвателю в slack. +* Ссылку на решение нужно скинуть преподавателю в slack. * Проверяет домашнее задание тот же преподаватель, который его давал. * Решение должно запускаться и проходить все тесты. Нарушение этого правила приводит к снижению баллов, вплоть до 100%. + +Сроки +----- + +* 10 дней, считая день лекции — полный балл; +* Еще 7 дней — минус 1 балл; +* Еще 7 дней — минус половина баллов (округляется в худшую сторону); +* Дальше баллов нет :]. diff --git a/homeworks/async_file_storage/README.md b/homeworks/async_file_storage/README.md new file mode 100644 index 0000000..d24edc4 --- /dev/null +++ b/homeworks/async_file_storage/README.md @@ -0,0 +1,56 @@ +Асинхронное распределенное файловое хранилище +============================================ + +Нужно написать демона с использованием `aiohttp`, который раздает файлы по HTTP из заранее назначенной директории +(без вложенностей), а за отсутствующими файлами пробует обратиться к другим таким же демонам. + +Общая схема +----------- + +Запускается несколько копий демона, каждый со своим конфигом. Например `A`, `B`, `C`. +`A` раздает файлы из `/tmp/a`, `B` – из `/tmp/b/`, `С` — из `/tmp/c/`. +Когда пользователь запрашивает файл `x` с ноды `A`, она ищет `/tmp/a/x`. +Если такой файл есть, его содержимое отдается пользователю. +Если такого файла нет, `A` делает запрос на `B` и `C`. +Если такого файла нет и там, пользователю возвращается код ошибки 404. +Если такой файл есть, то `A` получает его, отдает пользователю и сохраняет в `/tmp/a` +(последнее зависит от конфигурации). + +Запросы +------- + +Чтобы получить файл `x`, пользователь делает GET-запрос на `http://hostname:port/x`. + +Демоны между собой тоже общаются по HTTP, запрос можно использовать любой. + +Конфигурация +------------ + +При запуске демону указывается путь до yaml-файла с конфигурацией, в которой указано: + +* Какой порт слушать; +* Из какой директории раздавать файлы; +* Где искать другие ноды (хост, порт); +* Нужно ли сохранять файл, найденный на другой ноде, локально. + +Условия +--------- + +* Читать файл с диска надо в треде. +* Писать файл на диск надо в треде. +* Опрашивать все соседние ноды надо одновременно. +* Считается, что файл с одним именем на всех нодах одинаковый. +* Все файлы текстовые, пользователю можно отдавать просто текст. + +Дополнительные задачи +--------------------- + +* Сохранение файлов, полученных с других нод, включается для каждой такой ноды отдельно. + (Т. е. можно сконфигурировать первую ноду так, что со второй ноды файлы забираются и сохраняются, + а с третьей только забираются.) +* Файлы, полученные с других нод, сохраняются временно. + Время жизни указывается в конфиге, глобально и для каждой ноды отдельно. +* Поддержка заливки файла от пользователя. + При этом надо удостовериться, что на других нодах файла с таким именем еще нет. + +_Напоминаю, что баллов дополнительные задачи не приносят, только фан._ diff --git a/homeworks/blog/README.md b/homeworks/blog/README.md new file mode 100644 index 0000000..9f1e7d9 --- /dev/null +++ b/homeworks/blog/README.md @@ -0,0 +1,60 @@ +blogs + +================== + +Нужно написать класс, реализующий функционал блога. + +Описание +-------- + +# Основное задание +8 баллов + +Должны быть доступны следующие методы: + +* добавить пользователя +* авторизоваться пользователем по логину + паролю +* получить полный список пользователей +* создать блог +* редактировать блог +* удалить блог (или отметить блог удаленным) +* получить список не удаленных блогов +* получить список не удаленных блогов созданный авторизованным пользователем +* создать пост связанный с одним или несколькими блогами +* отредактировать пост, должна быть возможность изменить заголовки/текст/блоги в которых опубликован +* удалить пост +* добавить комментарий если пользователь авторизован +* комментарии должны реализовывать поддержку веток комментариев ( комментарий можно оставить на пост или на другой комментарий ) +* получить список всех комментариев пользователя к посту + +хранение данных организовать в MySQL + +# Доп задание №1 + +3 балла + +наполнить базу данных данными: + +* пользователи: 1000 +* блоги: 100 +* посты: 10000 +* комментарии: 100000 + +# Доп задание №2 +3 балла +* добавление необходимых индексов, обоснование добавленных индексов запросами +* оптимизация запросов, разбор нескольких запросов с помощью explain + +дополнить класс методами: + +* получения ветки комментариев начиная с заданного +* получения всех комментариев для 1 или нескольких указанных пользователей из указанного блога + +Результат +-------- + +для сдачи дз необходимо: +* прислать класс реализующий методы и sql запросы +* скрипт создающий базу данных +* Доп задание №1 скрипт наполняющий базу +* Доп задание №2 скрипт добавляющий индексы, разбор интересных запросов diff --git a/homeworks/blog_platform/README.md b/homeworks/blog_platform/README.md new file mode 100644 index 0000000..20ea558 --- /dev/null +++ b/homeworks/blog_platform/README.md @@ -0,0 +1,46 @@ +Blogs platform + +================== + +Необходимо реализовать платформу для ведения онлайн дневников + +Описание +-------- + +# Основное задание +8 баллов + +Необходимо реализовать анонимную платформу для ведения онлайн - дневника. + +Должны быть следующие возможности: + +- добавить пост (просто тема и текст) +- скрыть пост (через админку) +- добавить к посту комментарий +- посмотреть на пост с комментариями, причем комментарии должны показываться постранично по времени добавления (сначала старые) +- посмотреть на список постов по времени и по полпулярности (например, по количеству просмотров) +- поиск по теме поста + + +# Доп задание №1 +4 балла + +Платформа становится неанонимной. Добавляем авторизацию пользователей и теперь посты и комментарии не анонимны. Анонимный пользователь может все смотреть но не редактировать. + +Добавляются следующие возможности: + +- Профиль пользователя с постами и комментами +- Возможность редактирования своего профиля (никнейм, подпись к комментам, аватар) +- Возможность скрыть свой пост +- Авторизованный пользователь может "лайкнуть" пост, причем только один раз +- Топ постов за день, месяц, год + + +# Доп задание №2 + +Еще возможности для получения баллов: + +- Наличие необходимых индексов и отсуствие лишних запросов к базе - 1 балл +- Добавление форматирования в теле поста - 1 балл +- Верстка на страницах - 2 балла + diff --git a/homeworks/dir_dict/README.md b/homeworks/dir_dict/README.md new file mode 100644 index 0000000..d579cba --- /dev/null +++ b/homeworks/dir_dict/README.md @@ -0,0 +1,22 @@ +Словарь-директория +================== + +Нужно написать класс, который полностью повторяет интерфейс обычного `dict`, +но хранит все данные на диске в указанной директории. Причем все ключи — это файлы, +а значения — их содержимое. Значения — юникодные строки, при попытке записать что-то другое, +просто записывать строковое представление. + +Кеша быть не должно, все изменения на диске должны мгновенно отражаться в объекте. + +Пример +------ + +```python +d = DirDict('/tmp/dirdict') +d['lang'] = 'Python\n' +``` + +```bash +$ cat /tmp/dirdict/lang +Python +``` diff --git a/homeworks/grep/Makefile b/homeworks/grep/Makefile new file mode 100644 index 0000000..8831fc3 --- /dev/null +++ b/homeworks/grep/Makefile @@ -0,0 +1,4 @@ +test: + python -m unittest + +.PHONY: test diff --git a/homeworks/grep/README.md b/homeworks/grep/README.md new file mode 100644 index 0000000..cd1c034 --- /dev/null +++ b/homeworks/grep/README.md @@ -0,0 +1,33 @@ + +grep +================== + +Нужно написать функцию, реализующую аналог консольной утилиты grep + +Описание +-------- + +Функция фильтрует строки, поступающие на стандартный вход и фильтрует их, согласно параметрам. + +Перечисление параметров: +* _invert_ — выводить строки, которые __НЕ__ совпадают с шаблоном +* _ignore_case_ — при сравнении шаблона не учитывать регистр +* _count_ — выводить только число строк удовлетворивших шаблону +* _line_number_ — перед срокой выводить также и ее номер (строки нумеруются с единицы) в виде "5:строка" +* _context_ __N__ — помимо строки удовлетворяющей шаблону вывести также и __N__ строк до и __N__ строк после нее если столько есть. Если соседние блоки пересекаются то их нужно объединять. Если используется флаг _line_number_, то строки контекста нумеруются так "5-строка". +* _before_context_ __N__ — аналогично _context_, но выводить нужно только строки __ДО__ найденой. +* _after_context_ __N__ — аналогично _context_, но выводить нужно только строки __ПОСЛЕ__ найденой. +* _pattern_ __str__ — строка, описывающая шаблон поиска. В строке могут использоваться специальные сиволы: + * __"?"__ — один любой символ; + * __"*"__ — ноль или несколько любых символов (но в рамках одной строки) + +Формат выполнения задания +------- + +В файле grep.py указан шаблон функции, который необходимо заполнить вашим кодом. Шаблон берет на себя чтение из стандартного входа и разбор параметров, вам необходимо реализовать логику работы параметров. + +Эта функция получит на вход итератор со строками из стандартного входа, на выход она должна отдать то, что должно в результате быть выведено на экран. + +Если возникают вопросы по работе параметров - можно посмотреть как они работают в самой утилите _grep_. + +К заданию прилагается несколько тестов. Тесты будут добавляться по мере нахождения популярных ошибок. Прохождение тестов не гарантирует правильное выполнение задания, но это необходимое условие. Если вы обнаружили ошибку в тесте (например, в оригинальной утилите параметр работает не так как в тесте) пишите в чат. Написание собственных тестов - приветствуется. diff --git a/homeworks/grep/grep.py b/homeworks/grep/grep.py new file mode 100644 index 0000000..ade699c --- /dev/null +++ b/homeworks/grep/grep.py @@ -0,0 +1,66 @@ + +import argparse +import sys + + +def output(line): + print(line) + + +def grep(lines, params): + for line in lines: + line = line.rstrip() + if params.pattern in line: + output(line) + + +def parse_args(args): + parser = argparse.ArgumentParser(description='This is a simple grep on python') + parser.add_argument( + '-v', action="store_true", dest="invert", default=False, help='Selected lines are those not matching pattern.') + parser.add_argument( + '-i', action="store_true", dest="ignore_case", default=False, help='Perform case insensitive matching.') + parser.add_argument( + '-c', + action="store_true", + dest="count", + default=False, + help='Only a count of selected lines is written to standard output.') + parser.add_argument( + '-n', + action="store_true", + dest="line_number", + default=False, + help='Each output line is preceded by its relative line number in the file, starting at line 1.') + parser.add_argument( + '-C', + action="store", + dest="context", + type=int, + default=0, + help='Print num lines of leading and trailing context surrounding each match.') + parser.add_argument( + '-B', + action="store", + dest="before_context", + type=int, + default=0, + help='Print num lines of trailing context after each match') + parser.add_argument( + '-A', + action="store", + dest="after_context", + type=int, + default=0, + help='Print num lines of leading context before each match.') + parser.add_argument('pattern', action="store", help='Search pattern. Can contain magic symbols: ?*') + return parser.parse_args(args) + + +def main(): + params = parse_args(sys.argv[1:]) + grep(sys.stdin.readlines(), params) + + +if __name__ == '__main__': + main() diff --git a/homeworks/grep/tests/__init__.py b/homeworks/grep/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/homeworks/grep/tests/test_grep.py b/homeworks/grep/tests/test_grep.py new file mode 100644 index 0000000..4d5e387 --- /dev/null +++ b/homeworks/grep/tests/test_grep.py @@ -0,0 +1,145 @@ + +from unittest import TestCase + +import grep + +lst = [] + + +def save_to_list(line): + lst.append(line) + + +grep.output = save_to_list + +class GrepBaseTest(TestCase): + + lines = ['baab', 'bbb', 'ccc', 'A'] + + def tearDown(self): + global lst + lst.clear() + + def test_base_scenario(self): + params = grep.parse_args(['aa']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab']) + + def test_base_scenario_multi(self): + params = grep.parse_args(['b']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'bbb']) + + def test_base_scenario_count(self): + params = grep.parse_args(['-c', 'a']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['1']) + + def test_base_scenario_invert(self): + params = grep.parse_args(['-v', 'b']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['ccc', 'A']) + + def test_base_scenario_case(self): + params = grep.parse_args(['-i', 'a']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'A']) + +class GrepPatternTest(TestCase): + + lines = ['baab', 'abbb', 'fc', 'AA'] + + def tearDown(self): + global lst + lst.clear() + + def test_question_base(self): + params = grep.parse_args(['?b']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'abbb']) + + def test_question_start(self): + params = grep.parse_args(['?a']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab']) + + def test_queston_end(self): + params = grep.parse_args(['c?']) + grep.grep(self.lines, params) + self.assertEqual(lst, []) + + def test_queston_double(self): + params = grep.parse_args(['b??b']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab']) + + def test_queston_count(self): + params = grep.parse_args(['???']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'abbb']) + + def test_asterics(self): + params = grep.parse_args(['b*b']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'abbb']) + + def test_asterics_all(self): + params = grep.parse_args(['***']) + grep.grep(self.lines, params) + self.assertEqual(lst, self.lines) + +class GrepContextTest(TestCase): + + lines = ['vr','baab', 'abbb', 'fc', 'bbb', 'cc'] + + def tearDown(self): + global lst + lst.clear() + + def test_context_base(self): + params = grep.parse_args(['-C1','aa']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['vr', 'baab', 'abbb']) + + def test_context_intersection(self): + params = grep.parse_args(['-C1','ab']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['vr', 'baab', 'abbb', 'fc']) + + def test_context_intersection_hard(self): + params = grep.parse_args(['-C2','bbb']) + grep.grep(self.lines, params) + self.assertEqual(lst, self.lines) + + def test_before(self): + params = grep.parse_args(['-B1','bbb']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['baab', 'abbb', 'fc', 'bbb']) + + def test_after(self): + params = grep.parse_args(['-A1','bbb']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['abbb', 'fc', 'bbb', 'cc']) + +class GrepLineNumbersTest(TestCase): + + lines = ['vr','baab', 'abbb', 'fc', 'bbb', 'cc'] + + def tearDown(self): + global lst + lst.clear() + + def test_numbers_base(self): + params = grep.parse_args(['-n','ab']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['2:baab', '3:abbb']) + + def test_numbers_context(self): + params = grep.parse_args(['-n', '-C1','aa']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['1-vr', '2:baab', '3-abbb']) + + def test_numbers_context_question(self): + params = grep.parse_args(['-n', '-C1', '???']) + grep.grep(self.lines, params) + self.assertEqual(lst, ['1-vr', '2:baab', '3:abbb', '4-fc', '5:bbb', '6-cc']) diff --git a/homeworks/log_parse/README.md b/homeworks/log_parse/README.md index 9477a56..9cc21e5 100644 --- a/homeworks/log_parse/README.md +++ b/homeworks/log_parse/README.md @@ -1,40 +1,44 @@ -Даны логи запросов сервера обслуживающего несколько доменных имен.
-Кроме запросов в логе могут встречаться строки не являющиеся записями об обращении к серверу.
-если строка является логом запроса к серверу, то она имеет слелующую структуру:
-[request_date] "request_type request protocol" response_code response_time
+# Парсер логов +Даны логи запросов сервера обслуживающего несколько доменных имен. +Кроме запросов в логе могут встречаться строки не являющиеся записями об обращении к серверу. +Если строка является логом запроса к серверу, то она имеет следующую структуру: -request_date - дата выполнения запроса
-request_type - тип запроса (GET, POST, PUT ...)
-request - <схема>:[//[<логин>:<пароль>@]<хост>[:<порт>]][/][?<параметры>][#<якорь>] https://ru.wikipedia.org/wiki/URL
-protocol - протокол и версия протокола, по которому осуществлялся запрос HTTP/HTTPS/FTP ...
-response_code - статус код, которым ответил сервер на запрос
-response_time - время выполнения запроса сервером
+``` +[request_date] "request_type request protocol" response_code response_time +``` -пример логов: - -[21/Mar/2018 21:32:09] "GET https://sys.mail.ru/static/css/reset.css HTTPS/1.1" 200 1090
-[21/Mar/2018 21:32:09] "GET https://corp.mail.ru/static/css/login.css HTTPS/1.1" 200 638
-help
-[21/Mar/2018 21:32:09] "GET https://sys.mail.ru/static/js/auth_error_message.js HTTPS/1.1" 200 1081
-[21/Mar/2018 21:53:10] "GET https://mail.ru/fitness/pay_list HTTP/1.1" 301 0
-ERROR [django.request:135] Internal Server Error: /fitness/pay_list/
-Traceback (most recent call last):
- File "/root/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
- response = wrapped_callback(request, *callback_args, **callback_kwargs)
- File "/root/fitness_pay/views.py", line 80, in show_pays
- raise Exception
-Exception
-[21/Mar/2018 21:53:10] "GET https://corp.mail.ru/fitness/pay_list/ HTTP/1.1" 500 120426
-[21/Mar/2018 21:32:11] "GET https://mail.ru/static/js/jquery-go-top/go-top.png HTTP/1.1" 200 1845
+- **request_date** - дата выполнения запроса +- **request_type** - тип запроса (GET, POST, PUT ...) +- **request** - <схема>:[//[<логин>:<пароль>@]<хост>[:<порт>]][/][?<параметры>][#<якорь>] https://ru.wikipedia.org/wiki/URL +- **protocol** - протокол и версия протокола, по которому осуществлялся запрос HTTP/HTTPS/FTP ... +- **response_code** - статус код, которым ответил сервер на запрос +- **response_time** - время выполнения запроса сервером -цель задачи:
-Написать функцию, возвращающую списк из количества использования топ 5 урлов, которые запрашивают у сервера
-функция должна принимать дополнительные параметры:
-ignore_files (bool) - игнорирует фаилы
-ignore_urls (list)- игнорирует урлы из переданного списка
-start_at (datetime) - начать парсить логи начиная с указанной даты
-stop_at (datetime) - закончить парсить логи на указанной дате
-request_type (string) - тип запроса, которые надо парсить ( остальные игнорируются)
-ignore_www (bool) - игнорировать www перед доменом
-slow_queries (bool) - если True возвращает количество милисекунд (целую часть), потраченное на топ 5 самых медленных запросов к серверу
+#### Пример логов +``` +[21/Mar/2018 21:32:09] "GET https://sys.mail.ru/static/css/reset.css HTTPS/1.1" 200 1090 +[21/Mar/2018 21:32:09] "GET https://corp.mail.ru/static/css/login.css HTTPS/1.1" 200 638 +help +[21/Mar/2018 21:32:09] "GET https://sys.mail.ru/static/js/auth_error_message.js HTTPS/1.1" 200 1081 +[21/Mar/2018 21:53:10] "GET https://mail.ru/fitness/pay_list HTTP/1.1" 301 0 +ERROR [django.request:135] Internal Server Error: /fitness/pay_list/ +Traceback (most recent call last): + File "/root/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response + response = wrapped_callback(request, *callback_args, **callback_kwargs) + File "/root/fitness_pay/views.py", line 80, in show_pays + raise Exception +Exception +[21/Mar/2018 21:53:10] "GET https://corp.mail.ru/fitness/pay_list/ HTTP/1.1" 500 120426 +[21/Mar/2018 21:32:11] "GET https://mail.ru/static/js/jquery-go-top/go-top.png HTTP/1.1" 200 1845 +``` +## Цель задачи +Написать функцию, возвращающую список из количества использования топ 5 урлов, которые запрашивались у сервера. +### Функция должна принимать дополнительные параметры: +- **ignore_files (bool)** - игнорирует файлы (игнорируется весь лог, если в логе запрашивается фаил) +- **ignore_urls (list)** - игнорирует урлы из переданного списка +- **start_at (datetime)** - начать парсить логи начиная с указанной даты +- **stop_at (datetime)** - закончить парсить логи на указанной дате +- **equest_type (string)** - тип запроса, которые надо парсить ( остальные игнорируются) +- **ignore_www (bool)** - игнорировать www перед доменом (лог учитывается, но отбрасывается www из url лога) +- **slow_queries (bool)** - если True возвращает среднее значение в количестве миллисекунд (целую часть), потраченное на топ 5 самых медленных запросов к серверу (суммарное время ответов деленное на количество запросов) diff --git a/homeworks/log_parse/tests.py b/homeworks/log_parse/tests.py old mode 100644 new mode 100755 index 8584866..5d07d98 --- a/homeworks/log_parse/tests.py +++ b/homeworks/log_parse/tests.py @@ -1,18 +1,24 @@ +# -*- encoding: utf-8 -*- + import json from glob import glob from log_parse import parse -error_message = 'Ошибка в фаиле {}. Expected: "{}", got: "{}"' +error_message = "Полученный и ожидаемый массивы различаются, получен: {} ожидался: {}, файл {}" def run_tests(): for filename in glob('tests/*.json'): data = json.load(open(filename)) params, response = data['params'], data['response'] - got = parse(**data['params']) + got = parse(**params) + if len(got) != len(response): + print(error_message.format( + str(got), str(response), filename + )) for index, item in enumerate(response): - if len(got) != len(response) or got[index] != response[index]: - print("Полученный и ожидаемый массивы различаются, получен: {} ожидался: {}, фаили {}".format( + if got[index] != response[index]: + print(error_message.format( str(got), str(response), filename )) return diff --git a/homeworks/log_parse/tests/fifth_test.json b/homeworks/log_parse/tests/fifth_test.json new file mode 100755 index 0000000..aef9664 --- /dev/null +++ b/homeworks/log_parse/tests/fifth_test.json @@ -0,0 +1,2 @@ +{"params": {"start_at": "18/Mar/2018 11:19:41", "stop_at": "25/Mar/2018 11:17:31"}, "response": [2, 2, 2, 2, 2]} + diff --git a/homeworks/log_parse/tests/seventh_test.json b/homeworks/log_parse/tests/seventh_test.json new file mode 100755 index 0000000..bb13963 --- /dev/null +++ b/homeworks/log_parse/tests/seventh_test.json @@ -0,0 +1,2 @@ +{"params": {"request_type": "PUT", "slow_queries": true}, "response": []} + diff --git a/homeworks/log_parse/tests/sixth_test.json b/homeworks/log_parse/tests/sixth_test.json new file mode 100755 index 0000000..cf253d8 --- /dev/null +++ b/homeworks/log_parse/tests/sixth_test.json @@ -0,0 +1,2 @@ +{"params": {"request_type": "GET", "slow_queries": true}, "response": [61699, 53544, 42979, 34054, 27412]} + diff --git a/homeworks/minigolf/tests/test_minigolf.py b/homeworks/minigolf/tests/test_minigolf.py index 305a761..492c412 100644 --- a/homeworks/minigolf/tests/test_minigolf.py +++ b/homeworks/minigolf/tests/test_minigolf.py @@ -90,9 +90,7 @@ def test_scenario(self): with self.assertRaises(RuntimeError): m.hit() - self.assertEqual(m.get_winners(), [ - players[0], players[2] - ]) + self.assertEqual(m.get_winners(), [players[0]]) def _first_hole(self, m): m.hit(True) # 1 @@ -131,14 +129,14 @@ def _third_hole(self, m): (0, 0, 0), (None, None, 1), ]) - m.hit() # 1 - m.hit(True) # 2 + m.hit(True) # 1 + m.hit() # 2 self.assertTrue(m.finished) self.assertEqual(m.get_table(), [ ('A', 'B', 'C'), (1, 0, 0), (0, 0, 0), - (0, 1, 1), + (1, 0, 1), ]) diff --git a/homeworks/passengers/Makefile b/homeworks/passengers/Makefile deleted file mode 100644 index 75e4c42..0000000 --- a/homeworks/passengers/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -test: - python test.py diff --git a/homeworks/passengers/README.md b/homeworks/passengers/README.md deleted file mode 100644 index 110954f..0000000 --- a/homeworks/passengers/README.md +++ /dev/null @@ -1,28 +0,0 @@ - -Гуляющие пассажиры -================== - -Нужно написать функцию, реализующую подсчет пассажиров, гуляющих по вагонам поездов. - -Описание --------- - -Изначально вам известна информация о поездах, прицепленных к ним вагонах и находящихся в вагонах людях. - -Также вам дан набор событий, которые выполняются в порядке их перечисления во входных данных. События бывают следующих типов: - -* _walk_ — Указанный пассажир прошел указанное количество вагонов. Если число положительное, то он идет к хвосту поезда, если отрицательное - то к началу. -* _switch_ — У указанного поезда отцепили с конца указанное количество вагонов и прицепили к другому указанному поезду (в конец) - -После исполнения всех событий вам нужно ответить на вопрос о количестве пассажиров в указанном вагоне. - -Формат выполнения задания -------- - -В файле passangers.py указан шаблон функции, который необходимо заполнить вашим кодом. - -Эта функция получит на вход словарь с начальным расположением пассажиров, массив событий и вагон, для которого нужно указать окончательное количество пассажиров. В качестве результата функция должна вернуть одно число — количество пассажиров после исполнения всех событий. - -События могут содержать невалидный набор операций, например: слишком большое количество вагонов в переходе или сцепке, обращение к несуществующему пользователю. В этом случае не должно возникать исключений, а должен возвращаться результат «-1» (также числом). - -К заданию прилагается несколько тестовых примеров с результатом выполнения. Тестовые примеры будут расширяться по мере нахождения популярных ошибок. diff --git a/homeworks/passengers/passangers.py b/homeworks/passengers/passangers.py deleted file mode 100644 index 32e2182..0000000 --- a/homeworks/passengers/passangers.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- encoding: utf-8 -*- - - -def process(data, events, car): - ''' - ТУТ ДОЛЖЕН БЫТЬ ВАШ КОД - ''' - for train in data: - print(train['name']) - for car in train['cars']: - print('\t{}'.format(car['name'])) - for man in car['people']: - print('\t\t{}'.format(man)) \ No newline at end of file diff --git a/homeworks/passengers/test.py b/homeworks/passengers/test.py deleted file mode 100644 index bda097f..0000000 --- a/homeworks/passengers/test.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- encoding: utf-8 -*- - -import json -from glob import glob - -from passangers import process - -error_message = 'ERROR in file {}. Expected: "{}", got: "{}"' - - -def run_tests(): - for filename in glob('tests/*.json'): - data = json.load(open(filename)) - trains, events, result = data['trains'], data['events'], data['result'] - got = process(trains, events, result['car']) - expected = result['amount'] - if got != expected: - print(error_message.format(filename, expected, got)) - return - print("All tests passed!") - - -if __name__ == '__main__': - run_tests() diff --git a/homeworks/passengers/tests/test1.json b/homeworks/passengers/tests/test1.json deleted file mode 100644 index a1cd9e8..0000000 --- a/homeworks/passengers/tests/test1.json +++ /dev/null @@ -1,56 +0,0 @@ - -{ - "result":{ - "car":"c1", - "amount":2 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":1, - "train_from":"A", - "type":"switch", - "train_to":"B" - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test10.json b/homeworks/passengers/tests/test10.json deleted file mode 100644 index 6a105cb..0000000 --- a/homeworks/passengers/tests/test10.json +++ /dev/null @@ -1,112 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":-1 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-3 - }, - { - "cars":2, - "train_from":"B", - "type":"switch", - "train_to":"A" - }, - { - "cars":1, - "train_from":"C", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Roxanne", - "type":"walk", - "distance":-2 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - "Fred", - "Michael" - ] - }, - { - "name":"c4", - "people":[ - - ] - } - ], - "name":"B" - }, - { - "cars":[ - { - "name":"c5", - "people":[ - "Christine" - ] - }, - { - "name":"c6", - "people":[ - "Roxanne" - ] - } - ], - "name":"C" - } - ] -} diff --git a/homeworks/passengers/tests/test2.json b/homeworks/passengers/tests/test2.json deleted file mode 100644 index 9270ff5..0000000 --- a/homeworks/passengers/tests/test2.json +++ /dev/null @@ -1,72 +0,0 @@ - -{ - "result":{ - "car":"c1", - "amount":3 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":1, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"B", - "type":"switch", - "train_to":"A" - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-1 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test3.json b/homeworks/passengers/tests/test3.json deleted file mode 100644 index ee75be6..0000000 --- a/homeworks/passengers/tests/test3.json +++ /dev/null @@ -1,72 +0,0 @@ - -{ - "result":{ - "car":"c1", - "amount":-1 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":1, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"B", - "type":"switch", - "train_to":"A" - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-3 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test4.json b/homeworks/passengers/tests/test4.json deleted file mode 100644 index ca7f9c6..0000000 --- a/homeworks/passengers/tests/test4.json +++ /dev/null @@ -1,61 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":1 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test5.json b/homeworks/passengers/tests/test5.json deleted file mode 100644 index e18fa35..0000000 --- a/homeworks/passengers/tests/test5.json +++ /dev/null @@ -1,66 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":2 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-1 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test6.json b/homeworks/passengers/tests/test6.json deleted file mode 100644 index 0bb9911..0000000 --- a/homeworks/passengers/tests/test6.json +++ /dev/null @@ -1,71 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":3 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-2 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test7.json b/homeworks/passengers/tests/test7.json deleted file mode 100644 index fd7fa82..0000000 --- a/homeworks/passengers/tests/test7.json +++ /dev/null @@ -1,77 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":3 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-1 - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-2 - }, - { - "cars":3, - "train_from":"B", - "type":"switch", - "train_to":"A" - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - - ] - } - ], - "name":"B" - } - ] -} diff --git a/homeworks/passengers/tests/test8.json b/homeworks/passengers/tests/test8.json deleted file mode 100644 index dd0fe06..0000000 --- a/homeworks/passengers/tests/test8.json +++ /dev/null @@ -1,101 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":5 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-3 - }, - { - "cars":3, - "train_from":"B", - "type":"switch", - "train_to":"A" - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - "Fred", - "Michael" - ] - }, - { - "name":"c4", - "people":[ - - ] - } - ], - "name":"B" - }, - { - "cars":[ - { - "name":"c5", - "people":[ - "Christine" - ] - }, - { - "name":"c6", - "people":[ - "Roxanne" - ] - } - ], - "name":"C" - } - ] -} diff --git a/homeworks/passengers/tests/test9.json b/homeworks/passengers/tests/test9.json deleted file mode 100644 index 6d4e2bb..0000000 --- a/homeworks/passengers/tests/test9.json +++ /dev/null @@ -1,112 +0,0 @@ - -{ - "result":{ - "car":"c3", - "amount":6 - }, - "events":[ - { - "passenger":"Alex", - "type":"walk", - "distance":1 - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-1 - }, - { - "cars":2, - "train_from":"A", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Bob", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alice", - "type":"walk", - "distance":-2 - }, - { - "passenger":"Alex", - "type":"walk", - "distance":-3 - }, - { - "cars":2, - "train_from":"B", - "type":"switch", - "train_to":"A" - }, - { - "cars":1, - "train_from":"C", - "type":"switch", - "train_to":"B" - }, - { - "passenger":"Roxanne", - "type":"walk", - "distance":-2 - } - ], - "trains":[ - { - "cars":[ - { - "name":"c1", - "people":[ - "Alex", - "Alice" - ] - }, - { - "name":"c2", - "people":[ - "Bob" - ] - } - ], - "name":"A" - }, - { - "cars":[ - { - "name":"c3", - "people":[ - "Fred", - "Michael" - ] - }, - { - "name":"c4", - "people":[ - - ] - } - ], - "name":"B" - }, - { - "cars":[ - { - "name":"c5", - "people":[ - "Christine" - ] - }, - { - "name":"c6", - "people":[ - "Roxanne" - ] - } - ], - "name":"C" - } - ] -} diff --git a/homeworks/profile/README.md b/homeworks/profile/README.md new file mode 100644 index 0000000..804d393 --- /dev/null +++ b/homeworks/profile/README.md @@ -0,0 +1,35 @@ +Декоратор @profile +================== + +Написать декоратор, который, если применить его к функции, выводит сообщение +при запуске функции и при ее окончании, причем во втором указано общее +время работы функции. + +Если же применить его к классу, то там должны вести себя все методы класса. + +Пример +------ + +```python +@profile +def foo: + pass + +@profile +class Bar: + def __init__(self): + pass + +foo() +Bar() +``` + +Вывод: + +``` +`foo` started +`foo` finished in 0.01s + +`Bar.__init__` started +`Bar.__init__` finished in 0.01s +``` diff --git a/homeworks/task_queue/Makefile b/homeworks/task_queue/Makefile new file mode 100644 index 0000000..3bd9134 --- /dev/null +++ b/homeworks/task_queue/Makefile @@ -0,0 +1,4 @@ +test: + python3 -m unittest + +.PHONY: test diff --git a/homeworks/task_queue/README.md b/homeworks/task_queue/README.md new file mode 100644 index 0000000..c1d153e --- /dev/null +++ b/homeworks/task_queue/README.md @@ -0,0 +1,68 @@ +Очередь заданий +========= + +Реализовать сервер очередей заданий с заданным протоколом взаимодействия на базе TCP сокетов + +Описание +------- + +Сервер это программа поднимающая socket на порту указанном как параметр запуска (по умолчаню 5555) и работающая до нажатия Ctrl+C. + +Сервер должен поддерживать операцию добавление задания в очередь, взять очередное задание и подтвердить его выполнение. Если задание взято на обработку и не отмечено как выполненное в течение 5 минут (еще один параметр запуска), оно должно вернуться в очередь. + + +Требования к сдаче задания +------- +Код должен быть разбит по методам класса (или классов) и хорошо читаться. За плохой стиль, за неуместное копирование кусков кода, за огромные функции и пр. будет снижаться балл. Разбиение кода на осмысленые логические куски: объекты атрибуты, методы - тоже является частью задания. + +Также необходимо понять как устроены тесты и дописать хотя бы пару своих - это является частью задания. + + +Поддерживаемые команды +------- + +Команды выполняются по одной на соединение и после ответа на команду соединение закрывается. Параллельного обслуживания нескольких соединений не требуется, можно обслуживать соединения по одному. + +Задания должны выдаваться в порядке их добавления в очередь. Выданные задания должны помечаться и не выдаваться пока не истечет таймаут. После истечения таймаута они должны выдаваться в обработку в том же порядке, в котором были добавлены в очередь. + +После подтверждения выполнения задания его можно удалять. + +Присуствует также команда сохранения - которая должна сохранить текущее состояние очереди на диск, в указанную папку. Если сервер после выполнения этой команды остановить, он должен продолжить с очередью в том же состоянии что и сейчас (с результатами всех успешно выполненных команд). + +### Команды + +* __Добавление задания__ `ADD ` + - Параметры + - _queue_ - имя очереди: строка без пробелов + - _length_ - длина содержимого задания: целое число не больше 10^6 + - _data_ - содержимое: массив байт длины _length_ + - Ответ + - _id_ - уникальный идентификатор задания: строка без пробелов не длиннее 128 символов (не равная NONE) + - Примечание + - Если очереди с таким именем нет - то она создается +* __Получение задания__ `GET ` + - Параметры + - _queue_ - имя очереди: строка без пробелов + - Ответ + - _id_ - уникальный идентификатор задания: строка без пробелов не длиннее 128 символов + - _length_ - длина содержимого задания: целое число не больше 10^6 + - _data_ - содержимое: массив байт длины _length_ + - Примечание + - Если очереди с таким именем нет или в очереди нет заданий для обработки ( например, они все выполняются), то возвращается строка `NONE` +* __Подтверждение выполнения__ `ACK ` + - Параметры + - _queue_ - имя очереди: строка без пробелов + - _id_ - уникальный идентификатор задания: строка без пробелов не длиннее 128 символов + - Ответ + - `YES` - если такое задание присутствовало в очереди и было подтверждено его выполнение + - `NO` - если такое задание отсутсвовало в очереди +* __Проверка__ `IN ` + - Параметры + - _queue_ - имя очереди: строка без пробелов + - _id_ - уникальный идентификатор задания: строка без пробелов не длиннее 128 символов + - Ответ + - `YES` - если такое задание присутствует в очереди (не важно выполняется или нет) + - `NO` - если такого задания в очереди нет +* __Сохранение__ `SAVE` + - Ответ + - `OK` diff --git a/homeworks/task_queue/server.py b/homeworks/task_queue/server.py new file mode 100644 index 0000000..f500094 --- /dev/null +++ b/homeworks/task_queue/server.py @@ -0,0 +1,47 @@ + +import argparse + +class TaskQueueServer: + + def __init__(self, ip, port, path, timeout): + pass + + def run(self): + pass + +def parse_args(): + parser = argparse.ArgumentParser(description='This is a simple task queue server with custom protocol') + parser.add_argument( + '-p', + action="store", + dest="port", + type=int, + default=5555, + help='Server port') + parser.add_argument( + '-i', + action="store", + dest="ip", + type=str, + default='0.0.0.0', + help='Server ip adress') + parser.add_argument( + '-c', + action="store", + dest="path", + type=str, + default='./', + help='Server checkpoints dir') + parser.add_argument( + '-t', + action="store", + dest="timeout", + type=int, + default=300, + help='Task maximum GET timeout in seconds') + return parser.parse_args() + +if __name__ == '__main__': + args = parse_args() + server = TaskQueueServer(**args.__dict__) + server.run() diff --git a/homeworks/task_queue/tests/__init__.py b/homeworks/task_queue/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/homeworks/task_queue/tests/test_server.py b/homeworks/task_queue/tests/test_server.py new file mode 100644 index 0000000..957f5da --- /dev/null +++ b/homeworks/task_queue/tests/test_server.py @@ -0,0 +1,66 @@ +from unittest import TestCase + +import time +import socket + +import subprocess + +from server import TaskQueueServer + + +class ServerBaseTest(TestCase): + def setUp(self): + self.server = subprocess.Popen(['python3', 'server.py']) + # даем серверу время на запуск + time.sleep(0.5) + + def tearDown(self): + self.server.terminate() + self.server.wait() + + def send(self, command): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('127.0.0.1', 5555)) + s.send(command) + data = s.recv(1000000) + s.close() + return data + + def test_base_scenario(self): + task_id = self.send(b'ADD 1 5 12345') + self.assertEqual(b'YES', self.send(b'IN 1 ' + task_id)) + + self.assertEqual(task_id + b' 5 12345', self.send(b'GET 1')) + self.assertEqual(b'YES', self.send(b'IN 1 ' + task_id)) + self.assertEqual(b'YES', self.send(b'ACK 1 ' + task_id)) + self.assertEqual(b'NO', self.send(b'ACK 1 ' + task_id)) + self.assertEqual(b'NO', self.send(b'IN 1 ' + task_id)) + + def test_two_tasks(self): + first_task_id = self.send(b'ADD 1 5 12345') + second_task_id = self.send(b'ADD 1 5 12345') + self.assertEqual(b'YES', self.send(b'IN 1 ' + first_task_id)) + self.assertEqual(b'YES', self.send(b'IN 1 ' + second_task_id)) + + self.assertEqual(first_task_id + b' 5 12345', self.send(b'GET 1')) + self.assertEqual(b'YES', self.send(b'IN 1 ' + first_task_id)) + self.assertEqual(b'YES', self.send(b'IN 1 ' + second_task_id)) + self.assertEqual(second_task_id + b' 5 12345', self.send(b'GET 1')) + + self.assertEqual(b'YES', self.send(b'ACK 1 ' + second_task_id)) + self.assertEqual(b'NO', self.send(b'ACK 1 ' + second_task_id)) + + def test_long_input(self): + data = '12345' * 1000 + data = '{} {}'.format(len(data), data) + data = data.encode('utf') + task_id = self.send(b'ADD 1 ' + data) + self.assertEqual(b'YES', self.send(b'IN 1 ' + task_id)) + self.assertEqual(task_id + b' ' + data, self.send(b'GET 1')) + + def test_wrong_command(self): + self.assertEqual(b'ERROR', self.send(b'ADDD 1 5 12345')) + + +if __name__ == '__main__': + unittest.main() diff --git a/homeworks/task_tracker_django/README.md b/homeworks/task_tracker_django/README.md new file mode 100644 index 0000000..8a52550 --- /dev/null +++ b/homeworks/task_tracker_django/README.md @@ -0,0 +1,47 @@ +tasktracke APIr + +================== + +Нужно написать django проект, реализующий api таск трекера. +Таск трекер должен поддерживать вложенные задачи (максимум 1 уровень вложенности) + +Статусы задачи: новая, в работе, выполнена + +Задача может быть не назначена на пользователя. + +Описание +-------- + +апи должно поддерживать следующие методы: + +- создание нового таска +- создание дочернего таска +- привязка таска к пользователю +- смена статуса таска +- поиск таска по id, части заголовка. параметры поиска передавать как get параметры запроса +- получение всех тасков пользователя ( по id пользователя ) +- получение списка таска с вложенными подтасками в формате + +``` +{ + id: Id, + title: Заголовок, + text: текст, + user: { + first_name: Имя пользователя + last_name: Фамилия пользователя + id: Id + }, + status: Статус задачи, + childrens: [{Объект таска}, {}...], +} +``` + +при формировании таска использовать оптимизации запросов ( queryset api select_related ) + +Наполнить базу данных фейковыми данными: +10 000 пользователей +100 000 тасков + +наполнение базы фейковыми данными реализовать в миграции или management команде + diff --git a/homeworks/text_history/Makefile b/homeworks/text_history/Makefile new file mode 100644 index 0000000..8831fc3 --- /dev/null +++ b/homeworks/text_history/Makefile @@ -0,0 +1,4 @@ +test: + python -m unittest + +.PHONY: test diff --git a/homeworks/text_history/README.md b/homeworks/text_history/README.md new file mode 100644 index 0000000..b64c989 --- /dev/null +++ b/homeworks/text_history/README.md @@ -0,0 +1,40 @@ +История измения текста +====================== + +Нужно реализовать класс `TextHistory`, который хранит текст и историю его изменений. +Поддерживается три типа изменений — вставка текста (insert), +замена текста (replace) и удаление (delete). + +Интерфейс: +* `h.text` — текущий текст, read only +* `h.version` — текущая версия, read only. Начинается с 0 и только растет. +* `h.insert(text, pos=pos)` — вставить текст с позиции pos (по умолчанию — конец строки). +Кидает ValueError, если указана недопустимая позиция. Возвращает номер новой версии. +* `h.replace(text, pos=pos)` — заменить текст с позиции pos (по умолчанию — конец строки). +Кидает ValueError, если указана недопустимая позиция. Замена за пределами строки работает как +вставка (т. е. текст дописывается). Возвращает номер новой версии. +* `h.delete(pos, length)` — удаляет `length` символов начиная с позиции `pos`. +Возвращает номер новой версии. +* `h.action(action)` — применяет действие `action` (см. ниже). Возвращает номер новой версии. +Версия растет не на 1, а устанавливается та, которая указана в `action`. +* `h.get_actions(from_version=v1, to_version=v2)` — возвращает `list` всех действий +между двумя версиями. + +Действия +-------- + +Действия выражаются наследниками `Action`: `InsertAction`, `ReplaceAction` и `DeleteAction`. +Конструктор принимает позицию (`pos`) и строку (`text`) или позицию и длину (`length`), +а так же стартовую и конечную версию. +Если версии указаны неверно, кидается ValueError. +Единственный публичный метод `apply` принимает строку и возвращает модифицированную строку. + + +Оптимизации +----------- + +Метод `get_actions` должен уметь применять к полученному списку оптимизации, которые +сокращают количество действий. Например, очевидно, что последовательные `insert('x', pos=42)` +и `insert('y', pos=43)` можно заменить на `insert('xy', pos=42)`. + +Тестов на это нет, надо придумать минимум две любые оптимизации и реализовать. diff --git a/homeworks/text_history/tests/__init__.py b/homeworks/text_history/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/homeworks/text_history/tests/test_text_history.py b/homeworks/text_history/tests/test_text_history.py new file mode 100644 index 0000000..487b173 --- /dev/null +++ b/homeworks/text_history/tests/test_text_history.py @@ -0,0 +1,173 @@ +from unittest import TestCase + +from text_history import TextHistory, InsertAction, ReplaceAction, DeleteAction + + +class TextHistoryTestCase(TestCase): + def test_text__trivial(self): + h = TextHistory() + + self.assertEqual('', h.text) + with self.assertRaises(AttributeError): + h.text = 'NEW' + + def test_version__trivial(self): + h = TextHistory() + + self.assertEqual(0, h.version) + with self.assertRaises(AttributeError): + h.version = 42 + + def test_action(self): + h = TextHistory() + action = InsertAction(pos=0, text='abc', from_version=0, to_version=10) + + self.assertEqual(10, h.action(action)) + self.assertEqual('abc', h.text) + self.assertEqual(10, h.version) + + def test_action__bad(self): + h = TextHistory() + action = InsertAction(pos=0, text='abc', from_version=10, to_version=10) + + with self.assertRaises(ValueError): + h.action(action) + + def test_insert(self): + h = TextHistory() + + self.assertEqual(1, h.insert('abc')) + self.assertEqual('abc', h.text) + self.assertEqual(1, h.version) + + self.assertEqual(2, h.insert('xyz', pos=2)) + self.assertEqual('abxyzc', h.text) + self.assertEqual(2, h.version) + + self.assertEqual(3, h.insert('END')) + self.assertEqual('abxyzcEND', h.text) + self.assertEqual(3, h.version) + + self.assertEqual(4, h.insert('BEGIN', pos=0)) + self.assertEqual('BEGINabxyzcEND', h.text) + self.assertEqual(4, h.version) + + def test_insert__bad(self): + h = TextHistory() + self.assertEqual(1, h.insert('abc')) + + with self.assertRaises(ValueError): + h.insert('abc', pos=10) + + with self.assertRaises(ValueError): + h.insert('abc', pos=-1) + + def test_replace(self): + h = TextHistory() + + self.assertEqual(1, h.replace('abc')) + self.assertEqual('abc', h.text) + self.assertEqual(1, h.version) + + self.assertEqual(2, h.replace('xyz', pos=2)) + self.assertEqual('abxyz', h.text) + self.assertEqual(2, h.version) + + self.assertEqual(3, h.replace('X', pos=2)) + self.assertEqual('abXyz', h.text) + self.assertEqual(3, h.version) + + self.assertEqual(4, h.replace('END')) + self.assertEqual('abXyzEND', h.text) + self.assertEqual(4, h.version) + + def test_replace__bad(self): + h = TextHistory() + self.assertEqual(1, h.insert('abc')) + + with self.assertRaises(ValueError): + h.replace('abc', pos=10) + + with self.assertRaises(ValueError): + h.replace('abc', pos=-1) + + def test_delete(self): + h = TextHistory() + self.assertEqual(1, h.insert('abc xyz')) + + self.assertEqual(2, h.delete(pos=1, length=2)) + self.assertEqual('a xyz', h.text) + self.assertEqual(2, h.version) + + self.assertEqual(3, h.delete(pos=3, length=0)) + self.assertEqual('a xyz', h.text) + self.assertEqual(3, h.version) + + def test_delete__bad(self): + h = TextHistory() + self.assertEqual(1, h.insert('abc')) + + with self.assertRaises(ValueError): + h.delete(pos=10, length=2) + + with self.assertRaises(ValueError): + h.delete(pos=1, length=3) + + with self.assertRaises(ValueError): + h.delete(pos=-1, length=2) + + def test_get_actions(self): + h = TextHistory() + h.insert('a') + h.insert('bc') + h.replace('B', pos=1) + h.delete(pos=0, length=1) + self.assertEqual('Bc', h.text) + + actions = h.get_actions(1) + + self.assertEqual(3, len(actions)) + insert, replace, delete = actions + self.assertIsInstance(insert, InsertAction) + self.assertIsInstance(replace, ReplaceAction) + self.assertIsInstance(delete, DeleteAction) + + # insert + self.assertEqual(1, insert.from_version) + self.assertEqual(2, insert.to_version) + self.assertEqual('bc', insert.text) + self.assertEqual(1, insert.pos) + + # replace + self.assertEqual(2, replace.from_version) + self.assertEqual(3, replace.to_version) + self.assertEqual('B', replace.text) + self.assertEqual(1, replace.pos) + + # delete + self.assertEqual(3, delete.from_version) + self.assertEqual(4, delete.to_version) + self.assertEqual(0, delete.pos) + self.assertEqual(1, delete.length) + + def test_get_actions__bad(self): + h = TextHistory() + h.insert('a') + h.insert('b') + h.insert('c') + + with self.assertRaises(ValueError): + h.get_actions(0, 10) + with self.assertRaises(ValueError): + h.get_actions(10, 10) + with self.assertRaises(ValueError): + h.get_actions(2, 1) + with self.assertRaises(ValueError): + h.get_actions(-1, 1) + + def test_get_actions__empty(self): + h = TextHistory() + self.assertEqual([], h.get_actions()) + + h.insert('a') + self.assertEqual([], h.get_actions(0, 0)) diff --git a/homeworks/text_history/text_history.py b/homeworks/text_history/text_history.py new file mode 100644 index 0000000..514c356 --- /dev/null +++ b/homeworks/text_history/text_history.py @@ -0,0 +1,18 @@ +class TextHistory: + pass + + +class Action: + pass + + +class InsertAction(Action): + pass + + +class ReplaceAction(Action): + pass + + +class DeleteAction(Action): + pass diff --git a/homeworks/whenthen/README.md b/homeworks/whenthen/README.md new file mode 100644 index 0000000..7b6e328 --- /dev/null +++ b/homeworks/whenthen/README.md @@ -0,0 +1,32 @@ +Декоратор @whenthen +=================== + +Нужно написать декоратор, который позволяет указывать для функции специальные случаи значений аргументов, при которых надо выполнять не базовый код, а какой-то другой. + +```python +@whenthen +def fract(x): + return x * fract(x - 1) + + +@fract.when +def fract(x): + return x == 0 + + +@fract.then +def fract(x): + return 1 + + +@fract.when +def fract(x): + return x > 5 + + +@fract.then +def fract(x): + return x * (x - 1) * (x - 2) * (x - 3) * (x - 4) * fract(x - 5) +``` + +Условие описывается с помощью дополнительного декоратора `@when`, действие — с помощью `@then`. Каждый `then` относится к предыдущему `when`, т. е. имеет значение порядок обяъвляения. Если два `when` объявлено подряд, `then` объявлен не после `when`, не хватает `then` и т. д. — кидать ошибку. diff --git a/live/2018-04-06/action.py b/live/2018-04-06/action.py new file mode 100644 index 0000000..e5108c1 --- /dev/null +++ b/live/2018-04-06/action.py @@ -0,0 +1,21 @@ +class Action: + def __init__(self, name, amount_resource_pairs): + self._name = name + self._amount_resource_pairs = amount_resource_pairs + + @property + def name(self): + return self._name + + def _can_consume(self, dt): + return all( + resource.can_consume(dt, amount) + for amount, resource in self._amount_resource_pairs + ) + + def consume(self, dt): + if not self._can_consume(dt): + raise RuntimeError("Can't consume") + + for amount, resource in self._amount_resource_pairs: + resource.consume(dt, amount) diff --git a/live/2018-04-06/config.yaml b/live/2018-04-06/config.yaml new file mode 100644 index 0000000..e68787b --- /dev/null +++ b/live/2018-04-06/config.yaml @@ -0,0 +1,26 @@ +actions: + - name: post + consumes: + hdd: 1 + ram: 2 + - name: get + consumes: + ram: 1 + lan: 3 +resources: + - name: lan + limits: + - quota: 5 + period: 60 + - quota: 10 + period: 600 + - name: ram + limits: + - quota: 50 + period: 60 + - quota: 100 + period: 600 + - name: hdd + limits: + - quota: 1 + period: 2 diff --git a/live/2018-04-06/consumable.py b/live/2018-04-06/consumable.py new file mode 100644 index 0000000..a5baef1 --- /dev/null +++ b/live/2018-04-06/consumable.py @@ -0,0 +1,30 @@ +from abc import ABCMeta, abstractmethod + + +class Consumable(metaclass=ABCMeta): + @abstractmethod + def can_consume(self, dt, to_consume): + pass + + @abstractmethod + def consume(self, dt, to_consume): + pass + + +class NestedConsumable(Consumable): + @abstractmethod + def _get_nested_consumables(self): + pass + + def can_consume(self, dt, to_consume=1): + return all( + consumable.can_consume(dt, to_consume) + for consumable in self._get_nested_consumables() + ) + + def consume(self, dt, to_consume=1): + if not self.can_consume(dt, to_consume): + raise RuntimeError("Can't consume") + + for consumable in self._get_nested_consumables(): + consumable.consume(dt, to_consume) diff --git a/live/2018-04-06/limit.py b/live/2018-04-06/limit.py new file mode 100644 index 0000000..df528b8 --- /dev/null +++ b/live/2018-04-06/limit.py @@ -0,0 +1,22 @@ +from consumable import Consumable + + +class Limit(Consumable): + def __init__(self, quota, period): + self._quota = quota + self._period = period + self._start = None + self._consumed = 0 + + def can_consume(self, dt, to_consume=1): + if self._start is None or self._start + self._period <= dt: + self._start = dt + self._consumed = 0 + + return self._consumed + to_consume <= self._quota + + def consume(self, dt, to_consume=1): + if not self.can_consume(dt, to_consume): + raise RuntimeError("Can't consume") + + self._consumed += to_consume diff --git a/live/2018-04-06/repository.py b/live/2018-04-06/repository.py new file mode 100644 index 0000000..170e648 --- /dev/null +++ b/live/2018-04-06/repository.py @@ -0,0 +1,20 @@ +class Repository: + def __init__(self, actions): + self._actions = actions + self._actions_index = self._get_actions_index() + + def do_action(self, action_name, dt): + action = self._get_action_by_name(action_name) + if action is None: + raise ValueError("Action couldn't be found") + try: + action.consume(dt) + return True + except RuntimeError: + return False + + def _get_actions_index(self): + return {action.name: action for action in self._actions} + + def _get_action_by_name(self, action_name): + return self._actions_index.get(action_name) diff --git a/live/2018-04-06/resource.py b/live/2018-04-06/resource.py new file mode 100644 index 0000000..5543c88 --- /dev/null +++ b/live/2018-04-06/resource.py @@ -0,0 +1,10 @@ +from consumable import NestedConsumable + + +class Resource(NestedConsumable): + def __init__(self, name, limits): + self._name = name + self._limits = limits + + def _get_nested_consumables(self): + return self._limits diff --git a/live/2018-04-06/tests/__init__.py b/live/2018-04-06/tests/__init__.py new file mode 100644 index 0000000..143cc02 --- /dev/null +++ b/live/2018-04-06/tests/__init__.py @@ -0,0 +1,21 @@ +from datetime import timedelta +from unittest import TestCase + +from limit import Limit +from resource import Resource + + +class BaseTestCase(TestCase): + def setUp(self): + self._resource1 = Resource('test_resource1', [ + Limit(2, timedelta(seconds=1)), + Limit(3, timedelta(seconds=1)), + ]) + self._resource2 = Resource('test_resource2', [ + Limit(6, timedelta(seconds=1)), + Limit(7, timedelta(seconds=1)), + ]) + self._resource3 = Resource('test_resource3', [ + Limit(10, timedelta(seconds=1)), + Limit(10, timedelta(seconds=1)), + ]) diff --git a/live/2018-04-06/tests/test_action.py b/live/2018-04-06/tests/test_action.py new file mode 100644 index 0000000..b52c35b --- /dev/null +++ b/live/2018-04-06/tests/test_action.py @@ -0,0 +1,32 @@ +from datetime import timedelta, datetime + +from action import Action +from tests import BaseTestCase + + +class ActionTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self._action = Action('test', [ + (2, self._resource1), + (3, self._resource2), + ]) + + def test__can_consume(self): + action = self._action + + dt = datetime(2018, 1, 1) + self.assertTrue(action._can_consume(dt)) + action.consume(dt) + self.assertFalse(action._can_consume(dt)) + self.assertTrue(action._can_consume(dt + timedelta(seconds=1))) + + def test_consume(self): + action = self._action + + dt = datetime(2018, 1, 1) + action.consume(dt) + with self.assertRaises(RuntimeError): + action.consume(dt) + action.consume(dt + timedelta(seconds=1)) + diff --git a/live/2018-04-06/tests/test_limit.py b/live/2018-04-06/tests/test_limit.py new file mode 100644 index 0000000..b96efe8 --- /dev/null +++ b/live/2018-04-06/tests/test_limit.py @@ -0,0 +1,28 @@ +from datetime import timedelta, datetime + +from limit import Limit +from tests import BaseTestCase + + +class LimitTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self._limit = Limit(2, timedelta(seconds=3)) + + def test_can_consume(self): + limit = self._limit + + dt = datetime(2018, 1, 1) + self.assertTrue(limit.can_consume(dt)) + self.assertTrue(limit.can_consume(dt + timedelta(seconds=1), 2)) + self.assertFalse(limit.can_consume(dt + timedelta(seconds=2), 4)) + self.assertTrue(limit.can_consume(dt + timedelta(seconds=3), 1)) + + def test_consume(self): + limit = self._limit + + dt = datetime(2018, 1, 1) + limit.consume(dt) + limit.consume(dt + timedelta(seconds=1), 1) + with self.assertRaises(RuntimeError): + limit.consume(dt + timedelta(seconds=2), 1) diff --git a/live/2018-04-06/tests/test_repository.py b/live/2018-04-06/tests/test_repository.py new file mode 100644 index 0000000..da76b68 --- /dev/null +++ b/live/2018-04-06/tests/test_repository.py @@ -0,0 +1,39 @@ +from datetime import datetime + +from action import Action +from repository import Repository +from tests import BaseTestCase + + +class RepositoryTestCase(BaseTestCase): + def setUp(self): + super().setUp() + + self._action1 = Action('action1', [ + (2, self._resource1), + (3, self._resource2), + ]) + self._action2 = Action('action2', [ + (3, self._resource3), + (3, self._resource2), + ]) + self._repository = Repository([self._action1, self._action2]) + + def test_do_action(self): + dt = datetime(2018, 1, 1) + self.assertTrue(self._repository.do_action('action1', dt)) + self.assertFalse(self._repository.do_action('action1', dt)) + + with self.assertRaises(ValueError): + self._repository.do_action('UNKNOWN', datetime(2018, 1, 1)) + + def test__get_action_by_name(self): + self.assertIs(self._repository._get_action_by_name('action1'), self._action1) + self.assertIs(self._repository._get_action_by_name('action2'), self._action2) + self.assertIsNone(self._repository._get_action_by_name('UNKNOWN')) + + def test__get_actions_index(self): + self.assertDictEqual( + self._repository._get_actions_index(), + dict(action1=self._action1, action2=self._action2) + ) diff --git a/live/2018-04-06/tests/test_resource.py b/live/2018-04-06/tests/test_resource.py new file mode 100644 index 0000000..48b46c9 --- /dev/null +++ b/live/2018-04-06/tests/test_resource.py @@ -0,0 +1,22 @@ +from datetime import timedelta, datetime + +from tests import BaseTestCase + + +class ResourceTestCase(BaseTestCase): + def test_can_consume(self): + resource = self._resource1 + + dt = datetime(2018, 1, 1) + self.assertTrue(resource.can_consume(dt)) + self.assertFalse(resource.can_consume(dt, 3)) + self.assertTrue(resource.can_consume(dt, 2)) + + def test_consume(self): + resource = self._resource1 + + dt = datetime(2018, 1, 1) + resource.consume(dt) + with self.assertRaises(RuntimeError): + resource.consume(dt, 2) + resource.consume(dt + timedelta(seconds=1), 2) diff --git a/live/2018-05-23/client.py b/live/2018-05-23/client.py new file mode 100644 index 0000000..d50a378 --- /dev/null +++ b/live/2018-05-23/client.py @@ -0,0 +1,22 @@ +import asyncio +import json + +import aiohttp + + +async def get_score_and_print(i): + async with aiohttp.ClientSession() as session: + async with session.get('http://localhost:8081/{}'.format(i)) as resp: + print(await resp.text()) + + +async def main(): + i = 0 + while True: + i += 1 + asyncio.ensure_future(get_score_and_print(i)) + await asyncio.sleep(0.01) + + +loop = asyncio.get_event_loop() +loop.run_until_complete(main()) \ No newline at end of file diff --git a/live/2018-05-23/proxy.py b/live/2018-05-23/proxy.py new file mode 100644 index 0000000..b88bf27 --- /dev/null +++ b/live/2018-05-23/proxy.py @@ -0,0 +1,71 @@ +import asyncio +import json + +import aiohttp + +from aiohttp import web + +routes = web.RouteTableDef() + + +class Queue: + _INSTANCE = None + + def __init__(self): + self._queue = [] + + @classmethod + def get_instance(cls): + if cls._INSTANCE is None: + cls._INSTANCE = cls() + + return cls._INSTANCE + + def add(self, product_id, future): + self._queue.append((product_id, future)) + + async def infinite_process(self): + while True: + await asyncio.sleep(1) + current_queue = self._queue[:] + + if current_queue: # [(1, f1), (2, f2), (3, f3)] + async with aiohttp.ClientSession() as session: + product_ids = [product_id for product_id, future in current_queue] + print(product_ids) + async with session.post('http://localhost:8080/', data=json.dumps(product_ids)) as resp: + response_data = await resp.json() # [1, 8, 27] + for i, score in enumerate(response_data): + product_id, future = current_queue[i] + future.set_result(score) + + self._queue = [] + + +def process_batch(batch): + return [x ** 3 for x in batch] + + +async def get_score(product_id): + future = asyncio.get_event_loop().create_future() + Queue.get_instance().add(product_id, future) + + return await future + + +@routes.get('/{product_id}') +async def get_data(request): + product_id = int(request.match_info['product_id']) + + return web.json_response(await get_score(product_id)) + + +def main(): + asyncio.get_event_loop().create_task(Queue.get_instance().infinite_process()) + app = web.Application() + app.add_routes(routes) + web.run_app(app, port=8081) + + +if __name__ == '__main__': + main() diff --git a/live/2018-05-23/server.py b/live/2018-05-23/server.py new file mode 100644 index 0000000..32e83ec --- /dev/null +++ b/live/2018-05-23/server.py @@ -0,0 +1,30 @@ +from aiohttp import web + +routes = web.RouteTableDef() + + +def process_batch(batch): + return [x ** 3 for x in batch] + + +@routes.get('/') +async def hello(request): + return web.Response(text="Hello, world") + + +@routes.post('/') +async def get_score(request): + data = await request.json() + print(data) + + return web.json_response(process_batch(data)) + + +def main(): + app = web.Application() + app.add_routes(routes) + web.run_app(app) + + +if __name__ == '__main__': + main() diff --git a/live/2018-05-23/test_server.py b/live/2018-05-23/test_server.py new file mode 100644 index 0000000..7f99d16 --- /dev/null +++ b/live/2018-05-23/test_server.py @@ -0,0 +1,8 @@ +import unittest + +from server import process_batch + + +class ServerTestCase(unittest.TestCase): + def test_process_batch(self): + self.assertEqual([1, 8, 27], process_batch([1, 2, 3])) \ No newline at end of file diff --git a/live/2018-10-19/notes/__init__.py b/live/2018-10-19/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/live/2018-10-19/notes/access.py b/live/2018-10-19/notes/access.py new file mode 100644 index 0000000..34848ac --- /dev/null +++ b/live/2018-10-19/notes/access.py @@ -0,0 +1,38 @@ +from crypt import crypt + + +def get_account_key(username): + return f'account:{username}' + + +def get_token_key(token): + return f'token:{token}' + + +def get_ro_token_key(token): + return f'ro_token:{token}' + + +def check_password(username, password, storage): + key = get_account_key(username) + digest = storage.get(key) + if digest is None: + raise NoSuchUserError() + + return crypt(password, digest) == digest + + +def check_token(token, expected_username, storage): + key = get_token_key(token) + username = storage.get(key) + return username == expected_username + + +def check_ro_token(token, expected_username, storage): + key = get_ro_token_key(token) + username = storage.get(key) + return username == expected_username + + +class NoSuchUserError(Exception): + pass \ No newline at end of file diff --git a/live/2018-10-19/notes/account.py b/live/2018-10-19/notes/account.py new file mode 100644 index 0000000..10421c1 --- /dev/null +++ b/live/2018-10-19/notes/account.py @@ -0,0 +1,47 @@ +from crypt import crypt, mksalt, METHOD_SHA512 +from uuid import uuid4 + +from notes.access import check_password, check_token, get_account_key, get_token_key, get_ro_token_key + + +class Account: + def __init__(self, username): + self._username = username + + @classmethod + def create(cls, username, password, storage): + key = get_account_key(username) + + salt = mksalt(METHOD_SHA512) + digest = crypt(password, salt) + storage.set(key, digest) + + def _check_password(self, password, storage): + return check_password(self._username, password, storage) + + def _check_token(self, token, storage): + return check_token(token, self._username, storage) + + def create_token(self, password, storage): + if not self._check_password(password, storage): + raise InvalidPasswordError() + + token = str(uuid4()) + key = get_token_key(token) + storage.set(key, self._username) + + def create_ro_token(self, master_token, storage): + if not self._check_token(master_token, storage): + raise InvalidTokenError() + + token = str(uuid4()) + key = get_ro_token_key(token) + storage.set(key, self._username) + + +class InvalidPasswordError(Exception): + pass + + +class InvalidTokenError(Exception): + pass diff --git a/live/2018-10-19/notes/note.py b/live/2018-10-19/notes/note.py new file mode 100644 index 0000000..ebb6d29 --- /dev/null +++ b/live/2018-10-19/notes/note.py @@ -0,0 +1,2 @@ +class Note: + pass \ No newline at end of file diff --git a/live/2018-10-19/notes/storage.py b/live/2018-10-19/notes/storage.py new file mode 100644 index 0000000..57b740c --- /dev/null +++ b/live/2018-10-19/notes/storage.py @@ -0,0 +1,24 @@ +from abc import ABCMeta, abstractmethod +from typing import Optional + + +class AbstractStorage(metaclass=ABCMeta): + @abstractmethod + def get(self, key: str) -> Optional[str]: + pass + + @abstractmethod + def set(self, key: str, value: str) -> None: + pass + + +class MemoryStorage(AbstractStorage): + def __init__(self): + super().__init__() + self._data = {} + + def get(self, key: str) -> Optional[str]: + return self._data.get(key) + + def set(self, key: str, value: str) -> None: + self._data[key] = value \ No newline at end of file diff --git a/live/2018-10-19/tests/__init__.py b/live/2018-10-19/tests/__init__.py new file mode 100644 index 0000000..056c322 --- /dev/null +++ b/live/2018-10-19/tests/__init__.py @@ -0,0 +1,5 @@ +from unittest import TestCase + + +class BaseTestCase(TestCase): + pass diff --git a/live/2018-10-19/tests/notes/__init__.py b/live/2018-10-19/tests/notes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/live/2018-10-19/tests/notes/test_account.py b/live/2018-10-19/tests/notes/test_account.py new file mode 100644 index 0000000..d97327f --- /dev/null +++ b/live/2018-10-19/tests/notes/test_account.py @@ -0,0 +1,99 @@ +from unittest.mock import MagicMock, patch + +from notes.account import Account, InvalidPasswordError, InvalidTokenError +from notes.access import NoSuchUserError +from tests import BaseTestCase + + +class AccountTestCase(BaseTestCase): + def test_create(self): + storage = MagicMock() + + fake_set_called = False + + def fake_set(key, _): + nonlocal fake_set_called + fake_set_called = True + self.assertEqual('account:katya', key) + storage.set = fake_set + + Account.create('katya', 'abc', storage) + + self.assertTrue(fake_set_called) + + @patch('notes.access.crypt') + def test__check_password__false(self, patched_crypt): + storage = MagicMock() + storage.get.return_value = 'DIGEST' + + account = Account('katya') + self.assertFalse(account._check_password('abc', storage)) + + patched_crypt.assert_called_once_with('abc', 'DIGEST') + + @patch('notes.access.crypt') + def test__check_password__true(self, patched_crypt): + storage = MagicMock() + storage.get.return_value = 'DIGEST' + patched_crypt.return_value = 'DIGEST' + + account = Account('katya') + self.assertTrue(account._check_password('abc', storage)) + + patched_crypt.assert_called_once_with('abc', 'DIGEST') + + def test__check_password__no_such_user(self): + storage = MagicMock() + storage.get.return_value = None + + account = Account('katya') + with self.assertRaises(NoSuchUserError): + account._check_password('abc', storage) + + @patch('notes.account.uuid4') + def test_create_token(self, patched_uuid4): + patched_uuid4.return_value = 'UUID' + + storage = MagicMock() + account = Account('katya') + account._check_password = lambda p, s: True + + account.create_token('abc', storage) + + storage.set.assert_called_once_with('token:UUID', 'katya') + + def test__check_token(self): + storage = MagicMock() + storage.get.return_value = 'vasya' + + katya_acc = Account('katya') + self.assertFalse(katya_acc._check_token('abc', storage)) + + vasya_acc = Account('vasya') + self.assertTrue(vasya_acc._check_token('abc', storage)) + + def test_create_token__wrong_password(self): + account = Account('katya') + account._check_password = lambda p, s: False + + with self.assertRaises(InvalidPasswordError): + account.create_token('abc', None) + + @patch('notes.account.uuid4') + def test_create_ro_token(self, patched_uuid4): + patched_uuid4.return_value = 'UUID' + + storage = MagicMock() + account = Account('katya') + account._check_token = lambda t, s: True + + account.create_ro_token('abc', storage) + + storage.set.assert_called_once_with('ro_token:UUID', 'katya') + + def test_create_token__wrong_token(self): + account = Account('katya') + account._check_token = lambda p, s: False + + with self.assertRaises(InvalidTokenError): + account.create_ro_token('abc', None) diff --git a/live/2018-10-19/tests/notes/test_storage.py b/live/2018-10-19/tests/notes/test_storage.py new file mode 100644 index 0000000..56d6540 --- /dev/null +++ b/live/2018-10-19/tests/notes/test_storage.py @@ -0,0 +1,22 @@ +from notes.storage import MemoryStorage +from tests import BaseTestCase + + +class MemoryStorageTestCase(BaseTestCase): + def test_get(self): + storage = MemoryStorage() + + storage._data['x'] = 1 + + self.assertEqual(1, storage.get('x')) + self.assertIsNone(storage.get('y')) + + def test_set(self): + storage = MemoryStorage() + + storage.set('x', 1) + + self.assertEqual( + dict(x=1), + storage._data + ) \ No newline at end of file diff --git a/talks/02-data-structures/notebook.ipynb b/talks/02-data-structures/notebook.ipynb deleted file mode 100644 index ada9968..0000000 --- a/talks/02-data-structures/notebook.ipynb +++ /dev/null @@ -1,1777 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Структуры данных" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Мы рассмотрим для начала структуры данных, доступные \"из коробки\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# built-in структуры данных\n", - "list, tuple, dict, set, frozenset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Список: list" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Позволяет хранить последовательность элементов одного или разных типов с Сохранением порядка добавления элементов в список.\n", - "\n", - "Изменяемые\n", - "Не хешируемые" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Создание списков" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# создаем list используя квадратные скобки или ф-ю list\n", - "empty_list = []\n", - "empty_list = list()\n", - "not_empty_list = list([1,2,3])\n", - "not_empty_list = [1, 2, 3]\n", - "item = [None] * 100" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# list может содержать любые объекты, внутри Python они хранятся как массив указателей\n", - "example_list = [1, True, \"a\"]\n", - "for element in example_list:\n", - " print(element)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# добавляем элемент в конец списка O(1)\n", - "example_list.append(\"foo\")\n", - "print(example_list)\n", - "\n", - "# добавляем элемент в начало O(n)\n", - "example_list.insert(0, \"bar\")\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_list.append(example_list)\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "benchmark_list = []\n", - "%timeit -n10000 benchmark_list.append(\"foo\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "benchmark_list = []\n", - "%timeit -n10000 benchmark_list.insert(0, \"bar\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_list = [0, 1, 2, 3, 4, 5, 6]\n", - "\n", - "# доступ к элементу O(1)\n", - "print(example_list[0])\n", - "print(example_list[-1])\n", - "\n", - "# Изменение по индексу O(1)\n", - "example_list[6] = 10" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Удаление элемента O(n)\n", - "print(example_list)\n", - "del example_list[-1]\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_list.remove(5)\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_list.remove(5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "del example_list[5]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Обращение к несуществующему индексу.\n", - "example_list[100]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# срезы\n", - "print(example_list)\n", - "print(example_list[2:-1])\n", - "print(example_list[2:])\n", - "print(example_list[2:4])\n", - "print(example_list[::2])\n", - "print(example_list[-1])\n", - "print(example_list[::-1])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# при простом присваивании новая переменная ссылается на тот же список\n", - "example_list = [1, 2, 3, 4, 5]\n", - "another_list = example_list\n", - "another_list[0] = 100\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# копирование списка\n", - "example_list = [0, 1, 2, 3, 4, 5]\n", - "another_list = example_list[:]\n", - "# import copy\n", - "# another_list = copy.copy(example_list)\n", - "# another_list = list(example_list)\n", - "another_list[0] = 100\n", - "print(example_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# но вложенные объекты не копируются (shallow copy)\n", - "nested_list = [1, 2, 3]\n", - "example_list = [nested_list, 1, 2, 3, 4, 5]\n", - "another_list = example_list[:]\n", - "nested_list.append(4)\n", - "print(another_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# используем ф-ю deepcopy из пакета copy\n", - "from copy import deepcopy\n", - "nested_list = [1, 2, 3]\n", - "example_list = [nested_list, 1, 2, 3, 4, 5]\n", - "another_list = deepcopy(example_list)\n", - "nested_list.append(4)\n", - "print(another_list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Немного о сортировке" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Сортировка списка\n", - "unsorted_list = [2, 1, 5, 4, 3]\n", - "unsorted_list.sort()\n", - "print(unsorted_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# сортировка in-place\n", - "unsorted_list = [2, 1, 5, 4, 3]\n", - "print(sorted(unsorted_list))\n", - "print(unsorted_list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Кортеж: tuple" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Так же как и список позволяет хранить последовательность элементов одного или разного типа\n", - "\n", - "* Неизменяемые (Immutable) => защищают данные\n", - "* Быстрее списка\n", - "* Хешируемые (Hashable)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# создадим пустой tuple используя круглые скобки или ф-ю tuple\n", - "empty_tuple = ()\n", - "empty_tuple = tuple()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "len(empty_tuple)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hash(empty_tuple)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_tuple = (1, \"a\", True)\n", - "for element in example_tuple:\n", - " print(element)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "len(example_tuple)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_tuple = (1, 2, 3)\n", - "print(example_tuple[0])\n", - "print(example_tuple[1:])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_tuple[0] = 2017" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_list[200]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = (123)\n", - "a\n", - "print(type(a))\n", - "a = (123, ) # в кортеже с одним элементом запятая обязательна!\n", - "print(type(a))\n", - "a = 1,2,3\n", - "print (type(a))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Важно понимать, что **неизменяемым является сам tuple, но не объекты, которые он содержит!**. Например:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "first_list = [1, 2]\n", - "second_list = [3, 4]\n", - "example_tuple = (first_list, second_list)\n", - "print(example_tuple)\n", - "first_list.append(5)\n", - "print(example_tuple)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Написать в одну строку все цифры от 0 до 100\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "long_arr = [1] * 101\n", - "result = \"\"\n", - "current_item = 0\n", - "for index, item in enumerate(long_arr):\n", - " result += str(current_item)\n", - " current_item += item\n", - "\n", - "print(result)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"\".join([str(item) for item in range(0, 101)]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Словарь: dict" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Позволяет хранить пары ключ-значение, для быстрого доступа к значению по ключу. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# создаем словарь используя фигурные скобки или ф-ю dict\n", - "empty_dict = dict()\n", - "empty_dict = {}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dict(a=4, b=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_dict = {\n", - " \"a\": 1,\n", - " \"b\": True,\n", - " (1, 2, 3): True\n", - "}\n", - "\n", - "# Добавляем ключ и соответствующее ему значение в словарь O(1)\n", - "example_dict[\"c\"] = 4\n", - "print(example_dict)\n", - "\n", - "# Поиск значения по ключу O(1)\n", - "print(example_dict[\"c\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Доступ к несуществующему ключу:\n", - "print(example_dict[\"x\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "# ключом словаря может быть только hashable объект\n", - "example_dict[example_list] = True" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Объект является hashable объектом, если он имеет hash-значение, неизменное в процессе его жизни (т.е. у объекта должен быть определен **______hash______()** метод) и объект можно сравнить с другими объектами (**________eq________()** или **________cmp________()** методы). Одинаковые объекты должны иметь одинаковое знаение hash.\n", - "\n", - "Если объект hashable, то он может быть использован как ключ в словаре или член множества (так как эти структуры используют значение hash внутри своей имплементации).\n", - "\n", - "Все immutable встроенные объекты в Python - hashable, mutable контейнеры - нет (списки, словари). Инстансы классов - hashable (их hash значение - их id, то есть адрес в памяти)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hash([1,2,3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "hash((1,2,3))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# если мы не уверены, что ключ есть в словаре - можно воспользоваться методом get\n", - "print(example_dict.get(\"x\"))\n", - "print(example_dict.get(\"x\", \"default value\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# итерируемся по ключам и значениям - внимание, порядок недетерминирован (до Python 3.6)!\n", - "for key, value in example_dict.items():\n", - " print(\"{}: {}\".format(key, value))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# посмотрим на разницу поиска значения в словаре и списке\n", - "search_list = list(range(100000)) \n", - "search_dict = dict.fromkeys(list(range(100000)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit -n10000 0 in search_list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit -n10000 0 in search_dict" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit -n1000 99999 in search_list" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%timeit -n10000 99999 in search_dict" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# объединение словарей\n", - "d1 = {\"a\": 1, \"c\": 3}\n", - "d2 = {\"b\": 2}\n", - "d1.update(d2)\n", - "print(d1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# копирование\n", - "d1 = {\"a\": 1, \"c\": 3}\n", - "d2 = d1.copy()\n", - "d2[\"a\"] = 100\n", - "print(d1)\n", - "print(d2)\n", - "print()\n", - "\n", - "# вложенные не копируются\n", - "nested = [1, 2, 3]\n", - "d1 = {\"a\": nested}\n", - "d2 = d1.copy()\n", - "nested.append(4)\n", - "print(d1)\n", - "print(d2)\n", - "print()\n", - "\n", - "# поэтому опять используем deepcopy\n", - "from copy import deepcopy\n", - "nested = [1, 2, 3]\n", - "d1 = {\"a\": nested}\n", - "d2 = deepcopy(d1)\n", - "nested.append(4)\n", - "print(d1)\n", - "print(d2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Задача - есть список элементов. Известно, что у большинства элементов в списке есть пары, то есть их по 2. Но есть некоторое количество элементов, у которых пары нет - их по 1 в списке. Цель - найти эти одиночные элементы.**\n", - "\n", - "Вот решение с помощью словаря:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "elements = [2, 1, 5, 2, 4, 3, 1, 4]\n", - "\n", - "solution_dict = {}\n", - "\n", - "#TODO by hand\n", - "\n", - "for item in elements:\n", - " if item in solution_dict:\n", - " solution_dict[item] = solution_dict.get(item, 0) + 1\n", - "for key, value in solution_dict.items():\n", - " if value is 1:\n", - " print(key)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "В python3.6 dict сохранет порядок добавления элементов" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example = dict()\n", - "for item in range(0, 10):\n", - " example[item] = item\n", - "print(example)\n", - "for key, value in example.items():\n", - " print(key, value)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Равенство и идентичность" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "first_list = [1,2,3]\n", - "from copy import deepcopy\n", - "second_list = deepcopy(first_list)\n", - "print(first_list == second_list)\n", - "print(first_list is second_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "apple_count = \"one\"\n", - "orange_count = \"one\"\n", - "print(apple_count == orange_count)\n", - "print(apple_count is orange_count)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "apple_count = \"more then one\"\n", - "orange_count = \"more then one\"\n", - "print(apple_count == orange_count)\n", - "print(apple_count is orange_count)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Введение в генераторы" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "first_list = [0, 1,2,3,4,5,6]\n", - "second_list = [item + 1 for item in first_list]\n", - "print(first_list is second_list)\n", - "print(second_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "new_dict = {item: item + 1 for item in first_list }\n", - "print(new_dict)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list(range(0, 10)))\n", - "even_list = [num for num in range(10) if num % 2 == 0]\n", - "print(even_list)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Множество: set" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Неупорядоченная коллекция, содержащая только уникальные хешируемые элементы.\n", - "\n", - "Изменяемые\n", - "Не хешируемые" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# создадим пустой set c помощью ф-ии set()\n", - "empty_set = set()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# непустое множество можно также задать фигурными скобками\n", - "example_set = {1, 2, 3}\n", - "print('id is ', id(example_set))\n", - "# добавляем элемент в set\n", - "example_set.add(4)\n", - "print(example_set)\n", - "print('id is ', id(example_set))\n", - "\n", - "# добавляем уже присутствующий элемент в set - ничего не меняется\n", - "example_set.add(1)\n", - "print(example_set)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Проверяем наличие значения в множестве O(1)\n", - "2 in example_set" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "another_set = {4, 5, 6}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(example_set.difference(another_set)) # различие\n", - "print(example_set.union(another_set)) # объединение\n", - "print(example_set.intersection(another_set)) # пересечение" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# например, вызов какой-либо ф-ии чат сервиса вернул нам список активных в данный момент пользователей в комнате\n", - "active_users = [\"Александр\", \"Михаил\", \"Ольга\"]\n", - "# вызов другой ф-ии вернул список администраторов чата\n", - "admins = [\"Ольга\", \"Мария\"]\n", - "# нам нужно найти список активных администраторов и послать им какое-либо уведомление\n", - "active_admins = set(active_users).intersection(set(admins))\n", - "active_admins" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# сеты могут содержать только hashable объекты.\n", - "example_set = set()\n", - "example_set.add([1, 2, 3])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Основная задача, очень часто возникающая на практике. Есть список элементов, нужно оставить только уникальные. Это одна строка с использованием сета:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "elements = [\"yellow\", \"yellow\", \"green\", \"red\", \"blue\", \"blue\", \"magenta\", \"orange\", \"red\"]\n", - "set(elements)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## frozenset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Так как set может содержать только hashable объекты, а иногда нужен сет сетов - существует frozenset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "example_set = set()\n", - "example_set.add(frozenset([1,2,3]))\n", - "print(example_set)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## collections" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import collections\n", - "print([x for x in collections.__dict__.keys() if not x.startswith(\"_\")])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# counter\n", - "counter = collections.Counter([1,2,3,4,1,2,1,1,1])\n", - "for elem, count in counter.most_common(3):\n", - " print(\"{}: {} times\".format(elem, count))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# defaultdict\n", - "default_dict = collections.defaultdict(int)\n", - "default_dict[\"not_exists\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# namedtuple\n", - "import math\n", - "Point = collections.namedtuple('Point', ['x', 'y'])\n", - "point1 = Point(0, 0)\n", - "point2 = Point(3, 4)\n", - "distance = math.sqrt((point2.x - point1.x)**2 + (point2.y - point1.y)**2)\n", - "distance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## queue" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Queue: Create a queue object with a given maximum size.\n", - "\n", - " If maxsize is <= 0, the queue size is infinite.\n", - " \n", - "\n", - "PriorityQueue: Variant of Queue that retrieves open entries in priority order (lowest first).\n", - "\n", - " Entries are typically tuples of the form: (priority number, data).\n", - " \n", - "\n", - "LifoQueue: Variant of Queue that retrieves most recently added entries first.\n" - ] - } - ], - "source": [ - "import queue\n", - "\n", - "print(\"Queue:\", queue.Queue.__doc__)\n", - "print()\n", - "print(\"PriorityQueue:\", queue.PriorityQueue.__doc__)\n", - "print()\n", - "print(\"LifoQueue:\", queue.LifoQueue.__doc__)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* Queue\n", - "* PriorityQueue\n", - "* LifoQueue\n", - "\n", - "Разберем позже, когда будем говорить о многопоточности, так как доступ к этим структурам синхронизирован." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Другие структуры данных" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* Связанные списки\n", - "* Деревья\n", - "* Графы\n", - "* ...\n", - "\n", - "Если вам нужна реализация какой-то конкретно структуры данных - скорее всего вы найдете готовую open-source реализацию." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Функции" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from datetime import datetime\n", - "\n", - "\n", - "def get_seconds():\n", - " \"\"\"Return current seconds\"\"\"\n", - " return datetime.now().second\n", - "\n", - "\n", - "get_seconds()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "help(get_seconds)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "get_seconds.__doc__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "get_seconds.__name__" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def split_tags(tag_string):\n", - " tag_list = []\n", - " for tag in tag_string.split(','):\n", - " tag_list.append(tag.strip())\n", - " \n", - " return tag_list\n", - "\n", - "\n", - "split_tags('functions, classes, OOP, inheritance, methods')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "split_tags()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def add(x: int, y: int) -> int:\n", - " return x + y\n", - "\n", - "\n", - "print(add(10, 11))\n", - "print(add('still ', 'works'))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Напишите функцию, которая находит медиану переданного списка" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def nlogn_median(l):\n", - " l = sorted(l)\n", - " if len(l) % 2 == 1:\n", - " return l[len(l) / 2]\n", - " else:\n", - " return (l[len(l) // 2 - 1] + l[len(l) // 2])/2\n", - " \n", - "nlogn_median([4,2,3,1])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### По ссылке или по значению?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def extender(source_list, extend_list):\n", - " source_list.extend(extend_list)\n", - " \n", - "\n", - "values = [1, 2, 3]\n", - "extender(values, [4, 5, 6])\n", - "\n", - "print(values)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def replacer(source_tuple, replace_with):\n", - " source_tuple = replace_with\n", - " \n", - "\n", - "user_info = ('Guido', '31/01')\n", - "replacer(user_info, ('Larry', '27/09'))\n", - "\n", - "print(user_info)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# mutable vs immutable" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Именованные аргументы" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def say(greeting, name):\n", - " print('{} {}!'.format(greeting, name))\n", - " \n", - "\n", - "say('Hello', 'Kitty')\n", - "say(name='Kitty', greeting='Hello')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "say(name='Kitty', 'Hello')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Область видимости" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result = 0\n", - "\n", - "def increment():\n", - " return result\n", - "\n", - "result = increment()\n", - "print(result)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result = 0\n", - "\n", - "def increment():\n", - " result = 1\n", - " return result\n", - "\n", - "result = increment()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result = 0\n", - "\n", - "def increment():\n", - " print(result)\n", - " result = 1\n", - " return result\n", - "\n", - "result = increment()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# global & nonlocal" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Аргументы по умолчанию" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def greeting(name='it\\'s me...'):\n", - " print('Hello, {}'.format(name))\n", - " \n", - "greeting() \n", - "greeting('Kitty')\n", - "\n", - "print(greeting.__defaults__)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def append_one(iterable=[]):\n", - " print('iterable', iterable)\n", - " iterable.append(1)\n", - " \n", - " return iterable\n", - "\n", - "print('defaults', append_one.__defaults__)\n", - "print(append_one([1]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(append_one())\n", - "print(append_one())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(append_one.__defaults__)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def function(iterable=None):\n", - " if iterable is None:\n", - " iterable = []\n", - " return iterable\n", - "\n", - "def function(iterable=None):\n", - " iterable = iterable or []\n", - " return iterable\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Распаковка" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def printer(*args):\n", - " print(type(args))\n", - " \n", - " for argument in args:\n", - " print(argument)\n", - "\n", - "\n", - "printer(1, 2, 3, 4, 5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "name_list = ['John', 'Bill', 'Amy', 'Jane']\n", - "printer(*name_list)\n", - "a = [1,2,3]\n", - "printer(*a)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def printer(**kwargs):\n", - " print(type(kwargs))\n", - " \n", - " print(kwargs.keys())\n", - " \n", - " \n", - "printer(a=10, b=11)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "printer(\"asd\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "payload = {\n", - " 'user_id': 117,\n", - " 'feedback': {\n", - " 'subject': 'Registration fields',\n", - " 'message': 'There is no country for old men'\n", - " }\n", - "}\n", - "printer(**payload)\n", - "# printer(user_id=117, feedback={\n", - "# 'subject':. ...\n", - "# })" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def printer(*args, **kwargs):\n", - " print(type(args), args)\n", - " print(type(kwargs), kwargs)\n", - " \n", - "printer(1, 2, 3, a=1, b=2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## lambda" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "need_sorted = [(0, 1), (1, 0), (2, 3)]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def sort_key(item):\n", - " return item[1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(need_sorted)\n", - "need_sorted.sort(key=sort_key)\n", - "print(need_sorted)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = lambda x: x[1]\n", - "print(type(l))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(need_sorted)\n", - "need_sorted.sort(key=lambda x: x[0])\n", - "print(need_sorted)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Исключения" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def average(num_list):\n", - " return sum(num_list) / len(num_list)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "average([])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " average([])\n", - "except ZeroDivisionError:\n", - " print('Error occurred')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " average([])\n", - "except ZeroDivisionError as e:\n", - " print(e)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " average([])\n", - "except Exception:\n", - " print('Avoid this')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " average([])\n", - "except (ValueError, TypeError, ZeroDivisionError):\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " average([])\n", - "except ValueError:\n", - " print('Value!')\n", - "except ZeroDivisionError:\n", - " print('Zero!')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class ParkException(ZeroDivisionError):\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "raise ParkException('Wrong value')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " 1 / 0\n", - "except ZeroDivisionError as e:\n", - " raise ValueError from e\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " 1 / 0\n", - "except ZeroDivisionError as e:\n", - " raise\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Генераторы часть 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = (item for item in range(3))\n", - "for item in a:\n", - " print(item)\n", - "\n", - "print(a)\n", - "\n", - "for item in a:\n", - " print(item)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def simple_gen():\n", - " yield 1\n", - " yield 2\n", - "\n", - "gen = simple_gen()\n", - "print(next(gen))\n", - "print(next(gen))\n", - "print(next(gen))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "gen = simple_gen()\n", - "for i in gen:\n", - " print(i)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/talks/02_data_structures/notebook.ipynb b/talks/02_data_structures/notebook.ipynb new file mode 100644 index 0000000..e8f4db7 --- /dev/null +++ b/talks/02_data_structures/notebook.ipynb @@ -0,0 +1,2927 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Структуры данных" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Мы рассмотрим для начала структуры данных, доступные \"из коробки\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# built-in структуры данных\n", + "list, tuple, dict, set, frozenset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Список: list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Позволяет хранить последовательность элементов одного или разных типов с Сохранением порядка добавления элементов в список.\n", + "\n", + "Изменяемые" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Создание списков" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# создаем list используя квадратные скобки или ф-ю list\n", + "empty_list = []\n", + "empty_list = list()\n", + "not_empty_list = list([1,2,3])\n", + "not_empty_list = [1, 2, 3]\n", + "item = [None] * 100" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "True\n", + "a\n" + ] + } + ], + "source": [ + "# list может содержать любые объекты, внутри Python они хранятся как массив указателей\n", + "example_list = [1, True, \"a\"]\n", + "for element in example_list:\n", + " print(element)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# добавляем элемент в конец списка O(1)\n", + "example_list.append(\"foo\")\n", + "print(example_list)\n", + "\n", + "# добавляем элемент в начало O(n)\n", + "example_list.insert(0, \"bar\")\n", + "print(example_list)\n", + "\n", + "example_list.extend([\"foo\", 1])\n", + "print(example_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, True, 'a', [...]]\n", + "[1, True, 'a', [...]]\n" + ] + } + ], + "source": [ + "example_list.append(example_list)\n", + "print(example_list)\n", + "print(example_list[-1])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "116 ns ± 21.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "benchmark_list = []\n", + "%timeit -n10000 benchmark_list.append(\"foo\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 12.49 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "21.2 µs ± 12.6 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "benchmark_list = []\n", + "%timeit -n10000 benchmark_list.insert(0, \"bar\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "6\n" + ] + } + ], + "source": [ + "example_list = [0, 1, 2, 3, 4, 5, 6]\n", + "\n", + "# доступ к элементу O(1)\n", + "print(example_list[0])\n", + "print(example_list[-1])\n", + "\n", + "# Изменение по индексу O(1)\n", + "example_list[6] = 10" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 10]\n", + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# Удаление элемента O(n)\n", + "print(example_list)\n", + "del example_list[-1]\n", + "print(example_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 5]\n" + ] + } + ], + "source": [ + "example_list.remove(4)\n", + "print(example_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "list.remove(x): x not in list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mexample_list\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: list.remove(x): x not in list" + ] + } + ], + "source": [ + "example_list.remove(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list assignment index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mdel\u001b[0m \u001b[0mexample_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: list assignment index out of range" + ] + } + ], + "source": [ + "del example_list[5]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Обращение к несуществующему индексу.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mexample_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "# Обращение к несуществующему индексу.\n", + "example_list[100]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 5]\n", + "[2, 3]\n", + "[2, 3, 5]\n", + "[2, 3, 5]\n", + "[2, 3]\n", + "[0, 2, 5]\n", + "[0, 2, 5]\n", + "5\n", + "[5, 3, 2]\n", + "[5, 3, 2, 1, 0]\n" + ] + } + ], + "source": [ + "# срезы\n", + "print(example_list)\n", + "print(example_list[2:-1])\n", + "print(example_list[2:len(example_list)])\n", + "print(example_list[2:])\n", + "print(example_list[2:4])\n", + "print(example_list[0:len(example_list):2])\n", + "print(example_list[::2])\n", + "print(example_list[-1])\n", + "print(example_list[-1:1:-1])\n", + "print(example_list[::-1])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[100, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# при простом присваивании новая переменная ссылается на тот же список\n", + "example_list = [1, 2, 3, 4, 5]\n", + "another_list = example_list\n", + "another_list[0] = 100\n", + "print(example_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# копирование списка\n", + "example_list = [0, 1, 2, 3, 4, 5]\n", + "another_list = example_list[:]\n", + "# import copy\n", + "# another_list = copy.copy(example_list)\n", + "# another_list = list(example_list)\n", + "another_list[0] = 100\n", + "print(example_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1, 2, 3, 4], 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# но вложенные объекты не копируются (shallow copy)\n", + "nested_list = [1, 2, 3]\n", + "example_list = [nested_list, 1, 2, 3, 4, 5]\n", + "another_list = example_list[:]\n", + "nested_list.append(4)\n", + "print(another_list)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# используем ф-ю deepcopy из пакета copy\n", + "from copy import deepcopy\n", + "nested_list = [1, 2, 3]\n", + "example_list = [nested_list, 1, 2, 3, 4, 5]\n", + "another_list = deepcopy(example_list)\n", + "nested_list.append(4)\n", + "print(another_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Немного о сортировке" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "# Сортировка списка\n", + "unsorted_list = [2, 1, 5, 4, 3]\n", + "unsorted_list.sort()\n", + "print(unsorted_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5]\n", + "[2, 1, 5, 4, 3]\n" + ] + } + ], + "source": [ + "# сортировка in-place\n", + "unsorted_list = [2, 1, 5, 4, 3]\n", + "print(sorted(unsorted_list))\n", + "print(unsorted_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Кортеж: tuple" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Так же как и список позволяет хранить последовательность элементов одного или разного типа\n", + "\n", + "* Неизменяемые (Immutable) => защищают данные\n", + "* Быстрее списка" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# создадим пустой tuple используя круглые скобки или ф-ю tuple\n", + "empty_tuple = ()\n", + "empty_tuple = tuple()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(empty_tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "a\n", + "True\n" + ] + } + ], + "source": [ + "example_tuple = (1, \"a\", True)\n", + "for element in example_tuple:\n", + " print(element)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(example_tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "(2, 3)\n" + ] + } + ], + "source": [ + "example_tuple = (1, 2, 3)\n", + "print(example_tuple[0])\n", + "print(example_tuple[1:])" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mexample_tuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m2017\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "example_tuple[0] = 2017" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mexample_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m200\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "example_list[200]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "a = (123)\n", + "a\n", + "print(type(a))\n", + "a = (123, ) # в кортеже с одним элементом запятая обязательна!\n", + "print(type(a))\n", + "a = 1,2,3\n", + "print (type(a))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Важно понимать, что **неизменяемым является сам tuple, но не объекты, которые он содержит!**. Например:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "([1, 2], [3, 4])\n", + "([1, 2, 5], [3, 4])\n" + ] + } + ], + "source": [ + "first_list = [1, 2]\n", + "second_list = [3, 4]\n", + "example_tuple = (first_list, second_list)\n", + "print(example_tuple)\n", + "first_list.append(5)\n", + "print(example_tuple)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Написать в одну строку все цифры от 0 до 100\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899\n" + ] + } + ], + "source": [ + "print(\"\".join([ str(item) for item in list(range(101)) ]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "long_arr = [1] * 101\n", + "result = \"\"\n", + "current_item = 0\n", + "for index, item in enumerate(long_arr):\n", + " result += str(current_item)\n", + " current_item += item\n", + "\n", + "print(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"\".join([str(item) for item in range(0, 101)]))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2\n", + "1 2 3\n" + ] + } + ], + "source": [ + "a = (1,2)\n", + "b, c = a\n", + "print(b, c)\n", + "d, e, f = 1, 2, 3\n", + "print(d, e, f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Словарь: dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Позволяет хранить пары ключ-значение, для быстрого доступа к значению по ключу. " + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "# создаем словарь используя фигурные скобки или ф-ю dict\n", + "empty_dict = dict()\n", + "empty_dict = {}" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 4, 'b': True}\n", + "{'a': 4, 'b': True}\n" + ] + } + ], + "source": [ + "dict_constructor = dict(a=4, b=True)\n", + "print(dict_constructor)\n", + "a = \"c\"\n", + "b = \"d\"\n", + "new_dict = dict(a=4, b=True)\n", + "print(new_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'b': True, (1, 2, 3): True, 'c': 4}\n", + "4\n" + ] + } + ], + "source": [ + "example_dict = {\n", + " \"a\": 1,\n", + " \"b\": True,\n", + " (1, 2, 3): True\n", + "}\n", + "\n", + "# Добавляем ключ и соответствующее ему значение в словарь O(1)\n", + "example_dict[\"c\"] = 4\n", + "print(example_dict)\n", + "\n", + "# Поиск значения по ключу O(1)\n", + "print(example_dict[\"c\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'x'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Доступ к несуществующему ключу:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexample_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"x\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 'x'" + ] + } + ], + "source": [ + "# Доступ к несуществующему ключу:\n", + "print(example_dict[\"x\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1, 2, 3, 4], 1, 2, 3, 4, 5]\n" + ] + }, + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# ключом словаря может быть только hashable объект\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexample_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mexample_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mexample_list\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "# ключом словаря может быть только hashable объект\n", + "print(example_list)\n", + "example_dict[example_list] = True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Объект является hashable объектом, если он имеет hash-значение, неизменное в процессе его жизни (т.е. у объекта должен быть определен **______hash______()** метод) и объект можно сравнить с другими объектами (**________eq________()** или **________cmp________()** методы). Одинаковые объекты должны иметь одинаковое знаение hash.\n", + "\n", + "Если объект hashable, то он может быть использован как ключ в словаре или член множества (так как эти структуры используют значение hash внутри своей имплементации).\n", + "\n", + "Все immutable встроенные объекты в Python - hashable, mutable контейнеры - нет (списки, словари). Инстансы классов - hashable (их hash значение - их id, то есть адрес в памяти)." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mhash\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "hash([1,2,3])" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2528502973977326415" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash((1,2,3))" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n", + "default value\n" + ] + } + ], + "source": [ + "# если мы не уверены, что ключ есть в словаре - можно воспользоваться методом get\n", + "print(example_dict.get(\"x\"))\n", + "print(example_dict.get(\"x\", \"default value\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a: 1\n", + "b: True\n", + "(1, 2, 3): True\n", + "c: 4\n" + ] + } + ], + "source": [ + "# итерируемся по ключам и значениям - внимание, порядок недетерминирован (до Python 3.6)!\n", + "for key, value in example_dict.items():\n", + " print(\"{}: {}\".format(key, value))" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "# посмотрим на разницу поиска значения в словаре и списке\n", + "search_list = list(range(100000))\n", + "search_dict = dict.fromkeys(list(range(100000)))" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "57 ns ± 10.9 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "%timeit -n10000 0 in search_list" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "79.5 ns ± 16.7 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "%timeit -n10000 0 in search_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.56 ms ± 12.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "%timeit -n1000 99999 in search_list" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "119 ns ± 33.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "%timeit -n10000 99999 in search_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'c': 3, 'b': 2}\n" + ] + } + ], + "source": [ + "# объединение словарей\n", + "d1 = {\"a\": 1, \"c\": 3}\n", + "d2 = {\"b\": 2}\n", + "d1.update(d2)\n", + "print(d1)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'a': 1, 'c': 3}\n", + "{'a': 100, 'c': 3}\n", + "\n", + "{'a': [1, 2, 3, 4]}\n", + "{'a': [1, 2, 3, 4]}\n", + "\n", + "{'a': [1, 2, 3, 4]}\n", + "{'a': [1, 2, 3]}\n" + ] + } + ], + "source": [ + "# копирование\n", + "d1 = {\"a\": 1, \"c\": 3}\n", + "d2 = d1.copy()\n", + "d2[\"a\"] = 100\n", + "print(d1)\n", + "print(d2)\n", + "print()\n", + "\n", + "# вложенные не копируются\n", + "nested = [1, 2, 3]\n", + "d1 = {\"a\": nested}\n", + "d2 = d1.copy()\n", + "nested.append(4)\n", + "print(d1)\n", + "print(d2)\n", + "print()\n", + "\n", + "# поэтому опять используем deepcopy\n", + "from copy import deepcopy\n", + "nested = [1, 2, 3]\n", + "d1 = {\"a\": nested}\n", + "d2 = deepcopy(d1)\n", + "nested.append(4)\n", + "print(d1)\n", + "print(d2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Задача - есть список элементов. Известно, что у большинства элементов в списке есть пары, то есть их по 2. Но есть некоторое количество элементов, у которых пары нет - их по 1 в списке. Цель - найти эти одиночные элементы.**\n", + "\n", + "Вот решение с помощью словаря:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "3\n" + ] + } + ], + "source": [ + "elements = [2, 1, 5, 2, 4, 3, 1, 4]\n", + "\n", + "solution_dict = {}\n", + "\n", + "for item in elements:\n", + " if item in solution_dict:\n", + " solution_dict[item] = solution_dict.get(item, 0) + 1\n", + " if not solution_dict.get(item, None):\n", + " solution_dict[item] = 1\n", + "for key, value in solution_dict.items():\n", + " if value is 1:\n", + " print(key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "В python3.6 dict сохранет порядок добавления элементов" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "example = dict()\n", + "for item in range(0, 10):\n", + " example[item - 1] = item\n", + "print(example)\n", + "for key, value in example.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Равенство и идентичность" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"a\" in [\"a\", \"b\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "first_list = [1,2,3]\n", + "from copy import deepcopy\n", + "second_list = deepcopy(first_list)\n", + "print(first_list == second_list)\n", + "print(first_list is second_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n" + ] + } + ], + "source": [ + "apple_count = \"one\"\n", + "orange_count = \"one\"\n", + "print(apple_count == orange_count)\n", + "print(apple_count is orange_count)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "apple_count = \"more then one\"\n", + "orange_count = \"more then one\"\n", + "print(apple_count == orange_count)\n", + "print(apple_count is orange_count)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Введение в генераторы" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "[1, 2, 3, 4, 5, 6, 7]\n" + ] + } + ], + "source": [ + "first_list = [0, 1,2,3,4,5,6]\n", + "second_list = [ item + 1 for item in first_list ]\n", + "print(first_list is second_list)\n", + "print(second_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7}\n" + ] + } + ], + "source": [ + "new_dict = {item: item + 1 for item in first_list }\n", + "print(new_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", + "[0, 2, 4, 6, 8]\n" + ] + } + ], + "source": [ + "print(list(range(0, 10)))\n", + "even_list = [num for num in range(10) if num % 2 == 0]\n", + "print(even_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Множество: set" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Неупорядоченная коллекция, содержащая только уникальные хешируемые элементы.\n", + "\n", + "Изменяемые\n", + "Не хешируемые" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "# создадим пустой set c помощью ф-ии set()\n", + "empty_set = set()" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "id is 4401253960\n", + "{1, 2, 3, 4}\n", + "id is 4401253960\n", + "{1, 2, 3, 4}\n" + ] + } + ], + "source": [ + "# непустое множество можно также задать фигурными скобками\n", + "example_set = {1, 2, 3, 4, 4}\n", + "print('id is ', id(example_set))\n", + "# добавляем элемент в set\n", + "example_set.add(4)\n", + "print(example_set)\n", + "print('id is ', id(example_set))\n", + "\n", + "# добавляем уже присутствующий элемент в set - ничего не меняется\n", + "example_set.add(1)\n", + "print(example_set)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Проверяем наличие значения в множестве O(1)\n", + "2 in example_set" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [], + "source": [ + "another_set = {4, 5, 6}" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1, 2, 3}\n", + "{1, 2, 3, 4, 5, 6}\n", + "{4}\n" + ] + } + ], + "source": [ + "print(example_set.difference(another_set)) # различие\n", + "print(example_set.union(another_set)) # объединение\n", + "print(example_set.intersection(another_set)) # пересечение" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Ольга'}" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# например, вызов какой-либо ф-ии чат сервиса вернул нам список активных в данный момент пользователей в комнате\n", + "active_users = [\"Александр\", \"Михаил\", \"Ольга\"]\n", + "# вызов другой ф-ии вернул список администраторов чата\n", + "admins = [\"Ольга\", \"Мария\"]\n", + "# нам нужно найти список активных администраторов и послать им какое-либо уведомление\n", + "active_admins = set(active_users).intersection(set(admins))\n", + "active_admins" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [], + "source": [ + "# сеты могут содержать только hashable объекты.\n", + "example_set = set()\n", + "example_set.add([1, 2, 3])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Основная задача, очень часто возникающая на практике. Есть список элементов, нужно оставить только уникальные. Это одна строка с использованием сета:**" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'blue', 'green', 'magenta', 'orange', 'red', 'yellow'}" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "elements = [\"yellow\", \"yellow\", \"green\", \"red\", \"blue\", \"blue\", \"magenta\", \"orange\", \"red\"]\n", + "set(elements)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## frozenset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Так как set может содержать только hashable объекты, а иногда нужен сет сетов - существует frozenset" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{frozenset({1, 2, 3})}\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'frozenset' object has no attribute 'add'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexample_set\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfrozenset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"4\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'frozenset' object has no attribute 'add'" + ] + } + ], + "source": [ + "example_set = set()\n", + "example_set.add(frozenset([1,2,3]))\n", + "print(example_set)\n", + "a = frozenset([1,2,3,3])\n", + "print(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## collections" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Awaitable', 'Coroutine', 'AsyncIterable', 'AsyncIterator', 'AsyncGenerator', 'Hashable', 'Iterable', 'Iterator', 'Generator', 'Reversible', 'Sized', 'Container', 'Callable', 'Collection', 'Set', 'MutableSet', 'Mapping', 'MutableMapping', 'MappingView', 'KeysView', 'ItemsView', 'ValuesView', 'Sequence', 'MutableSequence', 'ByteString', 'deque', 'defaultdict', 'OrderedDict', 'namedtuple', 'Counter', 'ChainMap', 'UserDict', 'UserList', 'UserString', 'abc']\n" + ] + } + ], + "source": [ + "import collections\n", + "print([x for x in collections.__dict__.keys() if not x.startswith(\"_\")])" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1: 5 times\n", + "2: 2 times\n", + "3: 1 times\n" + ] + } + ], + "source": [ + "# counter\n", + "counter = collections.Counter([1,2,3,4,1,2,1,1,1])\n", + "for elem, count in counter.most_common(3):\n", + " print(\"{}: {} times\".format(elem, count))" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "1", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 1" + ] + } + ], + "source": [ + "a = dict()\n", + "a[1] += 1" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "defaultdict(, {1: [1]})\n", + "[]\n", + "dict_keys([1, 2])\n" + ] + } + ], + "source": [ + "# defaultdict\n", + "default_dict = collections.defaultdict(list)\n", + "default_dict[1].append(1)\n", + "print(default_dict)\n", + "default_dict[2]\n", + "print(default_dict[2])\n", + "print(default_dict.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 0\n" + ] + }, + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# namedtuple\n", + "import math\n", + "Point = collections.namedtuple('Point', ['x', 'y'])\n", + "point1 = Point(0, 0)\n", + "point2 = Point(3, 4)\n", + "print(point1.x, point1.y)\n", + "distance = math.sqrt((point2.x - point1.x)**2 + (point2.y - point1.y)**2)\n", + "distance" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## queue" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import queue\n", + "\n", + "print(\"Queue:\", queue.Queue.__doc__)\n", + "print()\n", + "print(\"PriorityQueue:\", queue.PriorityQueue.__doc__)\n", + "print()\n", + "print(\"LifoQueue:\", queue.LifoQueue.__doc__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Queue\n", + "* PriorityQueue\n", + "* LifoQueue\n", + "\n", + "Разберем позже, когда будем говорить о многопоточности, так как доступ к этим структурам синхронизирован." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Другие структуры данных" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Связанные списки\n", + "* Деревья\n", + "* Графы\n", + "* ...\n", + "\n", + "Если вам нужна реализация какой-то конкретно структуры данных - скорее всего вы найдете готовую open-source реализацию." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Функции" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "15" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import datetime\n", + "\n", + "\n", + "def get_seconds():\n", + " \"\"\"Return current seconds\"\"\"\n", + " return datetime.now().second\n", + "\n", + "\n", + "get_seconds()" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function get_seconds in module __main__:\n", + "\n", + "get_seconds()\n", + " Return current seconds\n", + "\n" + ] + } + ], + "source": [ + "help(get_seconds)" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Return current seconds'" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_seconds.__doc__" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'get_seconds'" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_seconds.__name__" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['functions', ' classes', ' OOP', ' inheritance', ' methods']\n" + ] + }, + { + "data": { + "text/plain": [ + "['functions', 'classes', 'OOP', 'inheritance', 'methods']" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def split_tags(tag_string):\n", + " tag_list = []\n", + " print(tag_string.split(','))\n", + " for tag in tag_string.split(','):\n", + " tag_list.append(tag.strip())\n", + " \n", + " return tag_list\n", + "\n", + "\n", + "split_tags('functions, classes, OOP, inheritance, methods')" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "split_tags() missing 1 required positional argument: 'tag_string'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msplit_tags\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: split_tags() missing 1 required positional argument: 'tag_string'" + ] + } + ], + "source": [ + "split_tags()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21\n", + "still works\n" + ] + } + ], + "source": [ + "def add(x: int, y: int) -> int:\n", + " return x + y\n", + "\n", + "\n", + "print(add(10, 11))\n", + "print(add('still ', 'works'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Напишите функцию, которая находит медиану переданного списка" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mnot_sorted_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmediana\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmediana\u001b[0;34m(not_sorted_list)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m//\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m//\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnot_sorted_list\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m//\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 8\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mnot_sorted_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "def mediana(not_sorted_list):\n", + " sorted_list = not_sorted_list[:]\n", + " sorted_list.sort()\n", + " if(len(not_sorted_list) % 2 == 1):\n", + " return not_sorted_list[len(not_sorted_list) // 2]\n", + " else:\n", + " return (not_sorted_list[len(not_sorted_list) // 2] + not_sorted_list[len(not_sorted_list) // 2 - 1])/2\n", + "\n", + "not_sorted_list = []\n", + "print(int(mediana(not_sorted_list)))\n", + "print(not_sorted_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": {}, + "outputs": [], + "source": [ + "def nlogn_median(l):\n", + " if not len(l):\n", + " return None\n", + " l = sorted(l)\n", + " if len(l) % 2 == 1:\n", + " return l[len(l) / 2]\n", + " else:\n", + " return (l[len(l) // 2 - 1] + l[len(l) // 2])/2\n", + " \n", + "nlogn_median([])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### По ссылке или по значению?" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5, 6]\n" + ] + } + ], + "source": [ + "def extender(source_list, extend_list):\n", + " source_list.extend(extend_list)\n", + " \n", + "\n", + "values = [1, 2, 3]\n", + "extender(values, [4, 5, 6])\n", + "\n", + "print(values)" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('Guido', '31/01')\n" + ] + } + ], + "source": [ + "def replacer(source_tuple, replace_with):\n", + " source_tuple = replace_with\n", + " \n", + "\n", + "user_info = ('Guido', '31/01')\n", + "replacer(user_info, ('Larry', '27/09'))\n", + "\n", + "print(user_info)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# mutable vs immutable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Именованные аргументы" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello Kitty!\n", + "Hello Kitty!\n" + ] + } + ], + "source": [ + "def say(greeting, name):\n", + " print('{} {}!'.format(greeting, name))\n", + " \n", + "\n", + "say('Hello', 'Kitty')\n", + "say(name='Kitty', greeting='Hello')" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "positional argument follows keyword argument (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m say(name='Kitty', 'Hello')\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" + ] + } + ], + "source": [ + "say(name='Kitty', 'Hello')" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello Kitty!\n" + ] + } + ], + "source": [ + "say('Hello', name='Kitty')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Область видимости" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "result = 0\n", + "\n", + "def increment():\n", + " return result\n", + "\n", + "result = increment()\n", + "print(result)" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n" + ] + } + ], + "source": [ + "result = 0\n", + "\n", + "def increment():\n", + " result = 1\n", + " return result\n", + "\n", + "new_result = increment()\n", + "print(result)\n", + "print(new_result)" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [ + { + "ename": "UnboundLocalError", + "evalue": "local variable 'result' referenced before assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnboundLocalError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mincrement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mincrement\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mincrement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'result' referenced before assignment" + ] + } + ], + "source": [ + "result = 0\n", + "\n", + "def increment():\n", + " print(result)\n", + " result = 1\n", + " return result\n", + "\n", + "result = increment()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# global & nonlocal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Аргументы по умолчанию" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, it's me...\n", + "Hello, Kitty\n", + "(\"it's me...\",)\n" + ] + } + ], + "source": [ + "def greeting(name='it\\'s me...'):\n", + " print('Hello, {}'.format(name))\n", + " \n", + "greeting() \n", + "greeting('Kitty')\n", + "\n", + "print(greeting.__defaults__)" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "defaults ([],)\n", + "iterable [1]\n", + "[1, 1]\n" + ] + } + ], + "source": [ + "def append_one(iterable=[]):\n", + " print('iterable', iterable)\n", + " iterable.append(1)\n", + " \n", + " return iterable\n", + "\n", + "print('defaults', append_one.__defaults__)\n", + "print(append_one([1]))" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "iterable []\n", + "[1]\n", + "iterable [1]\n", + "[1, 1]\n" + ] + } + ], + "source": [ + "print(append_one())\n", + "print(append_one())" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "([1, 1],)\n" + ] + } + ], + "source": [ + "print(append_one.__defaults__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def function(iterable=None):\n", + " if iterable is None:\n", + " iterable = []\n", + " return iterable\n", + "\n", + "def function(iterable=None):\n", + " iterable = iterable or []\n", + " return iterable\n", + "\n", + "function()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Распаковка" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "4\n", + "5\n" + ] + } + ], + "source": [ + "def printer(a, b, c, *args):\n", + " print(type(args))\n", + " \n", + " for argument in args:\n", + " print(argument)\n", + "\n", + "printer(1, 2, 3, 4, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Jane\n", + "\n" + ] + } + ], + "source": [ + "name_list = ['John', 'Bill', 'Amy', 'Jane']\n", + "printer(*name_list)\n", + "a = [1,2,3]\n", + "printer(*a)" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "10\n", + "11\n", + "dict_keys(['et1', 'b'])\n" + ] + } + ], + "source": [ + "def printer(**kwargs):\n", + " print(type(kwargs))\n", + " print(kwargs['et1'])\n", + " print(kwargs['b'])\n", + " print(kwargs.keys())\n", + " \n", + "\n", + "printer(et1=10, b=11)" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "printer() takes 0 positional arguments but 1 was given", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprinter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"asd\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: printer() takes 0 positional arguments but 1 was given" + ] + } + ], + "source": [ + "printer(\"asd\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "payload = {\n", + " 'user_id': 117,\n", + " 'feedback': {\n", + " 'subject': 'Registration fields',\n", + " 'message': 'There is no country for old men'\n", + " }\n", + "}\n", + "printer(**payload)\n", + "# printer(user_id=117, feedback={\n", + "# 'subject':. ...\n", + "# })" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": {}, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "positional argument follows keyword argument (, line 6)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m6\u001b[0m\n\u001b[0;31m printer(a=1, b=2, 1, 2, 3)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" + ] + } + ], + "source": [ + "def printer(*args, **kwargs):\n", + " print(type(args), args)\n", + " print(type(kwargs), kwargs)\n", + " \n", + "printer(1, 2, 3, a=1, b=2)\n", + "printer(a=1, b=2, 1, 2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## lambda" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": {}, + "outputs": [], + "source": [ + "need_sorted = [('Jhon', 1), (\"Mari\", 0), (\"Egor\", 3)]" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": {}, + "outputs": [], + "source": [ + "def sort_key(item):\n", + " return -item[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('Jhon', 1), ('Mari', 0), ('Egor', 3)]\n", + "[('Egor', 3), ('Jhon', 1), ('Mari', 0)]\n" + ] + } + ], + "source": [ + "print(need_sorted)\n", + "need_sorted.sort(key=sort_key)\n", + "print(need_sorted)" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "l = lambda x: x[1]\n", + "print(type(l))" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('Egor', 3), ('Jhon', 1), ('Mari', 0)]\n", + "[('Mari', 0), ('Jhon', 1), ('Egor', 3)]\n" + ] + } + ], + "source": [ + "print(need_sorted)\n", + "need_sorted.sort(key=lambda x: x[1])\n", + "print(need_sorted)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Исключения" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": {}, + "outputs": [], + "source": [ + "def average(num_list):\n", + " return sum(num_list) / len(num_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": {}, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36maverage\u001b[0;34m(num_list)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0maverage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + } + ], + "source": [ + "average([])" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error occurred\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except ZeroDivisionError:\n", + " print('Error occurred')" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "division by zero\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except ZeroDivisionError as e:\n", + " print(e)" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Avoid this\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except Exception:\n", + " print('Avoid this')" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "division by zero\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except (ValueError, TypeError, ZeroDivisionError) as e:\n", + " print(e)" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Zero!\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except ValueError:\n", + " print('Value!')\n", + "except ZeroDivisionError:\n", + " print('Zero!')" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": {}, + "outputs": [], + "source": [ + "class ParkException(ZeroDivisionError):\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": {}, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0maverage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mParkException\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Zero!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36maverage\u001b[0;34m(num_list)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0maverage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnum_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except ParkException:\n", + " print('Zero!')" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Zero!\n" + ] + } + ], + "source": [ + "try:\n", + " average([])\n", + "except Exception:\n", + " print('Zero!')" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": {}, + "outputs": [], + "source": [ + "class ParkException(ZeroDivisionError):\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": {}, + "outputs": [ + { + "ename": "ParkException", + "evalue": "Wrong value", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mParkException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mParkException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Wrong value'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mParkException\u001b[0m: Wrong value" + ] + } + ], + "source": [ + "raise ParkException('Wrong value')" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "ERROR\n" + ] + } + ], + "source": [ + "import random\n", + "def printer():\n", + " a = random.randint(0, 1)\n", + " print(a)\n", + " if a == 1:\n", + " raise ParkException('Wrong value')\n", + " return a\n", + "\n", + "try:\n", + " printer()\n", + "except ParkException:\n", + " print(\"ERROR\")" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mParkException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mParkException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Wrong value'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mZeroDivisionError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mParkException\u001b[0m: Wrong value", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mParkException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Wrong value'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mZeroDivisionError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: " + ] + } + ], + "source": [ + "try:\n", + " raise ParkException('Wrong value')\n", + "except ZeroDivisionError as e:\n", + " raise ValueError from e\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " 1 / 0\n", + "except ZeroDivisionError as e:\n", + " raise\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Генераторы часть 2" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + " at 0x107599888>\n" + ] + } + ], + "source": [ + "a = (item for item in range(3))\n", + "for item in a:\n", + " print(item)\n", + "\n", + "print(a)\n", + "\n", + "for item in a:\n", + " print(item)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n" + ] + }, + { + "ename": "StopIteration", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m: " + ] + } + ], + "source": [ + "def simple_gen():\n", + " yield 1\n", + " yield 2\n", + "\n", + "gen = simple_gen()\n", + "print(next(gen))\n", + "print(next(gen))\n", + "print(next(gen))" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n" + ] + } + ], + "source": [ + "gen = simple_gen()\n", + "for i in gen:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/talks/03_oop/main.ipynb b/talks/03_oop/main.ipynb index c52d857..10ef61e 100644 --- a/talks/03_oop/main.ipynb +++ b/talks/03_oop/main.ipynb @@ -22,7 +22,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "sys.path # PYTHONPATH" @@ -53,7 +55,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "predicates" @@ -62,7 +66,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "predicates.empty([1, 2])" @@ -71,7 +77,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "predicates._odd(4)" @@ -80,7 +88,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from predicates import positive\n", @@ -90,7 +100,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from predicates import *\n", @@ -100,7 +112,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from predicates import *\n", @@ -119,7 +133,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "import geometry" @@ -128,7 +144,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "geometry" @@ -137,7 +155,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from math import pi\n", @@ -159,7 +179,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "geometry.square.square_area(4)" @@ -168,7 +190,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "circle.circle_length(4)" @@ -177,7 +201,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from geometry import circle as circle2\n", @@ -218,7 +244,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval" @@ -227,7 +255,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "type(interval)" @@ -236,7 +266,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "type(TimeInterval)" @@ -245,7 +277,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval is type(interval)" @@ -254,7 +288,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "isinstance(interval, TimeInterval)" @@ -285,7 +321,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import datetime\n", @@ -298,7 +336,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.begin" @@ -315,7 +355,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.xxx = 42\n", @@ -325,7 +367,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.not_found" @@ -359,7 +403,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval(datetime(year=2016, month=1, day=1), datetime.now())\n", @@ -373,7 +419,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.unknown_method()" @@ -382,7 +430,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.get_length" @@ -416,7 +466,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import datetime\n", @@ -445,7 +497,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import datetime\n", @@ -490,7 +544,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval()\n", @@ -500,7 +556,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval.DEFAULT_BEGIN" @@ -509,7 +567,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval.get_length" @@ -518,7 +578,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval.get_length()" @@ -527,7 +589,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval.get_length(interval)" @@ -564,7 +628,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval()\n", @@ -613,7 +679,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval()\n", @@ -623,7 +691,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "TimeInterval._get_default_end() # static" @@ -633,7 +703,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### _D&D ability score_" + "### _Chat history_" ] }, { @@ -667,7 +737,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import datetime\n", @@ -704,14 +776,16 @@ " return self._end\n", " \n", " @end.setter\n", - " def end(self):\n", + " def end(self, value):\n", " self._end = value" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import datetime\n", @@ -736,7 +810,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval.begin" @@ -745,7 +821,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval._begin" @@ -794,7 +872,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval, str(interval)" @@ -837,7 +917,7 @@ " return self._end - self._begin\n", " \n", " def __repr__(self):\n", - " return 'TimeInterval({}, {})'.format(repr(self._begin), repr(self._end))\n", + " return 'TimeInterval({!r}, {!r})'.format(self._begin, self._end)\n", " \n", " def __str__(self):\n", " return '{} -> {}'.format(self._begin, self._end)" @@ -875,7 +955,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import timedelta\n", @@ -903,7 +985,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from datetime import timedelta, datetime\n", @@ -969,7 +1053,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "stream = InputOutputStream()\n", @@ -979,7 +1065,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "InputOutputStream.__mro__" @@ -1003,7 +1091,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from collections import namedtuple\n", @@ -1030,7 +1120,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval(datetime(2018, 1, 1), datetime(2018, 1, 2))\n", @@ -1059,7 +1151,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "type(stream) == OutputStream" @@ -1068,7 +1162,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "isinstance(stream, OutputStream)" @@ -1077,7 +1173,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "isinstance(stream, object)" @@ -1086,7 +1184,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "stream.__class__" @@ -1095,7 +1195,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "type(stream).__bases__" @@ -1104,7 +1206,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "amount.__dict__" @@ -1154,13 +1258,54 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "interval = TimeInterval()\n", "interval._begin, interval._end" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__slots__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class TimeInterval:\n", + " __slots__ = ['_begin', '_end']\n", + " \n", + " def __init__(self, begin, end):\n", + " self._begin = begin\n", + " self._end = end\n", + " \n", + " def get_length(self):\n", + " return self._end - self._begin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "interval = TimeInterval(datetime(2018, 1, 1), datetime(2018, 1, 2))\n", + "interval.xxx = 123" + ] + }, { "cell_type": "markdown", "metadata": { diff --git a/talks/03_oop/test_sample/tests/test_interval.py b/talks/03_oop/test_sample/tests/test_interval.py index ee577d5..f44eb76 100644 --- a/talks/03_oop/test_sample/tests/test_interval.py +++ b/talks/03_oop/test_sample/tests/test_interval.py @@ -17,6 +17,4 @@ def test_str(self): ) # test_init -# patch -# TimeInterval(datetime.datetime(2017, 1, 1, 0, 0), datetime.datetime(2018, 1, 1, 0, 0)) # setUp diff --git a/talks/04_io/VENUS.bmp b/talks/04_io/VENUS.bmp new file mode 100644 index 0000000..79a244c Binary files /dev/null and b/talks/04_io/VENUS.bmp differ diff --git a/talks/04_io/http_server.py b/talks/04_io/http_server.py new file mode 100644 index 0000000..c1ff759 --- /dev/null +++ b/talks/04_io/http_server.py @@ -0,0 +1,16 @@ +import tornado.ioloop +import tornado.web + +class MainHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello, world") + +def make_app(): + return tornado.web.Application([ + (r"/", MainHandler), + ]) + +if __name__ == "__main__": + app = make_app() + app.listen(8090) + tornado.ioloop.IOLoop.current().start() \ No newline at end of file diff --git a/talks/04_io/main.ipynb b/talks/04_io/main.ipynb new file mode 100644 index 0000000..ecdea5b --- /dev/null +++ b/talks/04_io/main.ipynb @@ -0,0 +1,1146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Менеджеры контекста" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Что это такое" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "with open('tmp.txt', 'w') as f:\n", + " f.write('Привет всем. Текущее время ' + str(datetime.datetime.now()))\n", + " \n", + "# Эквивалентные конструкции\n", + "f = open('tmp.txt', 'r')\n", + "print(f.read())\n", + "f.close()\n", + "print(f.closed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Но не совсем\n", + "f = open('tmp.txt', 'r')\n", + "print(f.read())\n", + "raise(ValueError)\n", + "f.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f.closed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Как реализовать свой" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class File(object):\n", + " def __init__(self, file_name, method):\n", + " self.file_obj = open(file_name, method)\n", + " def __enter__(self):\n", + " return self.file_obj\n", + " def __exit__(self, type, value, traceback):\n", + " # Здесь может быть сложная логика обработки исключения\n", + " self.file_obj.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with File('tmp.txt', 'r') as f:\n", + " print(f.read())\n", + " raise(ValueError)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f.closed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Это не только файлы и соединения" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Транзакция\n", + "\n", + "```python\n", + "from django.db import transaction\n", + "\n", + "def viewfunc(request):\n", + " # This code executes in autocommit mode (Django's default).\n", + " do_stuff()\n", + "\n", + " with transaction.atomic():\n", + " # This code executes inside a transaction.\n", + " do_more_stuff()\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Блокировка\n", + "\n", + "```python\n", + "from filelock import Timeout, FileLock\n", + "\n", + "lock = FileLock(\"high_ground.txt.lock\")\n", + "with lock:\n", + " open(\"high_ground.txt\", \"a\").write(\"You were the chosen one.\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## contextlib" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contextlib import contextmanager\n", + "\n", + "@contextmanager\n", + "def closing(thing):\n", + " try:\n", + " yield thing\n", + " finally:\n", + " thing.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with closing(open('tmp.txt', 'r')) as f:\n", + " print(f.read())\n", + "print(f.closed)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contextlib import closing\n", + "from urllib.request import urlopen\n", + "\n", + "with closing(urlopen('http://ip.jsontest.com/')) as page:\n", + " for line in page:\n", + " print(line)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from contextlib import suppress\n", + "\n", + "with suppress(FileNotFoundError):\n", + " os.remove('somefile.tmp')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Работа с файлами" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Базовые операции" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "with open('tmp.txt', 'w') as f:\n", + " f.write('Привет всем. Текущее время ' + str(datetime.datetime.now()))\n", + " \n", + "with open('tmp.txt', 'r') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Дописываем в конец\n", + "with open('tmp.txt', 'a') as f:\n", + " for _ in range(5):\n", + " f.write('\\nПривет всем. Текущее время ' + str(datetime.datetime.now()))\n", + " \n", + "with open('tmp.txt', 'r') as f:\n", + " for line in f: # читаем построчно\n", + " print(line.strip())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# читаем целиком\n", + "with open('tmp.txt', 'r') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('tmp.txt', 'r') as f:\n", + " f.seek(25) # Переходим на нужню позицию\n", + " print(f.read(30)) # Читаем нужное количество байт" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Кодировки\n", + "\n", + "* https://ru.wikipedia.org/wiki/ASCII\n", + "* https://ru.wikipedia.org/wiki/Windows-1251\n", + "* https://ru.wikipedia.org/wiki/UTF-8\n", + "* https://ru.wikipedia.org/wiki/UTF-16\n", + "\n", + "https://ru.wikipedia.org/wiki/Юникод - это не кодировка!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Пишем в конкретной кодировке\n", + "with open('tmp.txt', 'w', encoding='cp1251') as f:\n", + " f.write('Привет всем. Текущее время ' + str(datetime.datetime.now()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open('tmp.txt', 'r') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Ошиблись в кодировке при чтении\n", + "with open('tmp.txt', 'r', encoding='cp1252') as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Можем прочитать как byte\n", + "with open('tmp.txt', 'rb') as f:\n", + " cont = f.read()\n", + "print(cont)\n", + "type(cont)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# И затем декодировать\n", + "cont_str = cont.decode('cp1251')\n", + "print(cont_str)\n", + "type(cont_str)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# И закодировать в другую кодировку\n", + "bytes(cont_str, 'utf8')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Кодировка по-умолчанию - utf8\n", + "bytes(cont_str, 'utf8').decode()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Материалы по теме\n", + "\n", + "Статья про кодировки на хабре: [Что нужно знать каждому разработчику о кодировках и наборах символов для работы с текстом](https://habrahabr.ru/post/158639/)\n", + "\n", + "И сразу про нормализацию строк: [«Й» вам не «и» краткое! О важности нормализации Unicode](https://habrahabr.ru/post/262679/)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bytes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# могут интерпретироваться как строки\n", + "b = bytes(cont_str, 'utf8')\n", + "print(b[1:5])\n", + "print(b.split())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# и как последовательности чисел\n", + "print(b[5])\n", + "print(list(b))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# иммутабельны как и строки\n", + "b[4] = '6'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# но есть и мутабельная версия\n", + "ba = bytearray(b)\n", + "print(ba[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# хоть в этом и мало смысла\n", + "ba[5] += 1\n", + "print(ba.decode())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# для чего-то низкоуровнего\n", + "ba = bytearray(range(50))\n", + "print(ba)\n", + "print(list(ba))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Бинарные форматы" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import struct\n", + "\n", + "bmp = open(\"VENUS.bmp\", 'rb')\n", + "print('Type:', bmp.read(2).decode())\n", + "print('Size: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Reserved 1: %s' % struct.unpack('H', bmp.read(2)))\n", + "print('Reserved 2: %s' % struct.unpack('H', bmp.read(2)))\n", + "print('Offset: %s' % struct.unpack('I', bmp.read(4)))\n", + "\n", + "print('DIB Header Size: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Width: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Height: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Colour Planes: %s' % struct.unpack('H', bmp.read(2)))\n", + "print('Bits per Pixel: %s' % struct.unpack('H', bmp.read(2)))\n", + "print('Compression Method: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Raw Image Size: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Horizontal Resolution: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Vertical Resolution: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Number of Colours: %s' % struct.unpack('I', bmp.read(4)))\n", + "print('Important Colours: %s' % struct.unpack('I', bmp.read(4)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import struct\n", + "\n", + "bmp = open(\"VENUS.bmp\", 'rb')\n", + "print('Type:', bmp.read(2).decode())\n", + "tpl = struct.unpack('IHHII', bmp.read(16))\n", + "print(tpl)\n", + "print('Size: {}; Reserved 1: {}; Reserved 2: {}; Offset {}'.format(*tpl))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import struct\n", + "example = 'Hello world'\n", + "ln = len(example)\n", + "enc = struct.pack('I{}s'.format(ln), ln, example.encode())\n", + "print(enc)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ln = struct.unpack_from('I', enc)[0]\n", + "print(ln)\n", + "enc = enc[struct.calcsize('I'):]\n", + "example = struct.unpack_from('{}s'.format(ln), enc)[0]\n", + "print(example)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pickle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pickle\n", + "\n", + "class A:\n", + " fld = 123\n", + " \n", + " def __init__(self, a, b):\n", + " self.a = a\n", + " self.b = b\n", + " \n", + " def __repr__(self):\n", + " return 'A({0.a},{0.b})'.format(self)\n", + " \n", + "t1 = [1, {'a': (1, 2, 3), 'b': set([1,2,3,4,5,4,3,2,1])}, 3, A(4, 5)]\n", + "s = pickle.dumps(t1)\n", + "print(s)\n", + "t2 = pickle.loads(s)\n", + "print(t2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Файловая система" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os # почитайте что там есть - очень полезно для скриптов\n", + "os.path.exists('/tmp')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from glob import glob\n", + "glob('../*/*.ipynb')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for t in os.walk('../'):\n", + " print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for curr_dir, sub_dirs, files in os.walk('../'):\n", + " for file in files:\n", + " if file.endswith('.ipynb'):\n", + " print(os.path.join(curr_dir, file))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# установим пакет\n", + "# !pip install requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import json\n", + "resp = requests.get('http://date.jsontest.com/')\n", + "print(resp, resp.ok, resp.status_code)\n", + "print(resp.content)\n", + "print(json.loads(resp.content))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(resp.json())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Многопоточность" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Потоки. Потоки здорового человека" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# просто пример отправки данных - так в жизни делать не надо)\n", + "base_url = \"http://md5.jsontest.com/?text=\"\n", + "import requests\n", + "\n", + "def slow_md5(data):\n", + " response = requests.get(base_url + data)\n", + " if response.ok:\n", + " rez = response.json()\n", + " return rez.get('md5')\n", + "\n", + "print(slow_md5('test1'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_strings = []\n", + "for i in range(50):\n", + " test_strings.append(f'test_{i}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "rez = {}\n", + "for data in test_strings:\n", + " rez[data] = slow_md5(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "import threading\n", + "threads_num = 4\n", + "\n", + "rez = {}\n", + "def thread_run(idx):\n", + " while idx < len(test_strings):\n", + " data = test_strings[idx]\n", + " rez[data] = slow_md5(data)\n", + " idx += threads_num\n", + " print(f'Thread end: {idx}')\n", + "\n", + "threads = []\n", + "for i in range(threads_num):\n", + " t = threading.Thread(target=thread_run, args=(i,))\n", + " t.start()\n", + " threads.append(t)\n", + "\n", + "for t in threads:\n", + " t.join()\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "sys.version" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Потоки. Потоки курильщика" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def slow_fib(n):\n", + " if n == 0:\n", + " return 0\n", + " if n < 3:\n", + " return 1\n", + " return slow_fib(n - 1) + slow_fib(n - 2)\n", + "\n", + "print(slow_fib(10))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_ns = []\n", + "for i in range(35):\n", + " test_ns.append(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "rez = {}\n", + "for n in test_ns:\n", + " rez[n] = slow_fib(n)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "import threading\n", + "threads_num = 4\n", + "\n", + "rez = {}\n", + "# простое определение потока\n", + "def thread_run(idx):\n", + " while idx < len(test_ns):\n", + " data = test_ns[idx]\n", + " rez[data] = slow_fib(data)\n", + " idx += threads_num\n", + " print(f'Thread end: {idx}')\n", + "\n", + "threads = []\n", + "for i in range(threads_num):\n", + " t = threading.Thread(target=thread_run, args=(i,))\n", + " t.start()\n", + " threads.append(t)\n", + "\n", + "for t in threads:\n", + " t.join()\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## GIL\n", + "- https://wiki.python.org/moin/GlobalInterpreterLock\n", + "- https://habrahabr.ru/post/84629/" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Что делать\n", + "- numpy http://www.numpy.org\n", + "- процессы\n", + "- Py_BEGIN_ALLOW_THREADS в C extension [документация](http://www.cmi.ac.in/~madhavan/courses/prog2-2015/docs/python-3.4.2-docs-html/c-api/init.html)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Процессы" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "import multiprocessing\n", + "\n", + "# Выделяем пул процессов\n", + "p = multiprocessing.Pool(processes=6)\n", + "\n", + "# Запускаем параллельную работу\n", + "result = p.map(slow_fib, test_ns)\n", + "print(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import multiprocessing as mp\n", + "\n", + "def foo(q):\n", + " q.put('hello')\n", + "\n", + "if __name__ == '__main__':\n", + " q = mp.Queue()\n", + " p = mp.Process(target=foo, args=(q,))\n", + " p.start()\n", + " print(q.get())\n", + " p.join()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# требует аккуратного проектирования (если бы не было timeout - зависло бы навсегда)\n", + "q.get(timeout=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Модуль subprocess" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "pr = subprocess.run('date')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pr.stdout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pr = subprocess.run('date', stdout=subprocess.PIPE)\n", + "pr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(pr.stdout.decode())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "subprocess.run('ls -l', stdout=subprocess.PIPE)\n", + "# subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pr = subprocess.Popen('sort', stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n", + "for i in range(25):\n", + " pr.stdin.write(f'{i}\\n'.encode())\n", + "out, err = pr.communicate()\n", + "print(out.decode())\n", + "pr.terminate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pr.returncode" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "https://amoffat.github.io/sh/ - более удобный способ" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Работа с сетью\n", + "\n", + "https://ru.wikipedia.org/wiki/TCP/IP\n", + "\n", + "## Сокеты" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import socket\n", + "def listen():\n", + " connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + " connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n", + " connection.bind(('0.0.0.0', 5555))\n", + " connection.listen(10)\n", + " while True:\n", + " current_connection, address = connection.accept()\n", + " while True:\n", + " data = current_connection.recv(2048)\n", + "\n", + " if data == b'quit\\n':\n", + " current_connection.shutdown(1)\n", + " current_connection.close()\n", + " break\n", + " elif data == b'stop\\n':\n", + " current_connection.shutdown(1)\n", + " current_connection.close()\n", + " return\n", + " elif data:\n", + " current_connection.send(data.upper())\n", + " \n", + "listen()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HTTP под капотом" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import socket\n", + "import sys\n", + "s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + "host = 'md5.jsontest.com'\n", + "port = 80\n", + "s.connect((host, port))\n", + "s.send(b'GET /?text=test1 HTTP/1.1\\n')\n", + "s.send(b'host: md5.jsontest.com\\n')\n", + "s.send(b'\\n\\n')\n", + "data = s.recv(1000000) \n", + "print(data.decode())\n", + "s.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "from http.server import BaseHTTPRequestHandler, HTTPServer\n", + "\n", + "HOST_NAME = 'localhost'\n", + "PORT_NUMBER = 8080\n", + "\n", + "class MyHandler(BaseHTTPRequestHandler):\n", + " def do_HEAD(s):\n", + " s.send_response(200)\n", + " s.send_header(\"Content-type\", \"text/html\")\n", + " s.end_headers()\n", + " def do_GET(s):\n", + " \"\"\"Respond to a GET request.\"\"\"\n", + " s.send_response(200)\n", + " s.send_header(\"Content-type\", \"text/html\")\n", + " s.end_headers()\n", + " s.wfile.write(b\"Title goes here.\")\n", + " s.wfile.write(b\"

This is a test.

\")\n", + "\n", + " s.wfile.write(b\"

You accessed path: {}

\".format(s.path.encode()))\n", + " s.wfile.write(b\"\")\n", + "\n", + "if __name__ == '__main__':\n", + " server_class = HTTPServer\n", + " httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler)\n", + " print(time.asctime(), f\"Server Starts - {HOST_NAME}:{PORT_NUMBER}\")\n", + " try:\n", + " httpd.serve_forever()\n", + " except KeyboardInterrupt:\n", + " pass\n", + " httpd.server_close()\n", + " print(time.asctime(), f\"Server Stops - {HOST_NAME}:{PORT_NUMBER}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HTTP с фреймворком\n", + "\n", + "Простой веб-сервер на Tornado\n", + "http://www.tornadoweb.org/en/stable/web.html - Документация\n", + "\n", + "```python\n", + "import tornado.ioloop\n", + "import tornado.web\n", + "\n", + "class MainHandler(tornado.web.RequestHandler):\n", + " def get(self):\n", + " self.write(\"Hello, world\")\n", + "\n", + "def make_app():\n", + " return tornado.web.Application([\n", + " (r\"/\", MainHandler),\n", + " ])\n", + "\n", + "if __name__ == \"__main__\":\n", + " app = make_app()\n", + " app.listen(8080)\n", + " tornado.ioloop.IOLoop.current().start()\n", + "```\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Домашнее задание\n", + "Сервер очередей заданий на базе TCP сокетов - task_queue" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/talks/05_meta/fotr.ee.elrond.jpg b/talks/05_meta/fotr.ee.elrond.jpg new file mode 100644 index 0000000..e936957 Binary files /dev/null and b/talks/05_meta/fotr.ee.elrond.jpg differ diff --git a/talks/05_meta/main.ipynb b/talks/05_meta/main.ipynb new file mode 100644 index 0000000..14b78a5 --- /dev/null +++ b/talks/05_meta/main.ipynb @@ -0,0 +1,1389 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python data model\n", + "========" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ListView([0, 1, 2, 3, 4], 1, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__len__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(ListView([0, 1, 2, 3, 4], 1, 3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__iter__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(ListView([0, 1, 2, 3, 4], 1, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start\n", + " \n", + " def __iter__(self):\n", + " for i in range(self._start, self._stop):\n", + " yield self._lst[i]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(ListView([0, 1, 2, 3, 4], 1, 3))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "itr = iter(ListView([0, 1, 2, 3, 4], 1, 3))\n", + "itr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(itr)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__getitem__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start\n", + " \n", + " def __getitem__(self, idx):\n", + " if self._start + idx < self._stop:\n", + " return self._lst[self._start + idx]\n", + " else:\n", + " raise IndexError(idx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ListView([0, 1, 2, 3, 4], 1, 3)[1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(ListView([0, 1, 2, 3, 4], 1, 3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__setitem__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start\n", + " \n", + " def __getitem__(self, idx):\n", + " if self._start + idx < self._stop:\n", + " return self._lst[self._start + idx]\n", + " else:\n", + " raise IndexError(idx)\n", + " \n", + " def __setitem__(self, idx, value):\n", + " if self._start + idx < self._stop:\n", + " self._lst[self._start + idx] = value\n", + " else:\n", + " raise IndexError(idx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = [0, 1, 2, 3, 4]\n", + "view = ListView(lst, 1, 3)\n", + "list(view)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "view[1] = 7" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(view)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__add__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ViewOffset:\n", + " def __init__(self, value):\n", + " self.value = value\n", + "\n", + "\n", + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start\n", + " \n", + " def __getitem__(self, idx):\n", + " if self._start + idx < self._stop:\n", + " return self._lst[self._start + idx]\n", + " else:\n", + " raise IndexError(idx)\n", + " \n", + " def __add__(self, offset):\n", + " if self._stop + offset.value > len(self._lst):\n", + " raise ValueError(offset)\n", + " \n", + " return type(self)(self._lst, self._start + offset.value, self._stop + offset.value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = [0, 1, 2, 3, 4, 5]\n", + "v = ListView(lst, 1, 3)\n", + "list(v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(v + ViewOffset(2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(ViewOffset(2) + v)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__radd__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class ViewOffset:\n", + " def __init__(self, value):\n", + " self.value = value\n", + "\n", + "\n", + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " def __len__(self):\n", + " return self._stop - self._start\n", + " \n", + " def __getitem__(self, idx):\n", + " if self._start + idx < self._stop:\n", + " return self._lst[self._start + idx]\n", + " else:\n", + " raise IndexError(idx)\n", + " \n", + " def __add__(self, offset):\n", + " if self._stop + offset.value > len(self._lst):\n", + " raise ValueError(offset)\n", + " \n", + " return type(self)(self._lst, self._start + offset.value, self._stop + offset.value)\n", + " \n", + " def __radd__(self, offset):\n", + " return self + offset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = [0, 1, 2, 3, 4, 5]\n", + "v = ListView(lst, 1, 3)\n", + "list(ViewOffset(2) + v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = [0, 1, 2, 3, 4, 5]\n", + "v = ListView(lst, 1, 3)\n", + "list(ViewOffset(1) + ViewOffset(1) + v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# def __add__(self, other):\n", + "# if isinstance(other, type(self)):\n", + "# return type(self)(self.value + other.value)\n", + "# else:\n", + "# return NotImplemented" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`collections.abc`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import Sequence\n", + "\n", + "class ListView:\n", + " def __init__(self, lst, start, stop):\n", + " self._lst = lst\n", + " self._start = start\n", + " self._stop = stop\n", + " \n", + " #def __len__(self):\n", + " # return self._stop - self._start\n", + " \n", + " def __getitem__(self, idx):\n", + " if self._start + idx < self._stop:\n", + " return self._lst[self._start + idx]\n", + " else:\n", + " raise IndexError(idx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(ListView([0, 1, 2, 3, 4], 1, 3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`__new__`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Interval:\n", + " def __init__(self, begin, end):\n", + " self.begin = begin\n", + " self.end = end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "i = Interval(1, 2)\n", + "i.begin, i.end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Interval:\n", + " def __new__(cls, begin, end):\n", + " if begin > end:\n", + " return None\n", + " return super().__new__(cls)\n", + " \n", + " def __init__(self, begin, end):\n", + " self.begin = begin\n", + " self.end = end" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(Interval(2, 1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Декораторы\n", + "===" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Декоратор функции\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def sqr(x):\n", + " return x * x\n", + "\n", + "def half(x):\n", + " return x / 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sqr(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "half(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "_sqr_cache = {}\n", + "def sqr(x):\n", + " if x not in _sqr_cache:\n", + " _sqr_cache[x] = x * x\n", + " return _sqr_cache[x]\n", + "\n", + "_half_cache = {}\n", + "def half(x):\n", + " if x not in _half_cache:\n", + " _half_cache[x] = x // 2\n", + " return _half_cache[x]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sqr(5), half(20)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_sqr_cache, _half_cache" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@cached\n", + "def sqr(x):\n", + " print('calculating sqr')\n", + " return x * x\n", + "\n", + "@cached\n", + "def half(x):\n", + " print('calculating half')\n", + " return x / 2\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sqr(2), half(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(func):\n", + " def new_func(x):\n", + " return func(x)\n", + "\n", + " return new_func" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(func):\n", + " cache = {}\n", + "\n", + " def new_func(x):\n", + " if x not in cache:\n", + " cache[x] = func(x)\n", + " return cache[x]\n", + "\n", + " return new_func" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sqr(x):\n", + " print('calculating sqr')\n", + " return x * x\n", + "\n", + "sqr = cached(sqr)\n", + "sqr(2), sqr(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Декоратор функции с параметрами\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "@cached(limit=3)\n", + "def sqr(x):\n", + " print('calculating sqr')\n", + " return x * x " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "sqr = cached(limit=3)(sqr)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(limit):\n", + " def decorator(func):\n", + " pass\n", + " \n", + " return decorator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(limit):\n", + " def decorator(func):\n", + " def new_func(x):\n", + " return func(x)\n", + "\n", + " return new_func\n", + " \n", + " return decorator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sqr(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`@wraps`\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from functools import wraps\n", + "\n", + "def cached(func):\n", + " cache = {}\n", + "\n", + " def new_func(x):\n", + " if x not in cache:\n", + " cache[x] = func(x)\n", + " return cache[x]\n", + "\n", + " return new_func\n", + "\n", + "@cached\n", + "def sqr(x):\n", + " \"\"\"Returns the square of X\"\"\"\n", + " return x * x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sqr.__name__, sqr.__doc__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Декораторы класса\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Number:\n", + " def __init__(self, value):\n", + " self._value = value\n", + " \n", + " def sqr(self):\n", + " return type(self)(self._value * self._value)\n", + " \n", + " def half(self):\n", + " return type(self)(self._value // 2)\n", + " \n", + " def __repr__(self):\n", + " return 'Number({value})'.format(value=self._value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(3).sqr()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Вручную декорируем методы\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cached(method): ###\n", + " method_name = method.__name__\n", + " \n", + " def new_method(self):\n", + " if method_name not in self.CACHE:\n", + " self.CACHE[method_name] = {}\n", + " cache = self.CACHE[method_name]\n", + "\n", + " if self._value not in cache:\n", + " cache[self._value] = method(self)\n", + " return cache[self._value]\n", + "\n", + " return new_method\n", + "\n", + "class Number:\n", + " CACHE = {} ###\n", + " \n", + " def __init__(self, value):\n", + " self._value = value\n", + " \n", + " @cached ###\n", + " def sqr(self):\n", + " return type(self)(self._value * self._value)\n", + " \n", + " @cached ###\n", + " def half(self):\n", + " return type(self)(self._value // 2)\n", + " \n", + " def __repr__(self):\n", + " return 'Number({value})'.format(value=self._value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(5).half()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(4).CACHE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Автоматически декорируем известные методы\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(method):\n", + " method_name = method.__name__\n", + " \n", + " def new_method(self):\n", + " if method_name not in self.CACHE:\n", + " self.CACHE[method_name] = {}\n", + " cache = self.CACHE[method_name]\n", + "\n", + " if self._value not in cache:\n", + " cache[self._value] = method(self)\n", + " return cache[self._value]\n", + "\n", + " return new_method\n", + "\n", + "def cache_all(klass): ###\n", + " for attr_name in ['sqr', 'half']:\n", + " attr = getattr(klass, attr_name)\n", + " setattr(klass, attr_name, cached(attr))\n", + " return klass\n", + "\n", + "@cache_all ###\n", + "class Number:\n", + " CACHE = {}\n", + " \n", + " def __init__(self, value):\n", + " self._value = value\n", + " \n", + " ###\n", + " def sqr(self):\n", + " return type(self)(self._value * self._value)\n", + " \n", + " ###\n", + " def half(self):\n", + " return type(self)(self._value // 2)\n", + " \n", + " def __repr__(self):\n", + " return 'Number({value})'.format(value=self._value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(5).half()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number.CACHE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Автоматически декорируем помеченные методы\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cached(method): ###\n", + " method._cached = True\n", + " return method\n", + "\n", + "def _cached(method): ###\n", + " method_name = method.__name__\n", + " \n", + " def new_method(self):\n", + " if method_name not in self.CACHE:\n", + " self.CACHE[method_name] = {}\n", + " cache = self.CACHE[method_name]\n", + "\n", + " if self._value not in cache:\n", + " cache[self._value] = method(self)\n", + " return cache[self._value]\n", + "\n", + " return new_method\n", + "\n", + "def cached_methods(klass):\n", + " for attr_name in klass.__dict__:\n", + " attr = getattr(klass, attr_name)\n", + " if hasattr(attr, '_cached') and attr._cached:\n", + " setattr(klass, attr_name, _cached(attr))\n", + " \n", + " return klass\n", + "\n", + "@cached_methods ###\n", + "class Number:\n", + " CACHE = {}\n", + " \n", + " def __init__(self, value):\n", + " self._value = value\n", + " \n", + " @cached ###\n", + " def sqr(self):\n", + " return type(self)(self._value * self._value)\n", + " \n", + " @cached ###\n", + " def half(self):\n", + " return type(self)(self._value // 2)\n", + " \n", + " def __repr__(self):\n", + " return 'Number({value})'.format(value=self._value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(5).half()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number.CACHE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Параметризуем имя кеша\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def cached(method):\n", + " method._cached = True\n", + " return method\n", + "\n", + "def _cached(method, cache_name): ###\n", + " method_name = method.__name__\n", + "\n", + " def new_method(self):\n", + " global_cache = getattr(self, cache_name) ###\n", + " if method_name not in global_cache:\n", + " global_cache[method_name] = {}\n", + " cache = global_cache[method_name]\n", + "\n", + " if self._value not in cache:\n", + " cache[self._value] = method(self)\n", + " return cache[self._value]\n", + "\n", + " return new_method\n", + "\n", + "\n", + "def cached_methods(cache_name):\n", + " def decorator(klass):\n", + " setattr(klass, cache_name, {}) ###\n", + " \n", + " for attr_name in klass.__dict__:\n", + " attr = getattr(klass, attr_name)\n", + " if hasattr(attr, '_cached') and attr._cached:\n", + " setattr(klass, attr_name, _cached(attr, cache_name)) ###\n", + " \n", + " return klass\n", + " return decorator\n", + "\n", + "@cached_methods('MY_CACHE') ###\n", + "class Number:\n", + " ###\n", + " \n", + " def __init__(self, value):\n", + " self._value = value\n", + " \n", + " @cached\n", + " def sqr(self):\n", + " return type(self)(self._value * self._value)\n", + " \n", + " @cached\n", + " def half(self):\n", + " return type(self)(self._value // 2)\n", + " \n", + " def __repr__(self):\n", + " return 'Number({value})'.format(value=self._value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number(5).half()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Number.MY_CACHE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Метаклассы\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class MyList(list):\n", + " def get_length(self):\n", + " return len(self)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = MyList()\n", + "lst.append(1)\n", + "lst.append(2)\n", + "lst[0] = 3\n", + "lst, lst.get_length()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MyList = type(\n", + " 'MyList',\n", + " (list,),\n", + " dict(get_length=lambda self: len(self)),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class MyMeta(type):\n", + " def __init__(cls, name, bases, attrs):\n", + " print('Creating {}'.format(cls))\n", + " super().__init__(name, bases, attrs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class MyList(list, metaclass=MyMeta):\n", + " def get_length(self):\n", + " return len(self)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class AllPrivateMeta(type):\n", + " def __new__(meta, name, bases, attrs):\n", + " new_attrs = {}\n", + " for attr_name in attrs:\n", + " new_name = attr_name if attr_name.startswith('_') else '_' + attr_name\n", + " new_attrs[new_name] = attrs[attr_name]\n", + " \n", + " return super().__new__(meta, name, bases, new_attrs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class MyList(list, metaclass=AllPrivateMeta):\n", + " def get_length(self):\n", + " return len(self)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lst = MyList()\n", + "lst._get_length()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Дескрипторы\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Data:\n", + " x = Json()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d = Data()\n", + "type(d.x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Json:\n", + " #\n", + " #\n", + " #\n", + " #\n", + " #\n", + " \n", + " \n", + " \n", + " \n", + " def __set__(self, instance, value):\n", + " setattr(instance, '_' + self._name, value)\n", + " \n", + " def __get__(self, instance, owner):\n", + " if not hasattr(instance, '_' + self._name):\n", + " return None\n", + " data = getattr(instance, '_' + self._name)\n", + " if isinstance(data, str):\n", + " import json\n", + " return json.loads(data)\n", + " \n", + " def __set_name__(self, owner, name):\n", + " print(owner, name)\n", + " self._name = name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d.x = '[1,2,{}]'\n", + "d.x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Person(models.Model):\n", + " first_name = models.CharField(max_length=30)\n", + " last_name = models.CharField(max_length=30)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/talks/06_db/mysql_example.py b/talks/06_db/mysql_example.py new file mode 100644 index 0000000..8e7583f --- /dev/null +++ b/talks/06_db/mysql_example.py @@ -0,0 +1,13 @@ +import pymysql.cursors +import sys +login = "tp_user" +password = "tp_password" +db_name = "sys_base" + +connection = pymysql.connect(host='localhost', user=login, password=password, db=db_name, charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) +sql = 'SELECT first_name, last_name from users_user limit 5;' +cursor = connection.cursor() +cursor.execute(sql) +result = cursor.fetchall() +for user in result: + print('{} {}'.format(user['first_name'], user['last_name'])) diff --git "a/talks/06_db/\320\273\320\265\320\272\321\206\320\270\321\217 6.1.pptx" "b/talks/06_db/\320\273\320\265\320\272\321\206\320\270\321\217 6.1.pptx" new file mode 100644 index 0000000..62b0fa9 Binary files /dev/null and "b/talks/06_db/\320\273\320\265\320\272\321\206\320\270\321\217 6.1.pptx" differ diff --git a/talks/07_async/main.ipynb b/talks/07_async/main.ipynb new file mode 100644 index 0000000..6d268de --- /dev/null +++ b/talks/07_async/main.ipynb @@ -0,0 +1,1130 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`yield from`\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def braced_chain(*gens, before='{', after='}'):\n", + " for gen in gens:\n", + " yield before\n", + " for x in gen:\n", + " yield x\n", + " yield after" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "g2 = lambda: (x**2 for x in range(3))\n", + "g3 = lambda: (x**3 for x in range(3))\n", + "g4 = lambda: (x**4 for x in range(3))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "list(braced_chain(g2(), g3(), g4(), before='<', after='>'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def braced_chain(*gens, before='{', after='}'):\n", + " for gen in gens:\n", + " yield before\n", + " yield from gen\n", + " yield after" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Корутины\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Mean:\n", + " def __init__(self):\n", + " self._sum = 0\n", + " self._n = 0\n", + " \n", + " def add(self, x):\n", + " self._sum += x\n", + " self._n += 1\n", + " \n", + " return x >= self._sum / self._n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = Mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.add(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.add(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.add(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.add(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.add(4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean():\n", + " result = None\n", + " s = 0\n", + " n = 0\n", + " while True:\n", + " x = yield result\n", + " s += x\n", + " n += 1\n", + " result = x >= s / n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "next(m)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean():\n", + " result = None\n", + " s = 0\n", + " n = 0\n", + " while True:\n", + " x = yield result\n", + " if x is None:\n", + " break\n", + " s += x\n", + " n += 1\n", + " result = x >= s / n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "next(m)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m.send(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`RestoringGet`\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "logger = logging.getLogger(name)\n", + "\n", + "\n", + "class RestoringGet:\n", + " MAX_RESTORES = 10\n", + "\n", + " def init(self, url, get_kwargs=None):\n", + " if get_kwargs is None:\n", + " get_kwargs = {}\n", + "\n", + " self._url = url\n", + " self._kwargs = get_kwargs\n", + "\n", + " def get_generator(self):\n", + " restores = 0\n", + " offset = 0\n", + " headers = {}\n", + " while True:\n", + " restores += 1\n", + " if restores > self.MAX_RESTORES:\n", + " raise TooManyRestores()\n", + "\n", + " response = requests.get(\n", + " self._url, headers=headers, **self._kwargs)\n", + " response.raise_for_status()\n", + "\n", + " real_length = yield response\n", + "\n", + " content_length = parse_int(\n", + " response.headers.get('Content-Length'), None)\n", + " if content_length is None \\\n", + " or content_length + offset <= real_length:\n", + " break\n", + "\n", + " logger.info(\n", + " 'GET looks to be interrupted, trying to continue')\n", + " offset = real_length\n", + " headers = {'Range': 'bytes={}-'.format(offset)}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "gen = RestoringGet(...).get_generator()\n", + "response = next(gen)\n", + "while response:\n", + " file = save_response(response)\n", + " try:\n", + " response = gen.send(file.size)\n", + " except StopIteration:\n", + " response = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`yield from` для корутин\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def braced_chain(*gens, before='{', after='}'):\n", + " for gen in gens:\n", + " yield before\n", + " for x in gen:\n", + " yield x\n", + " yield after" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "b = braced_chain(g2(), mean())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "list(b)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def braced_chain(*gens, before='{', after='}'):\n", + " for gen in gens:\n", + " yield before\n", + " yield from gen\n", + " yield after" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = mean()\n", + "b = braced_chain(g2(), m)\n", + "(\n", + " next(b), next(b), next(b), next(b), next(b),\n", + " next(b), next(b), b.send(1), b.send(2), b.send(None)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "b.send(13)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`return` в генераторе\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def mean():\n", + " result = None\n", + " s = 0\n", + " n = 0\n", + " while True:\n", + " x = yield result\n", + " if x is None:\n", + " break\n", + " s += x\n", + " n += 1\n", + " result = x >= s / n\n", + " return s / n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = mean()\n", + "next(m), m.send(1), m.send(2), m.send(None)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def braced_chain(*gens, before='{', after='}'):\n", + " results = []\n", + " for gen in gens:\n", + " yield before\n", + " result = yield from gen\n", + " results.append(result)\n", + " yield after\n", + " return results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "m = mean()\n", + "b = braced_chain(g2(), m)\n", + "(\n", + " next(b), next(b), next(b), next(b), next(b),\n", + " next(b), next(b), b.send(1), b.send(2), b.send(None)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "next(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Communicate\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import subprocess" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = subprocess.run(['python', '-c', 'open(\"/tmp/5555\", \"w\").close()'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = subprocess.run(\n", + " ['perl', '-E', 'say \"out\"; warn \"err\\n\"'],\n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p.stdout, p.stderr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from subprocess import Popen" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = Popen(\n", + " ['perl', '-E', 'say \"out\"; warn \"err\\n\"'],\n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p.communicate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "while True:\n", + " out = p.stdout.readline()\n", + " err = p.stderr.readline()\n", + " if out:\n", + " print(f'out: {out}')\n", + " if err:\n", + " print(f'err: {out}')\n", + " if not out and not err:\n", + " break " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = Popen(\n", + " ['perl', '-E', 'warn \"err\\n\" x 1000000; say \"out\";'],\n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Threads\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import threading\n", + "threads_num = 4\n", + "\n", + "p = Popen(\n", + " ['perl', '-E', 'warn \"err\\n\"; say \"out\";'],\n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", + ")\n", + "\n", + "def log(fh, prefix):\n", + " while True:\n", + " line = fh.readline()\n", + " if line:\n", + " print('{}: {}'.format(prefix, line.decode('utf8')), end='')\n", + " else:\n", + " break\n", + "\n", + "threads = [\n", + " threading.Thread(target=log, args=(p.stdout, 'out')),\n", + " threading.Thread(target=log, args=(p.stderr, 'err')),\n", + "]\n", + "for t in threads:\n", + " t.start()\n", + "for t in threads:\n", + " t.join()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Selector\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import selectors\n", + "\n", + "sel = selectors.DefaultSelector()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "sel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = Popen(\n", + " ['perl', '-E', 'warn \"err\\n\"; say \"out\"; warn \"2\\n\"'],\n", + " stdout=subprocess.PIPE, stderr=subprocess.PIPE\n", + ")\n", + "sel.register(p.stdout, selectors.EVENT_READ, lambda l: print('out: {}'.format(l)))\n", + "sel.register(p.stderr, selectors.EVENT_READ, lambda l: print('err: {}'.format(l)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "while True:\n", + " events = sel.select()\n", + " if not events:\n", + " break\n", + " for key, mask in events:\n", + " callback = key.data\n", + " line = key.fileobj.readline()\n", + " if line:\n", + " callback(line)\n", + " else:\n", + " sel.unregister(key.fileobj)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`asyncio`\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import asyncio\n", + "loop = asyncio.get_event_loop()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p = asyncio.create_subprocess_exec(\n", + " 'perl', '-E', 'warn \"err\\n\"; say \"out\"; warn \"2\\n\"',\n", + " stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "p" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "next(p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "@asyncio.coroutine\n", + "def execute():\n", + " p = yield from asyncio.create_subprocess_exec(\n", + " 'perl', '-E', 'warn \"err\\n\"; say \"out\"; warn \"2\\n\"',\n", + " stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE\n", + " )\n", + " print('>>> {}'.format(p))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "execute()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "loop.run_until_complete(execute())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "@asyncio.coroutine\n", + "def log(fh, prefix):\n", + " while True:\n", + " line = yield from fh.readline() ###\n", + " if line:\n", + " print('{}: {}'.format(prefix, line.decode('utf8')), end='')\n", + " else:\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "@asyncio.coroutine\n", + "def execute(loop):\n", + " p = yield from asyncio.create_subprocess_exec(\n", + " 'perl', '-E', 'warn \"err\\n\"; say \"out\"; warn \"2\\n\"',\n", + " stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE\n", + " )\n", + " \n", + " loop.create_task(log(p.stdout, 'stdout'))\n", + " loop.create_task(log(p.stderr, 'stderr'))\n", + " \n", + " yield from p.wait()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "loop.run_until_complete(execute(loop))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`async` / `await`\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "async def log(fh, prefix):\n", + " while True:\n", + " line = await fh.readline()\n", + " if line:\n", + " print('{}: {}'.format(prefix, line))\n", + " else:\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "async def execute(loop):\n", + " p = await asyncio.create_subprocess_exec(\n", + " 'perl', '-E', 'warn \"err\\n\"; say \"out\"; warn \"2\\n\"',\n", + " stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE\n", + " )\n", + " \n", + " loop.create_task(log(p.stdout, 'stdout'))\n", + " loop.create_task(log(p.stderr, 'stderr'))\n", + " \n", + " await p.wait()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "loop.run_until_complete(execute(loop))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`aiohttp`\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import aiohttp\n", + "import asyncio\n", + "\n", + "async def fetch(session, url):\n", + " async with session.get(url) as response:\n", + " if response.status == 200:\n", + " return await response.text() # return yield from\n", + " else:\n", + " return f'ERROR: {response.status}'\n", + "\n", + "async def download_wiki(article):\n", + " async with aiohttp.ClientSession() as session:\n", + " html = await fetch(session, 'http://de.wikipedia.org/{}'.format(article))\n", + " return html[:15]\n", + "\n", + "loop = asyncio.get_event_loop()\n", + "tasks = asyncio.gather(\n", + " download_wiki('wiki/Évariste_Galois'),\n", + " download_wiki('wiki/Alan_Turing'),\n", + " download_wiki('zzz'),\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "tasks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "loop.run_until_complete(tasks)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Сервер\n", + "===" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from aiohttp import web" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/talks/07_async/server.py b/talks/07_async/server.py new file mode 100644 index 0000000..ad19186 --- /dev/null +++ b/talks/07_async/server.py @@ -0,0 +1,15 @@ +from aiohttp import web +import asyncio + +async def handle(request): + name = request.match_info.get('name', 'Anonymous') + text = f'Hello {name}' + return web.Response(text=text) + +app = web.Application() +app.add_routes([ + web.get('/', handle), + web.get('/{name}', handle), +]) + +web.run_app(app) diff --git a/talks/08_django/08-django.pdf b/talks/08_django/08-django.pdf new file mode 100644 index 0000000..6da8f0c Binary files /dev/null and b/talks/08_django/08-django.pdf differ