├── .gitattributes ├── .gitignore ├── README.md ├── docs ├── abstract_w1.pdf ├── abstract_w2.pdf ├── abstract_w3.pdf ├── abstract_w4.pdf └── abstract_w5.pdf └── projects ├── 1_1_sum_of_digits ├── _task_01_1.pdf └── solution.py ├── 1_2_stairs ├── _task_01_2.pdf └── solution.py ├── 1_3_quadratic_equation ├── _task_01_3.pdf └── solution.py ├── 2_1_key_value_storage ├── _task_02_1.pdf └── storage.py ├── 2_2_to_json_decorator ├── _task_02_2.pdf └── to_json_decorator.py ├── 3_1_FileReader ├── _task_03_1.pdf └── solution.py ├── 3_2_inheritance ├── _task_03_2.pdf └── solution.py ├── 4_1_magic_methods ├── _task_04_1.pdf └── solution.py ├── 4_2_descriptor ├── _task_04_2.pdf └── solution.py ├── 5_1_metrics_client ├── _task_05_metrics_client.pdf ├── client.py └── server_to_test.py └── 6_1_metrics_server_async ├── _task_06_metrics_server_async.pdf ├── client_to_test.py └── server.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #PyCharm 2 | .idea 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | pip-wheel-metadata/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # celery beat schedule file 98 | celerybeat-schedule 99 | 100 | # SageMath parsed files 101 | *.sage.py 102 | 103 | # Environments 104 | .env 105 | .venv 106 | env/ 107 | venv/ 108 | ENV/ 109 | env.bak/ 110 | venv.bak/ 111 | 112 | # Spyder project settings 113 | .spyderproject 114 | .spyproject 115 | 116 | # Rope project settings 117 | .ropeproject 118 | 119 | # mkdocs documentation 120 | /site 121 | 122 | # mypy 123 | .mypy_cache/ 124 | .dmypy.json 125 | dmypy.json 126 | 127 | # Pyre type checker 128 | .pyre/ 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Coursera] Погружение в Python 2 | #### Московский физико-технический институт (МФТИ), Mail.Ru Group, Фонд развития онлайн-образования (ФРОО) 3 | 4 | https://www.coursera.org/learn/diving-in-python 5 | 6 | **Курс 1 из 4 в специализации 'Программирование на Python'** 7 | 8 | Курс покрывает все необходимые для ежедневной работы программиста темы, а также рассказывает про многие особенности языка, которые часто опускают при его изучении. 9 | 10 | В ходе курса вы изучите конструкции языка, типы и структуры данных, функции, научитесь применять объектно-ориентированное и функциональное программирование, узнаете про особенности реализации Python, научитесь писать асинхронный и многопоточный код. 11 | 12 | ### Программа курса 13 | ##### Неделя 1. Введение в Python 14 | * Знакомство с языком, основными конструкциями и базовыми типами. Настройка окружения для работы и выбор среды разработки. 15 | 16 | ##### Неделя 2. Структуры данных и функции 17 | * Новые типы данных — коллекции, функции, функциональное программирование в Python. 18 | 19 | 20 | ##### Неделя 3. Объектно-ориентированное программирование 21 | * Погружение в мир объектно-ориентированного программирования на Python. Создание своих классов, применение наследования и обработка исключений в программах. 22 | 23 | ##### Неделя 4. Углубленный Python 24 | * Более подробное знакомство с работой классов в Python. Создание своих классов, которые поддерживают стандартные протоколы и методы. Отладка и тестирование программ. 25 | 26 | 27 | ##### Неделя 5. Многопоточное и асинхронное программирование 28 | * Процессы и потоки, асинхронный код с помощью asyncio. 29 | * Реализация синхронного клиентского приложения для отправки метрик. 30 | 31 | ##### Неделя 6. Финальный проект 32 | * Реализация асинхронного серверного приложения для получения метрик от множества клиентов. 33 | 34 | [Сертификат об окончании курса] 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/abstract_w1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/docs/abstract_w1.pdf -------------------------------------------------------------------------------- /docs/abstract_w2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/docs/abstract_w2.pdf -------------------------------------------------------------------------------- /docs/abstract_w3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/docs/abstract_w3.pdf -------------------------------------------------------------------------------- /docs/abstract_w4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/docs/abstract_w4.pdf -------------------------------------------------------------------------------- /docs/abstract_w5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/docs/abstract_w5.pdf -------------------------------------------------------------------------------- /projects/1_1_sum_of_digits/_task_01_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/1_1_sum_of_digits/_task_01_1.pdf -------------------------------------------------------------------------------- /projects/1_1_sum_of_digits/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Сумма цифр в строке 3 | 4 | Давайте начнем с несложной задачи. 5 | Ваша цель написать программу (скрипт), 6 | которая будет запускаться из командной строки. 7 | Программа принимает в качестве аргумента строку, состоящую из цифр. 8 | Гарантируется, что других символов в переданном параметре нет 9 | и на вход всегда подается не пустая строка. 10 | 11 | Программа должна вычислить сумму цифр из которых состоит строка 12 | и вывести полученный результат на печать в стандартный вывод. 13 | 14 | Файл с программой должен называться solution.py. 15 | После написания и отладки вашего решения, 16 | вам необходимо загрузить файл solution.py на платформу для проверки. 17 | 18 | Считать переданный параметр можно с помощью модуля стандартной библиотеки sys. 19 | ''' 20 | 21 | import sys 22 | 23 | digit_string = sys.argv[1] 24 | sum_ = 0 25 | 26 | for digit in digit_string: 27 | sum_ += int(digit) 28 | 29 | print(sum_) 30 | -------------------------------------------------------------------------------- /projects/1_2_stairs/_task_01_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/1_2_stairs/_task_01_2.pdf -------------------------------------------------------------------------------- /projects/1_2_stairs/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Рисуем лестницу 3 | 4 | Необходимо написать скрипт, который «нарисует» (выведет на консоль) лестницу. 5 | Количество ступенек в лестнице передается скрипту в качестве параметра. 6 | Гарантируется, что на вход подаются только целые числа > 0. 7 | Чтение данных нужно произвести способом, аналогичным тому, что описан 8 | в предыдущем задании. Ступени должны отображаться с помощью 9 | символа решетки "#" и пробелов. 10 | 11 | Вывод должен содержать только пробелы и символ "#". 12 | Первая строка вывода не должна быть пустой. 13 | Строки вывода лестницы не должны содержать лишних пробелов 14 | в начале и конце строки. Допускается наличие пустой строки 15 | после вывода последней строки, содержащей ступени. 16 | ''' 17 | 18 | import sys 19 | 20 | n = int(sys.argv[1]) 21 | for i in range(n): 22 | print(' ' * (n - i - 1), '#' * (i + 1), sep='') 23 | -------------------------------------------------------------------------------- /projects/1_3_quadratic_equation/_task_01_3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/1_3_quadratic_equation/_task_01_3.pdf -------------------------------------------------------------------------------- /projects/1_3_quadratic_equation/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Корни квадратного уравнения 3 | 4 | Yаписать программу, которая запускается в командной строке с параметрами, 5 | вычисляет значения корней квадратного уравнения и выводит их на печать. 6 | На вход программе подаются коэффициенты a, b и c. 7 | На печать должно выводиться два корня квадратного уравнения. 8 | Обратите внимание на то, как выводятся корни - каждый с новой строки. 9 | 10 | Чтобы не усложнять вашу задачу все коэффициенты, 11 | которые мы будем подавать вам на вход являются коэффициентами, 12 | которые в итоге дают 2 корня квадратного уравнения. 13 | 14 | Корни должны быть приведены к целочисленному виду перед выводом на экран, 15 | порядок вывода корней произвольный. 16 | ''' 17 | 18 | import sys 19 | 20 | a = int(sys.argv[1]) 21 | b = int(sys.argv[2]) 22 | c = int(sys.argv[3]) 23 | 24 | sqrt_D = (b ** 2 - 4 * a * c) ** 0.5 25 | x1 = int(-(b + sqrt_D) / 2 / a) 26 | x2 = int(-(b - sqrt_D) / 2 / a) 27 | 28 | print(x1) 29 | print(x2) 30 | -------------------------------------------------------------------------------- /projects/2_1_key_value_storage/_task_02_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/2_1_key_value_storage/_task_02_1.pdf -------------------------------------------------------------------------------- /projects/2_1_key_value_storage/storage.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Key-value хранилище 3 | 4 | Утилита может вызваться со следующими параметрами: 5 | Вывести на экран из хранилища значения по ключу: 6 | --key <имя ключа> , где <имя ключа> - ключ по которому получаются значения. 7 | Записать в хранилище значения по ключу: 8 | --key <имя ключа> --val <значение>, где <значение> - сохраняемое значение. 9 | ''' 10 | 11 | import os 12 | import tempfile 13 | import json 14 | import argparse 15 | 16 | 17 | def storage_data_get(): 18 | storage_path = os.path.join(tempfile.gettempdir(), 'storage.data') 19 | if os.path.exists(storage_path): 20 | with open(storage_path, 'r', encoding='utf8') as f: 21 | json_data = json.load(f) 22 | return json_data 23 | 24 | 25 | def storage_data_put(data_dict): 26 | storage_path = os.path.join(tempfile.gettempdir(), 'storage.data') 27 | with open(storage_path, 'w', encoding='utf8') as f: 28 | json.dump(data_dict, f) 29 | 30 | 31 | def val_add(k, v): 32 | data = storage_data_get() or dict() 33 | if k in data: 34 | data[k].append(v) 35 | else: 36 | data[k] = [v] 37 | storage_data_put(data) 38 | 39 | 40 | def val_get(k): 41 | data = storage_data_get() 42 | if data is not None and k in data: 43 | print(*data[k], sep=', ') 44 | return data[k] 45 | else: 46 | print(None) 47 | return None 48 | 49 | 50 | def main(): 51 | parser = argparse.ArgumentParser() 52 | parser.add_argument('-k', '--key') 53 | parser.add_argument('-v', '--val') 54 | args = parser.parse_args() 55 | k = args.key 56 | v = args.val 57 | if k and v: 58 | val_add(k, v) 59 | elif k: 60 | val_get(k) 61 | 62 | 63 | if __name__ == '__main__': 64 | main() 65 | -------------------------------------------------------------------------------- /projects/2_2_to_json_decorator/_task_02_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/2_2_to_json_decorator/_task_02_2.pdf -------------------------------------------------------------------------------- /projects/2_2_to_json_decorator/to_json_decorator.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Декоратор to_json 3 | 4 | Чтобы передавать данные между функциями, модулями или разными системами 5 | используются форматы данных. Одним из самых популярных форматов является JSON. 6 | 7 | Напишите декоратор to_json, который можно применить к различным функциям, 8 | чтобы преобразовывать их возвращаемое значение в JSON-формат. 9 | Не забудьте про сохранение корректного имени декорируемой функции. 10 | ''' 11 | 12 | import json 13 | from functools import wraps 14 | 15 | 16 | def to_json(func): 17 | @wraps(func) 18 | def wrapped(*args, **kwargs): 19 | return json.dumps(func(*args, **kwargs)) 20 | 21 | return wrapped 22 | -------------------------------------------------------------------------------- /projects/3_1_FileReader/_task_03_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/3_1_FileReader/_task_03_1.pdf -------------------------------------------------------------------------------- /projects/3_1_FileReader/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Реализация простого класса для чтения из файла 3 | 4 | Первое задание на этой неделе — не сложное, для разогрева. 5 | Ваша задача: написать python-модуль solution.py, внутрь которого необходимо 6 | поместить код класса FileReader. Конструктор этого класса принимает 7 | один параметр: путь до файла на диске. В классе FileReader должен быть 8 | реализован метод read, возвращающий строку - содержимое файла, путь к которому 9 | был указан при создании экземпляра класса. Python модуль должен быть написан 10 | таким образом, чтобы импорт класса FileReader из него не вызвал ошибок. 11 | 12 | При написании реализации метода read, вам нужно учитывать случай, 13 | когда при инициализации был передан путь к несуществующему файлу. 14 | Требуется обработать возникающее при этом исключение FileNotFoundError 15 | и вернуть из метода read пустую строку. 16 | ''' 17 | 18 | 19 | class FileReader: 20 | 21 | def __init__(self, path): 22 | self._path = path 23 | 24 | def read(self): 25 | try: 26 | with open(self._path, "r") as f: 27 | data = f.read() 28 | except IOError: 29 | data = '' 30 | return data 31 | -------------------------------------------------------------------------------- /projects/3_2_inheritance/_task_03_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/3_2_inheritance/_task_03_2.pdf -------------------------------------------------------------------------------- /projects/3_2_inheritance/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Классы и наследование 3 | 4 | https://www.coursera.org/learn/diving-in-python/programming/bd6aI/klassy-i-nasliedovaniie 5 | ''' 6 | 7 | import os 8 | import csv 9 | 10 | CAR_TYPES = {'Car': 'car', 'Truck': 'truck', 'SpecMachine': 'spec_machine'} 11 | PHOTO_FILE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.gif') 12 | 13 | 14 | class CarBase: 15 | def __init__(self, brand, photo_file_name, carrying): 16 | self.car_type = None 17 | self.photo_file_name = photo_file_name 18 | self.brand = brand 19 | self.carrying = float(carrying) 20 | 21 | def get_photo_file_ext(self): 22 | return os.path.splitext(self.photo_file_name)[1] 23 | 24 | 25 | class Car(CarBase): 26 | def __init__(self, brand, photo_file_name, carrying, passenger_seats_count): 27 | super().__init__(brand, photo_file_name, carrying) 28 | self.car_type = CAR_TYPES['Car'] 29 | self.passenger_seats_count = int(passenger_seats_count) 30 | 31 | 32 | class Truck(CarBase): 33 | def __init__(self, brand, photo_file_name, carrying, body_whl): 34 | super().__init__(brand, photo_file_name, carrying) 35 | self.car_type = CAR_TYPES['Truck'] 36 | self.body_width = 0.0 37 | self.body_height = 0.0 38 | self.body_length = 0.0 39 | self.body_volume = 0.0 40 | 41 | if body_whl: 42 | self.set_truck_body(body_whl) 43 | 44 | def set_truck_body(self, body_whl): 45 | try: 46 | l, w, h = map(float, body_whl.split('x')) 47 | except ValueError: 48 | l, w, h = 0.0, 0.0, 0.0 49 | 50 | self.body_width = w 51 | self.body_height = h 52 | self.body_length = l 53 | self.body_volume = w * h * l 54 | 55 | def get_body_volume(self): 56 | return self.body_volume 57 | 58 | 59 | class SpecMachine(CarBase): 60 | def __init__(self, brand, photo_file_name, carrying, extra): 61 | super().__init__(brand, photo_file_name, carrying) 62 | self.car_type = CAR_TYPES['SpecMachine'] 63 | self.extra = extra 64 | 65 | 66 | def get_car_list(csv_filename): 67 | car_list = [] 68 | with open(csv_filename) as csv_f: 69 | reader = csv.reader(csv_f, delimiter=';') 70 | next(reader) # skip csv-header 71 | for row in reader: 72 | car = get_car_from_csv_row(row) 73 | if car: 74 | car_list.append(car) 75 | return car_list 76 | 77 | 78 | def get_car_from_csv_row(row): 79 | if len(row) != 7 or row[0] not in CAR_TYPES.values(): 80 | return None 81 | 82 | car_type, brand, passenger_sc, photo_fn, body_whl, carrying, extra = row 83 | 84 | if car_type not in CAR_TYPES.values(): 85 | return None 86 | 87 | if not (brand and photo_fn and carrying): 88 | return None 89 | 90 | if os.path.splitext(photo_fn)[1] not in PHOTO_FILE_EXTENSIONS: 91 | return None 92 | 93 | try: 94 | carrying = float(carrying) 95 | except ValueError: 96 | return None 97 | 98 | if car_type == CAR_TYPES['Car']: 99 | try: 100 | passenger_sc = int(passenger_sc) 101 | except ValueError: 102 | return None 103 | return Car(brand, photo_fn, carrying, passenger_sc) 104 | 105 | if car_type == CAR_TYPES['Truck']: 106 | return Truck(brand, photo_fn, carrying, body_whl) 107 | 108 | if car_type == CAR_TYPES['SpecMachine']: 109 | if not extra: 110 | return None 111 | return SpecMachine(brand, photo_fn, carrying, extra) 112 | -------------------------------------------------------------------------------- /projects/4_1_magic_methods/_task_04_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/4_1_magic_methods/_task_04_1.pdf -------------------------------------------------------------------------------- /projects/4_1_magic_methods/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Файл с магическими методами 3 | 4 | https://www.coursera.org/learn/diving-in-python/programming/sypSV/fail-s-maghichieskimi-mietodami 5 | ''' 6 | 7 | import os.path 8 | import tempfile 9 | 10 | 11 | class File: 12 | def __init__(self, path): 13 | self._path = path 14 | self._curr = 0 15 | 16 | if not os.path.exists(path): 17 | with open(self._path, 'w'): 18 | pass 19 | 20 | def __str__(self): 21 | return self._path 22 | 23 | def __iter__(self): 24 | return self 25 | 26 | def __next__(self): 27 | with open(self._path, 'r', encoding='utf8') as f: 28 | f.seek(self._curr) 29 | line = f.readline() 30 | 31 | if line: 32 | self._curr = f.tell() 33 | return line 34 | else: 35 | self._curr = 0 36 | raise StopIteration 37 | 38 | def __add__(self, other): 39 | result_path = os.path.join(tempfile.gettempdir(), 'tmp.txt') 40 | result = File(result_path) 41 | result.write(self.read() + other.read()) 42 | return result 43 | 44 | def read(self): 45 | with open(self._path, 'r', encoding='utf8') as f: 46 | return f.read() 47 | 48 | def write(self, text): 49 | with open(self._path, 'w', encoding='utf8') as f: 50 | f.write(text) 51 | 52 | 53 | if __name__ == '__main__': 54 | pass 55 | -------------------------------------------------------------------------------- /projects/4_2_descriptor/_task_04_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/4_2_descriptor/_task_04_2.pdf -------------------------------------------------------------------------------- /projects/4_2_descriptor/solution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Дескриптор с комиссией 3 | 4 | Часто при зачислении каких-то средств на счет с нас берут комиссию. 5 | Давайте реализуем похожий механизм с помощью дескрипторов. 6 | 7 | Напишите дескриптор Value, который будет использоваться в нашем классе Account 8 | ''' 9 | 10 | 11 | class Value: 12 | def __init__(self): 13 | self.value = 0 14 | 15 | def __set__(self, obj, value): 16 | self.value = value * (1 - obj.commission) 17 | 18 | def __get__(self, obj, obj_type): 19 | return self.value 20 | 21 | 22 | class Account: 23 | amount = Value() 24 | 25 | def __init__(self, commission): 26 | self.commission = commission 27 | 28 | 29 | if __name__ == '__main__': 30 | pass 31 | # test: 32 | # acc = Account(0.1) 33 | # acc.amount = 100 34 | # print(acc.amount) 35 | -------------------------------------------------------------------------------- /projects/5_1_metrics_client/_task_05_metrics_client.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/5_1_metrics_client/_task_05_metrics_client.pdf -------------------------------------------------------------------------------- /projects/5_1_metrics_client/client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Клиент для отправки метрик 3 | https://www.coursera.org/learn/diving-in-python/programming/aG3x3/kliient-dlia-otpravki-mietrik 4 | """ 5 | 6 | import socket 7 | import time 8 | 9 | 10 | class ClientError(Exception): 11 | pass 12 | 13 | 14 | class Client: 15 | def __init__(self, host, port, timeout=None): 16 | self._host = host 17 | self._port = port 18 | self._timeout = timeout 19 | try: 20 | self._sock = socket.create_connection((self._host, self._port), self._timeout) 21 | except socket.error as err: 22 | raise ClientError(err) 23 | 24 | def put(self, metric_key, metric_value, timestamp=None): 25 | timestamp = str(timestamp or int(time.time())) 26 | send_data = f'put {metric_key} {metric_value} {timestamp}\n'.encode('utf8') 27 | 28 | try: 29 | self._sock.sendall(send_data) 30 | response = self._sock.recv(1024) 31 | if b'ok\n' not in response: 32 | raise ClientError 33 | except Exception: 34 | raise ClientError 35 | 36 | def get(self, key): 37 | metric_dict = {} 38 | send_data = f'get {key}\n'.encode('utf8') 39 | 40 | try: 41 | self._sock.sendall(send_data) 42 | response = self._sock.recv(1024) 43 | if b'ok' not in response: 44 | raise ClientError 45 | 46 | response = str(response).strip('\n').split('\\n') 47 | 48 | for m in response: 49 | metrics = m.split(' ') 50 | if len(metrics) == 3: 51 | metric_key = metrics[0] 52 | metric_value = float(metrics[1]) 53 | metric_timestamp = int(metrics[2]) 54 | metric_list = metric_dict.get(metric_key, []) 55 | metric_list.append((metric_timestamp, metric_value)) 56 | metric_dict.update({metric_key: sorted(metric_list)}) 57 | elif metrics not in [["b'ok"], [""], ["'"]]: 58 | raise ClientError 59 | 60 | return metric_dict 61 | 62 | except Exception as err: 63 | raise ClientError(err) 64 | 65 | 66 | if __name__ == '__main__': 67 | client = Client('127.0.0.1', 8888, timeout=15) 68 | print(client.get('*')) 69 | -------------------------------------------------------------------------------- /projects/5_1_metrics_client/server_to_test.py: -------------------------------------------------------------------------------- 1 | # реализация сервера для тестирования метода get по заданию - Клиент для отправки метрик 2 | # -*- coding: utf-8 -*- 3 | 4 | import socket 5 | 6 | sock = socket.socket() 7 | sock.bind(('127.0.0.1', 8888)) 8 | sock.listen() 9 | conn, addr = sock.accept() 10 | 11 | print('Соединение установлено:', addr) 12 | 13 | # переменная response хранит строку возвращаемую сервером, если вам для 14 | # тестирования клиента необходим другой ответ, измените ее 15 | response = b'ok\npalm.cpu 10.5 1501864247\neardrum.cpu 15.3 1501864259\n\n' 16 | 17 | while True: 18 | data = conn.recv(1024) 19 | if not data: 20 | break 21 | request = data.decode('utf-8') 22 | print(f'Получен запрос: {ascii(request)}') 23 | print(f'Отправлен ответ {ascii(response.decode("utf-8"))}') 24 | conn.send(response) 25 | 26 | conn.close() 27 | -------------------------------------------------------------------------------- /projects/6_1_metrics_server_async/_task_06_metrics_server_async.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n1k-n1k/programming-in-python-1--diving-in-python--coursera-MIPT-MailRuGroup/8ccf7abefdd36046192b7a5e813c58a18a244891/projects/6_1_metrics_server_async/_task_06_metrics_server_async.pdf -------------------------------------------------------------------------------- /projects/6_1_metrics_server_async/client_to_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Это вспомогательный скрипт для тестирования сервера из задания на неделе 6. 3 | 4 | Сначала запускаете ваш сервер на адресе 127.0.0.1 и порту 8888, а затем 5 | запускаете этот скрипт. 6 | """ 7 | 8 | import sys 9 | import bisect 10 | import socket 11 | import time 12 | 13 | 14 | class ClientError(Exception): 15 | """класс исключений клиента""" 16 | pass 17 | 18 | 19 | class Client: 20 | def __init__(self, host, port, timeout=None): 21 | self.host = host 22 | self.port = port 23 | self.timeout = timeout 24 | 25 | try: 26 | self.connection = socket.create_connection((host, port), timeout) 27 | except socket.error as err: 28 | raise ClientError("Cannot create connection", err) 29 | 30 | def _read(self): 31 | 32 | data = b"" 33 | 34 | while not data.endswith(b"\n\n"): 35 | try: 36 | data += self.connection.recv(1024) 37 | except socket.error as err: 38 | raise ClientError("Error reading data from socket", err) 39 | 40 | return data.decode('utf-8') 41 | 42 | def _send(self, data): 43 | 44 | try: 45 | self.connection.sendall(data) 46 | except socket.error as err: 47 | raise ClientError("Error sending data to server", err) 48 | 49 | def put(self, key, value, timestamp=None): 50 | 51 | timestamp = timestamp or int(time.time()) 52 | self._send(f"put {key} {value} {timestamp}\n".encode()) 53 | raw_data = self._read() 54 | 55 | if raw_data == 'ok\n\n': 56 | return 57 | raise ClientError('Server returns an error') 58 | 59 | def get(self, key): 60 | 61 | self._send(f"get {key}\n".encode()) 62 | raw_data = self._read() 63 | data = {} 64 | status, payload = raw_data.split("\n", 1) 65 | payload = payload.strip() 66 | 67 | if status != 'ok': 68 | raise ClientError('Server returns an error') 69 | 70 | if payload == '': 71 | return data 72 | 73 | try: 74 | 75 | for row in payload.splitlines(): 76 | key, value, timestamp = row.split() 77 | if key not in data: 78 | data[key] = [] 79 | bisect.insort(data[key], ((int(timestamp), float(value)))) 80 | 81 | except Exception as err: 82 | raise ClientError('Server returns invalid data', err) 83 | 84 | return data 85 | 86 | def close(self): 87 | 88 | try: 89 | self.connection.close() 90 | except socket.error as err: 91 | raise ClientError("Error. Do not close the connection", err) 92 | 93 | 94 | def run(host, port): 95 | client1 = Client(host, port, timeout=5) 96 | client2 = Client(host, port, timeout=5) 97 | command = "wrong command test\n" 98 | 99 | try: 100 | data = client1.get(command) 101 | except ClientError: 102 | pass 103 | except BaseException as err: 104 | print(f"Ошибка соединения с сервером: {err.__class__}: {err}") 105 | sys.exit(1) 106 | else: 107 | print("Неверная команда, отправленная серверу, должна возвращать ошибку протокола") 108 | sys.exit(1) 109 | 110 | command = 'some_key' 111 | try: 112 | data_1 = client1.get(command) 113 | data_2 = client1.get(command) 114 | except ClientError: 115 | print('Сервер вернул ответ на валидный запрос, который клиент определил, ' 116 | 'как не корректный.. ') 117 | except BaseException as err: 118 | print(f"Сервер должен поддерживать соединение с клиентом между запросами, " 119 | f"повторный запрос к серверу завершился ошибкой: {err.__class__}: {err}") 120 | sys.exit(1) 121 | 122 | assert data_1 == data_2 == {}, \ 123 | "На запрос клиента на получения данных по не существующему ключу, сервер " \ 124 | "вдолжен озвращать ответ с пустым полем данных." 125 | 126 | try: 127 | data_1 = client1.get(command) 128 | data_2 = client2.get(command) 129 | except ClientError: 130 | print('Сервер вернул ответ на валидный запрос, который клиент определил' 131 | ', как не корректный.. ') 132 | except BaseException as err: 133 | print(f"Сервер должен поддерживать соединение с несколькими клиентами: " 134 | f"{err.__class__}: {err}") 135 | sys.exit(1) 136 | 137 | assert data_1 == data_2 == {}, \ 138 | "На запрос клиента на получения данных по не существующему ключу, сервер " \ 139 | "должен возвращать ответ с пустым полем данных." 140 | 141 | try: 142 | client1.put("k1", 0.25, timestamp=1) 143 | client2.put("k1", 2.156, timestamp=2) 144 | client1.put("k1", 0.35, timestamp=3) 145 | client2.put("k2", 30, timestamp=4) 146 | client1.put("k2", 40, timestamp=5) 147 | client1.put("k2", 41, timestamp=5) 148 | except Exception as err: 149 | print(f"Ошибка вызова client.put(...) {err.__class__}: {err}") 150 | sys.exit(1) 151 | 152 | expected_metrics = { 153 | "k1": [(1, 0.25), (2, 2.156), (3, 0.35)], 154 | "k2": [(4, 30.0), (5, 41.0)], 155 | } 156 | 157 | try: 158 | metrics = client1.get("*") 159 | if metrics != expected_metrics: 160 | print(f"client.get('*') вернул неверный результат. Ожидается: " 161 | f"{expected_metrics}. Получено: {metrics}") 162 | sys.exit(1) 163 | except Exception as err: 164 | print(f"Ошибка вызова client.get('*') {err.__class__}: {err}") 165 | sys.exit(1) 166 | 167 | expected_metrics = {"k2": [(4, 30.0), (5, 41.0)]} 168 | 169 | try: 170 | metrics = client2.get("k2") 171 | if metrics != expected_metrics: 172 | print(f"client.get('k2') вернул неверный результат. Ожидается: " 173 | f"{expected_metrics}. Получено: {metrics}") 174 | sys.exit(1) 175 | except Exception as err: 176 | print(f"Ошибка вызова client.get('k2') {err.__class__}: {err}") 177 | sys.exit(1) 178 | 179 | try: 180 | result = client1.get("k3") 181 | if result != {}: 182 | print( 183 | f"Ошибка вызова метода get с ключом, который еще не был добавлен. " 184 | f"Ожидается: пустой словарь. Получено: {result}") 185 | sys.exit(1) 186 | except Exception as err: 187 | print(f"Ошибка вызова метода get с ключом, который еще не был добавлен: " 188 | f"{err.__class__} {err}") 189 | sys.exit(1) 190 | 191 | print("Похоже, что все верно! Попробуйте отправить решение на проверку.") 192 | 193 | 194 | if __name__ == "__main__": 195 | run("127.0.0.1", 8888) 196 | -------------------------------------------------------------------------------- /projects/6_1_metrics_server_async/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Асинхронный сервер для приема метрик 3 | https://www.coursera.org/learn/diving-in-python/programming/Xcdpa/siervier-dlia-priiema-mietrik 4 | """ 5 | 6 | import asyncio 7 | 8 | metrics_dict = dict() 9 | 10 | 11 | class ClientServerProtocol(asyncio.Protocol): 12 | def connection_made(self, transport): 13 | self.transport = transport 14 | 15 | def data_received(self, data): 16 | resp = process_data(data.decode()) 17 | self.transport.write(resp.encode()) 18 | 19 | 20 | def process_data(data): 21 | msg_ok = 'ok\n\n' 22 | msg_wrong_command = 'error\nwrong command\n\n' 23 | msg_index_error = 'Index error' 24 | 25 | if data.strip('\n').strip() == '': 26 | return msg_wrong_command 27 | 28 | command = data.split() 29 | 30 | if command[0] == 'put': 31 | if len(command) == 4: 32 | try: 33 | key, value, timestamp = command[1], float(command[2]), int(command[3]) 34 | except ValueError: 35 | return msg_wrong_command 36 | 37 | old_metrics_list = metrics_dict.get(key, []) 38 | for i, metric in enumerate(old_metrics_list): 39 | if metric[0] == timestamp: 40 | old_metrics_list.remove((timestamp, metric[1])) 41 | old_metrics_list.insert(i, (timestamp, value)) 42 | return msg_ok 43 | 44 | old_metrics_list.append((timestamp, value)) 45 | metrics_dict.update({key: old_metrics_list}) 46 | return msg_ok 47 | else: 48 | return msg_wrong_command 49 | 50 | elif command[0] == 'get': 51 | if len(command) == 2: 52 | send_metrics = 'ok\n' 53 | key = command[1] 54 | if key == '*': 55 | for key, item_list in metrics_dict.items(): 56 | item_list.sort() 57 | for item_tuple in item_list: 58 | try: 59 | send_metrics += f'{key} {item_tuple[1]} {item_tuple[0]}\n' 60 | except IndexError: 61 | return msg_index_error 62 | return send_metrics + '\n' 63 | 64 | metrics_list = metrics_dict.get(key, None) 65 | if metrics_list is None: 66 | return msg_ok 67 | 68 | try: 69 | metrics_list.sort() 70 | for metric in metrics_list: 71 | send_metrics += f'{key} {metric[1]} {metric[0]}\n' 72 | return send_metrics + '\n' 73 | except IndexError: 74 | return msg_index_error 75 | 76 | else: 77 | return msg_wrong_command 78 | 79 | else: 80 | return msg_wrong_command 81 | 82 | 83 | def run_server(host, port): 84 | loop = asyncio.get_event_loop() 85 | coro = loop.create_server(ClientServerProtocol, host, port) 86 | server = loop.run_until_complete(coro) 87 | 88 | try: 89 | loop.run_forever() 90 | except KeyboardInterrupt: 91 | pass 92 | 93 | server.close() 94 | loop.run_until_complete(server.wait_closed()) 95 | loop.close() 96 | 97 | 98 | if __name__ == '__main__': 99 | run_server('127.0.0.1', 8888) 100 | --------------------------------------------------------------------------------