├── .gitignore ├── 00-Introduction-to-Python ├── hw │ └── task1 │ │ └── is_permutation.py └── slides │ ├── Lection_1_Introduction.pdf │ └── Lection_1_Introduction.pptx ├── 01-Data-Structures ├── hw │ ├── carrots │ │ ├── files │ │ │ ├── dna.fasta │ │ │ └── rna_codon_table.txt │ │ └── homework_strings.py │ └── sticks │ │ ├── README.md │ │ ├── winedata_1.json │ │ └── winedata_2.json └── slides │ ├── data_structures.ipynb │ └── images │ ├── all.png │ ├── cat.jpg │ ├── datatypes.png │ ├── ekr.png │ ├── f.jpeg │ ├── frozen.gif │ ├── lenin.jpeg │ ├── me.jpeg │ ├── mutable.jpg │ ├── namespaces.png │ ├── numbers.png │ ├── pep8.jpg │ ├── pep_example.png │ ├── python_namespace.png │ ├── python_namespaces_code.jpg │ ├── python_namespaces_legb.jpg │ ├── r.png │ ├── recurse_meme.jpg │ ├── recursion.jpg │ ├── recursion.png │ ├── set_food.png │ ├── string.png │ ├── tuple.png │ ├── voprosi.jpg │ └── voprosiki.jpg ├── 02-Functions ├── hw │ ├── task1 │ │ └── hw1.py │ ├── task2 │ │ └── hw2.py │ ├── task3 │ │ └── hw3.py │ └── task4 │ │ └── hw4.py └── slides │ ├── file_descriptors.ipynb │ ├── files │ └── example.txt │ ├── functions.ipynb │ ├── images │ ├── life.png │ ├── namespaces.png │ ├── python_namespace.png │ ├── python_namespaces_code.jpg │ └── python_namespaces_legb.jpg │ └── test_print_vs_write.py ├── 03-fp-decorator ├── hw │ ├── task1 │ │ └── hw1.py │ ├── task2 │ │ └── hw2.py │ ├── task3 │ │ └── hw3.py │ ├── task4 │ │ └── hw4.py │ └── task5 │ │ └── hw5.py └── slides │ ├── Lambdas, maps.ipynb │ ├── images │ ├── Structure-of-scientific-revolutions-3rd-ed-pb.jpg │ ├── fp.png │ ├── func.png │ ├── prob.png │ ├── prolog.png │ ├── recurse_meme.jpg │ ├── recursion.jpg │ ├── recursion.png │ ├── reduce.png │ └── черч.jpeg │ └── recursion_decorators.ipynb ├── 04-OOP ├── hw │ ├── oop_1 │ │ └── oop_1.py │ └── save_original_info │ │ └── save_original_info.py └── slides │ └── OOP_1.pptx ├── 05-OOP_Exceptions ├── hw │ ├── counter │ │ └── counter.py │ └── oop_2 │ │ └── oop_2.py └── slides │ ├── Exceptions.pptx │ └── OOP_2.pptx ├── 06-advanced-python ├── advanced-python-1.ipynb ├── ap_1.html └── hw │ ├── task1.py │ ├── task2.py │ ├── task3.py │ └── task4.py ├── 07-python-for-advanced ├── ap2.ipynb └── hw │ ├── task1 │ └── task1.py │ ├── task2 │ └── task2.py │ └── task3 │ └── task3.py ├── 08-linux ├── Linux.pptx └── hw.txt ├── 09-networks-basics ├── Networks_tcp_ip_socket.pptx ├── dab.1.1.py ├── echo-server.py ├── hw1.txt ├── prefork.py ├── select.txt ├── tcp-client.py ├── tcp-echoclient.py ├── tcp-echoserver.py ├── udp-echoclient.py └── udp-echoserver.py ├── 10-HTTP ├── example1.py ├── example2.py ├── example3.py └── http_requests.pptx ├── 11-programming-and-debugging ├── 11_lecture.pdf └── hw │ ├── hw.py │ └── hw_module.so ├── 12-Data-Persistence ├── Data Engineering_Presentation_Introduction_Rasul Osmanov.pptx ├── PythonDataPersistence.pptx ├── app1.py ├── dump_json.py ├── dump_pickle.py └── hw.txt ├── 12-object-oriented-design ├── hw │ └── hw.pdf └── slides │ ├── 1_metaclass.ipynb │ ├── 2_inheritance.ipynb │ ├── 3_SOLID.ipynb │ ├── 4_composition.ipynb │ └── ood_lecture.pptx ├── 13-design-patterns ├── hw │ ├── 1-decorator │ │ ├── __init__.py │ │ └── decorator.py │ ├── 2-adapter │ │ ├── __init__.py │ │ ├── adapter.py │ │ └── documents │ │ │ ├── 1.xml │ │ │ ├── 2.xml │ │ │ └── 3.xml │ ├── 3-observer │ │ ├── __init__.py │ │ └── observer.py │ ├── 4-chain_of_responsibility │ │ ├── __init__.py │ │ └── chain_of_responsibility.py │ └── 5-abstract_factory │ │ ├── __init__.py │ │ ├── abstract_factory.py │ │ └── menu.yml └── slides │ ├── __init__.py │ ├── abstract_factory.py │ ├── abstract_factory_example.py │ ├── adapter.py │ ├── chain_of_responsibility.py │ ├── chain_of_responsibility_properties.py │ ├── decorator_coffee_shop.py │ ├── decorator_components.py │ ├── design_patterns_lections.pdf │ ├── factory_method.py │ ├── observer.py │ ├── singleton.py │ └── singleton_decorator.py ├── 14-data-persistence └── 20190517.PythonDataPersistence.pptx ├── 99-template-dir ├── README.md ├── hw │ ├── README.md │ └── task1 │ │ └── example.py └── slides │ └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,python,windows,pycharm+all,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=osx,python,windows,pycharm+all,visualstudiocode 4 | 5 | ### OSX ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### PyCharm+all ### 34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 35 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 36 | 37 | # User-specific stuff 38 | .idea/**/workspace.xml 39 | .idea/**/tasks.xml 40 | .idea/**/usage.statistics.xml 41 | .idea/**/dictionaries 42 | .idea/**/shelf 43 | 44 | # Generated files 45 | .idea/**/contentModel.xml 46 | 47 | # Sensitive or high-churn files 48 | .idea/**/dataSources/ 49 | .idea/**/dataSources.ids 50 | .idea/**/dataSources.local.xml 51 | .idea/**/sqlDataSources.xml 52 | .idea/**/dynamic.xml 53 | .idea/**/uiDesigner.xml 54 | .idea/**/dbnavigator.xml 55 | 56 | # Gradle 57 | .idea/**/gradle.xml 58 | .idea/**/libraries 59 | 60 | # Gradle and Maven with auto-import 61 | # When using Gradle or Maven with auto-import, you should exclude module files, 62 | # since they will be recreated, and may cause churn. Uncomment if using 63 | # auto-import. 64 | # .idea/modules.xml 65 | # .idea/*.iml 66 | # .idea/modules 67 | 68 | # CMake 69 | cmake-build-*/ 70 | 71 | # Mongo Explorer plugin 72 | .idea/**/mongoSettings.xml 73 | 74 | # File-based project format 75 | *.iws 76 | 77 | # IntelliJ 78 | out/ 79 | 80 | # mpeltonen/sbt-idea plugin 81 | .idea_modules/ 82 | 83 | # JIRA plugin 84 | atlassian-ide-plugin.xml 85 | 86 | # Cursive Clojure plugin 87 | .idea/replstate.xml 88 | 89 | # Crashlytics plugin (for Android Studio and IntelliJ) 90 | com_crashlytics_export_strings.xml 91 | crashlytics.properties 92 | crashlytics-build.properties 93 | fabric.properties 94 | 95 | # Editor-based Rest Client 96 | .idea/httpRequests 97 | 98 | # Android studio 3.1+ serialized cache file 99 | .idea/caches/build_file_checksums.ser 100 | 101 | # JetBrains templates 102 | **___jb_tmp___ 103 | 104 | ### PyCharm+all Patch ### 105 | # Ignores the whole .idea folder and all .iml files 106 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 107 | 108 | .idea/ 109 | 110 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 111 | 112 | *.iml 113 | modules.xml 114 | .idea/misc.xml 115 | *.ipr 116 | 117 | # Sonarlint plugin 118 | .idea/sonarlint 119 | 120 | ### Python ### 121 | # Byte-compiled / optimized / DLL files 122 | __pycache__/ 123 | *.py[cod] 124 | *$py.class 125 | 126 | # Distribution / packaging 127 | .Python 128 | build/ 129 | develop-eggs/ 130 | dist/ 131 | downloads/ 132 | eggs/ 133 | .eggs/ 134 | lib/ 135 | lib64/ 136 | parts/ 137 | sdist/ 138 | var/ 139 | wheels/ 140 | pip-wheel-metadata/ 141 | share/python-wheels/ 142 | *.egg-info/ 143 | .installed.cfg 144 | *.egg 145 | MANIFEST 146 | 147 | # PyInstaller 148 | # Usually these files are written by a python script from a template 149 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 150 | *.manifest 151 | *.spec 152 | 153 | # Installer logs 154 | pip-log.txt 155 | pip-delete-this-directory.txt 156 | 157 | # Unit test / coverage reports 158 | htmlcov/ 159 | .tox/ 160 | .nox/ 161 | .coverage 162 | .coverage.* 163 | .cache 164 | nosetests.xml 165 | coverage.xml 166 | *.cover 167 | .hypothesis/ 168 | .pytest_cache/ 169 | 170 | # Translations 171 | *.mo 172 | *.pot 173 | 174 | # Django stuff: 175 | *.log 176 | local_settings.py 177 | db.sqlite3 178 | 179 | # Flask stuff: 180 | instance/ 181 | .webassets-cache 182 | 183 | # Scrapy stuff: 184 | .scrapy 185 | 186 | # Sphinx documentation 187 | docs/_build/ 188 | 189 | # PyBuilder 190 | target/ 191 | 192 | # Jupyter Notebook 193 | .ipynb_checkpoints 194 | 195 | # IPython 196 | profile_default/ 197 | ipython_config.py 198 | 199 | # pyenv 200 | .python-version 201 | 202 | # pipenv 203 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 204 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 205 | # having no cross-platform support, pipenv may install dependencies that don’t work, or not 206 | # install all needed dependencies. 207 | #Pipfile.lock 208 | 209 | # celery beat schedule file 210 | celerybeat-schedule 211 | 212 | # SageMath parsed files 213 | *.sage.py 214 | 215 | # Environments 216 | .env 217 | .venv 218 | env/ 219 | venv/ 220 | ENV/ 221 | env.bak/ 222 | venv.bak/ 223 | 224 | # Spyder project settings 225 | .spyderproject 226 | .spyproject 227 | 228 | # Rope project settings 229 | .ropeproject 230 | 231 | # mkdocs documentation 232 | /site 233 | 234 | # mypy 235 | .mypy_cache/ 236 | .dmypy.json 237 | dmypy.json 238 | 239 | # Pyre type checker 240 | .pyre/ 241 | 242 | ### VisualStudioCode ### 243 | .vscode/* 244 | !.vscode/settings.json 245 | !.vscode/tasks.json 246 | !.vscode/launch.json 247 | !.vscode/extensions.json 248 | 249 | ### VisualStudioCode Patch ### 250 | # Ignore all local history of files 251 | .history 252 | 253 | ### Windows ### 254 | # Windows thumbnail cache files 255 | Thumbs.db 256 | ehthumbs.db 257 | ehthumbs_vista.db 258 | 259 | # Dump file 260 | *.stackdump 261 | 262 | # Folder config file 263 | [Dd]esktop.ini 264 | 265 | # Recycle Bin used on file shares 266 | $RECYCLE.BIN/ 267 | 268 | # Windows Installer files 269 | *.cab 270 | *.msi 271 | *.msix 272 | *.msm 273 | *.msp 274 | 275 | # Windows shortcuts 276 | *.lnk 277 | 278 | # End of https://www.gitignore.io/api/osx,python,windows,pycharm+all,visualstudiocode 279 | -------------------------------------------------------------------------------- /00-Introduction-to-Python/hw/task1/is_permutation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Реализуйте метод, определяющий, является ли одна строка 5 | перестановкой другой. Под перестановкой понимаем любое 6 | изменение порядка символов. Регистр учитывается, пробелы 7 | являются существенными. 8 | """ 9 | 10 | 11 | def is_permutation(a: str, b: str) -> bool: 12 | # Нужно проверить, являются ли строчки 'a' и 'b' перестановками 13 | return False 14 | 15 | 16 | assert is_permutation("baba", "abab") # True 17 | assert is_permutation("abbba", "abab") # False 18 | -------------------------------------------------------------------------------- /00-Introduction-to-Python/slides/Lection_1_Introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/00-Introduction-to-Python/slides/Lection_1_Introduction.pdf -------------------------------------------------------------------------------- /00-Introduction-to-Python/slides/Lection_1_Introduction.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/00-Introduction-to-Python/slides/Lection_1_Introduction.pptx -------------------------------------------------------------------------------- /01-Data-Structures/hw/carrots/files/dna.fasta: -------------------------------------------------------------------------------- 1 | >HSBGPG Human gene for bone gla protein (BGP) 2 | GGCAGATTCCCCCTAGACCCGCCCGCACCATGGTCAGGCATGCCCCTCCTCATCGCTGGGCACAGCCCAGAGGGT 3 | ATAAACAGTGCTGGAGGCTGGCGGGGCAGGCCAGCTGAGTCCTGAGCAGCAGCCCAGCGCAGCCACCGAGACACC 4 | ATGAGAGCCCTCACACTCCTCGCCCTATTGGCCCTGGCCGCACTTTGCATCGCTGGCCAGGCAGGTGAGTGCCCC 5 | CACCTCCCCTCAGGCCGCATTGCAGTGGGGGCTGAGAGGAGGAAGCACCATGGCCCACCTCTTCTCACCCCTTTG 6 | GCTGGCAGTCCCTTTGCAGTCTAACCACCTTGTTGCAGGCTCAATCCATTTGCCCCAGCTCTGCCCTTGCAGAGG 7 | GAGAGGAGGGAAGAGCAAGCTGCCCGAGACGCAGGGGAAGGAGGATGAGGGCCCTGGGGATGAGCTGGGGTGAAC 8 | CAGGCTCCCTTTCCTTTGCAGGTGCGAAGCCCAGCGGTGCAGAGTCCAGCAAAGGTGCAGGTATGAGGATGGACC 9 | TGATGGGTTCCTGGACCCTCCCCTCTCACCCTGGTCCCTCAGTCTCATTCCCCCACTCCTGCCACCTCCTGTCTG 10 | GCCATCAGGAAGGCCAGCCTGCTCCCCACCTGATCCTCCCAAACCCAGAGCCACCTGATGCCTGCCCCTCTGCTC 11 | CACAGCCTTTGTGTCCAAGCAGGAGGGCAGCGAGGTAGTGAAGAGACCCAGGCGCTACCTGTATCAATGGCTGGG 12 | GTGAGAGAAAAGGCAGAGCTGGGCCAAGGCCCTGCCTCTCCGGGATGGTCTGTGGGGGAGCTGCAGCAGGGAGTG 13 | GCCTCTCTGGGTTGTGGTGGGGGTACAGGCAGCCTGCCCTGGTGGGCACCCTGGAGCCCCATGTGTAGGGAGAGG 14 | AGGGATGGGCATTTTGCACGGGGGCTGATGCCACCACGTCGGGTGTCTCAGAGCCCCAGTCCCCTACCCGGATCC 15 | CCTGGAGCCCAGGAGGGAGGTGTGTGAGCTCAATCCGGACTGTGACGAGTTGGCTGACCACATCGGCTTTCAGGA 16 | GGCCTATCGGCGCTTCTACGGCCCGGTCTAGGGTGTCGCTCTGCTGGCCTGGCCGGCAACCCCAGTTCTGCTCCT 17 | CTCCAGGCACCCTTCTTTCCTCTTCCCCTTGCCCTTGCCCTGACCTCCCAGCCCTATGGATGTGGGGTCCCCATC 18 | ATCCCAGCTGCTCCCAAATAAACTCCAGAAG 19 | >HSGLTH1 Human theta 1-globin gene 20 | CCACTGCACTCACCGCACCCGGCCAATTTTTGTGTTTTTAGTAGAGACTAAATACCATATAGTGAACACCTAAGA 21 | CGGGGGGCCTTGGATCCAGGGCGATTCAGAGGGCCCCGGTCGGAGCTGTCGGAGATTGAGCGCGCGCGGTCCCGG 22 | GATCTCCGACGAGGCCCTGGACCCCCGGGCGGCGAAGCTGCGGCGCGGCGCCCCCTGGAGGCCGCGGGACCCCTG 23 | GCCGGTCCGCGCAGGCGCAGCGGGGTCGCAGGGCGCGGCGGGTTCCAGCGCGGGGATGGCGCTGTCCGCGGAGGA 24 | CCGGGCGCTGGTGCGCGCCCTGTGGAAGAAGCTGGGCAGCAACGTCGGCGTCTACACGACAGAGGCCCTGGAAAG 25 | GTGCGGCAGGCTGGGCGCCCCCGCCCCCAGGGGCCCTCCCTCCCCAAGCCCCCCGGACGCGCCTCACCCACGTTC 26 | CTCTCGCAGGACCTTCCTGGCTTTCCCCGCCACGAAGACCTACTTCTCCCACCTGGACCTGAGCCCCGGCTCCTC 27 | ACAAGTCAGAGCCCACGGCCAGAAGGTGGCGGACGCGCTGAGCCTCGCCGTGGAGCGCCTGGACGACCTACCCCA 28 | CGCGCTGTCCGCGCTGAGCCACCTGCACGCGTGCCAGCTGCGAGTGGACCCGGCCAGCTTCCAGGTGAGCGGCTG 29 | CCGTGCTGGGCCCCTGTCCCCGGGAGGGCCCCGGCGGGGTGGGTGCGGGGGGCGTGCGGGGCGGGTGCAGGCGAG 30 | TGAGCCTTGAGCGCTCGCCGCAGCTCCTGGGCCACTGCCTGCTGGTAACCCTCGCCCGGCACTACCCCGGAGACT 31 | TCAGCCCCGCGCTGCAGGCGTCGCTGGACAAGTTCCTGAGCCACGTTATCTCGGCGCTGGTTTCCGAGTACCGCT 32 | GAACTGTGGGTGGGTGGCCGCGGGATCCCCAGGCGACCTTCCCCGTGTTTGAGTAAAGCCTCTCCCAGGAGCAGC 33 | CTTCTTGCCGTGCTCTCTCGAGGTCAGGACGCGAGAGGAAGGCGC 34 | -------------------------------------------------------------------------------- /01-Data-Structures/hw/carrots/files/rna_codon_table.txt: -------------------------------------------------------------------------------- 1 | UUU F CUU L AUU I GUU V 2 | UUC F CUC L AUC I GUC V 3 | UUA L CUA L AUA I GUA V 4 | UUG L CUG L AUG M GUG V 5 | UCU S CCU P ACU T GCU A 6 | UCC S CCC P ACC T GCC A 7 | UCA S CCA P ACA T GCA A 8 | UCG S CCG P ACG T GCG A 9 | UAU Y CAU H AAU N GAU D 10 | UAC Y CAC H AAC N GAC D 11 | UAA Stop CAA Q AAA K GAA E 12 | UAG Stop CAG Q AAG K GAG E 13 | UGU C CGU R AGU S GGU G 14 | UGC C CGC R AGC S GGC G 15 | UGA Stop CGA R AGA R GGA G 16 | UGG W CGG R AGG R GGG G 17 | -------------------------------------------------------------------------------- /01-Data-Structures/hw/carrots/homework_strings.py: -------------------------------------------------------------------------------- 1 | """" 2 | 3 | Задание 1 4 | 5 | 0) Повторение понятий из биологии (ДНК, РНК, нуклеотид, протеин, кодон) 6 | 7 | 1) Построение статистики по входящим в последовательность ДНК нуклеотидам 8 | для каждого гена (например: [A - 46, C - 66, G - 23, T - 34]) 9 | 10 | 2) Перевод последовательности ДНК в РНК (окей, Гугл) 11 | 12 | 3) Перевод последовательности РНК в протеин* 13 | 14 | 15 | *В папке files вы найдете файл rna_codon_table.txt - 16 | в нем содержится таблица переводов кодонов РНК в аминокислоту, 17 | составляющую часть полипептидной цепи белка. 18 | 19 | 20 | Вход: файл dna.fasta с n-количеством генов 21 | 22 | Выход - 3 файла: 23 | - статистика по количеству нуклеотидов в ДНК 24 | - последовательность РНК для каждого гена 25 | - последовательность кодонов для каждого гена 26 | 27 | ** Если вы умеете в matplotlib/seaborn или еще что, 28 | welcome за дополнительными баллами за 29 | гистограммы по нуклеотидной статистике. 30 | (Не забудьте подписать оси) 31 | 32 | P.S. За незакрытый файловый дескриптор - караем штрафным дезе. 33 | 34 | """ 35 | 36 | # read the file dna.fasta 37 | dna = None 38 | 39 | 40 | def translate_from_dna_to_rna(dna): 41 | 42 | """your code here""" 43 | 44 | return rna 45 | 46 | 47 | def count_nucleotides(dna): 48 | 49 | """your code here""" 50 | 51 | return num_of_nucleotides 52 | 53 | 54 | def translate_rna_to_protein(rna): 55 | 56 | """your code here""" 57 | 58 | return protein 59 | -------------------------------------------------------------------------------- /01-Data-Structures/hw/sticks/README.md: -------------------------------------------------------------------------------- 1 | Подготовка: 2 | * [Посмотреть синтаксис json формата](https://www.quackit.com/json/tutorial/json_syntax.cfm) 3 | * Прочитать [PEP8](https://www.python.org/dev/peps/pep-0008/) 4 | 5 | Задание: 6 | 1. Реализовать простейший parser/dumper json 7 | 2. Смерджить два файла в один `winedata_full.json`, убрав, если они есть, дупликаты и отсортировать объекты в низходящем порядке по цене, в случае коллизий сортировать по сорту в лексикографическом порядке 8 | * табуляция на ваше усмотрение 9 | 3. Найти для сортов `Gew[üu]rztraminer, Riesling, Merlot, Madera, Tempranillo, Red Blend` следующую информацию: 10 | * `avarege_price` 11 | * `min_price` 12 | * `max_price` 13 | * `most_common_region` где больше всего вин этого сорта производят ? 14 | * `most_common_country` 15 | * `average_score` 16 | 17 | Найти: 18 | * `most_expensive_wine` в случае коллизий тут и далее делаем список. 19 | * `cheapest_wine` 20 | * `highest_score` 21 | * `lowest_score` 22 | * `most_expensive_coutry` в среднем самое дорогое вино среди стран 23 | * `cheapest_coutry` в среднем самое дешевое вино среди стран 24 | * `most_rated_country` 25 | * `underrated_country` 26 | * `most_active_commentator` 27 | 28 | Эти данные, именно с этими ключами сохранить в виде json - объекта в файл `stats.json` 29 | Объект вида: 30 | ``` 31 | {"statistics": { 32 | "wine": { 33 | "Riesling": { ... }, 34 | ... 35 | }, 36 | "most_expensive_wine": ..., 37 | ... 38 | } 39 | } 40 | 41 | 4. Оформить результаты из пункта `3` в виде красивого `markdown` файла 42 | 43 | 44 | Проверка: 45 | * оба файла - валидные json 46 | * статистика собрана верно 47 | * код написан согласно PEP8 (буду проверять чем-то вроде [этого](https://pypi.org/project/pep8/)) 48 | 49 | Дополнительно: 50 | * использованы наиболее подходящие функции, структуры данных и методы, но без фанатизма 51 | * самое быстрое решение - дополнительный балл 52 | * мерять буду так `bash time python3 script.py` 53 | 54 | Обратить внимание: 55 | * запрещается использовать какие-либо библиотеки/функции, типа `eval` для парсинга/дампа json. Все делаем руками. Исключение, встроенные функции для работы со строками, которые мы прошли на лекции. 56 | 57 | -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/all.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/cat.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/datatypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/datatypes.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/ekr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/ekr.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/f.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/f.jpeg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/frozen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/frozen.gif -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/lenin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/lenin.jpeg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/me.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/me.jpeg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/mutable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/mutable.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/namespaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/namespaces.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/numbers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/numbers.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/pep8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/pep8.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/pep_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/pep_example.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/python_namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/python_namespace.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/python_namespaces_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/python_namespaces_code.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/python_namespaces_legb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/python_namespaces_legb.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/r.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/recurse_meme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/recurse_meme.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/recursion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/recursion.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/recursion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/recursion.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/set_food.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/set_food.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/string.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/tuple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/tuple.png -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/voprosi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/voprosi.jpg -------------------------------------------------------------------------------- /01-Data-Structures/slides/images/voprosiki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/01-Data-Structures/slides/images/voprosiki.jpg -------------------------------------------------------------------------------- /02-Functions/hw/task1/hw1.py: -------------------------------------------------------------------------------- 1 | """Некоторые встроенные функции в Python имеют нестандартное поведение, когда 2 | дело касается аргументов и их значений по умолчанию. 3 | Например, range, принимает от 1 до 3 аргументов, которые обычно называются 4 | start, stop и step и при использовании всех трех, должны указываться 5 | именно в таком порядке. При этом только у аргументов start и step есть значения 6 | по умолчанию (ноль и единица), а у stop его нет, но ведь аргументы без значения 7 | по умолчанию, то есть позиционные аргументы, должны указываться до именнованных, 8 | а stop указывается после start. Более того, при передаче функции только одного 9 | аргумента он интерпретируется как stop, а не start. 10 | Подумайте, каким образом, можно было бы добиться такого же поведения для 11 | какой-нибудь нашей пользовательской функции. 12 | Напишите функцию letters_range, которая ведет себя похожим на range образом, 13 | однако в качестве start и stop принимает не числа, а буквы латинского алфавита 14 | (в качестве step принимает целое число) и возвращает не перечисление чисел, а 15 | список букв, начиная с указанной в качестве start (либо начиная с 'a', 16 | если start не указан), до указанной в качестве stop с шагом step (по умолчанию 17 | равным 1). Добавить возможность принимать словарь с заменами букв для подобия траслитерации. 18 | Т.е. замена символов из оригинального алфавита другими, возможно несколькими символами. 19 | 20 | """ 21 | 22 | 23 | assert letters_range('b', 'w', 2) == ['b', 'd', 'f', 'h', 'j', 'l', 'n', 'p', 'r', 't', 'v'] 24 | assert letters_range('g') == ['a', 'b', 'c', 'd', 'e', 'f'] 25 | assert letters_range('g', 'p') == ['g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'] 26 | assert letters_range('g', 'p', **{'l': 7, 'o': 0}) == ['g', 'h', 'i', 'j', 'k', '7', 'm', 'n', '0'] 27 | assert letters_range('p', 'g', -2) == ['p', 'n', 'l', 'j', 'h'] 28 | assert letters_range('a') == [] -------------------------------------------------------------------------------- /02-Functions/hw/task2/hw2.py: -------------------------------------------------------------------------------- 1 | """Напишите реализацию функции atom, которая инкапсулирует некую переменную, 2 | предоставляя интерфейс для получения и изменения ее значения, 3 | таким образом, что это значение нельзя было бы получить или изменить 4 | иными способами. 5 | Пусть функция atom принимает один аргумент, инициализирующий хранимое значение 6 | (значение по умолчанию, в случае вызова atom без аргумента - None), 7 | а возвращает 3 функции - get_value, set_value, process_value, delete_value,такие, что: 8 | 9 | get_value - позволяет получить значение хранимой переменной; 10 | set_value - позволяет установить новое значение хранимой переменной, 11 | возвращает его; 12 | process_value - принимает в качестве аргументов сколько угодно функций 13 | и последовательно (в порядке перечисления аргументов) применяет эти функции 14 | к хранимой переменной, обновляя ее значение (перезаписывая получившийся 15 | результат) и возвращая получишееся итоговое значение. 16 | delete_value - удаляет значение 17 | """ 18 | 19 | get_x, set_x, process_x, delete_x = atom('Hello python') 20 | assert get_x() == 'Hello python' 21 | assert process_x() == 'Hello python' 22 | assert process_x(lambda x: x[::-1], ) == 'nohtyp olleH' 23 | assert set_x(10) == 10 24 | delete_x() 25 | assert not get_x() -------------------------------------------------------------------------------- /02-Functions/hw/task3/hw3.py: -------------------------------------------------------------------------------- 1 | """Напишите реализацию функции make_it_count, которая принимает в качестве 2 | аргументов некую функцию (обозначим ее func) и имя глобальной переменной 3 | (обозначим её counter_name), возвращая новую функцию, которая ведет себя 4 | в точности как функция func, за тем исключением, что всякий раз при вызове 5 | инкрементирует значение глобальной переменной с именем counter_name. 6 | """ 7 | 8 | -------------------------------------------------------------------------------- /02-Functions/hw/task4/hw4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Напишите функцию partial, которая принимает функцию (обозначим ее func), 3 | а также произвольный набор позиционных (назовем их fixated_args) и именованных 4 | (назовем их fixated_kwargs) аргументов и возвращет новую функцию, 5 | которая обладает следующими свойствами: 6 | 7 | 1.При вызове без аргументов повторяет поведение функции func, вызванной 8 | с fixated_args и fixated_kwargs. 9 | 2.При вызове с позиционными и именованными аргументами дополняет ими 10 | fixated_args (приписывает в конец списка fixated_args), и fixated_kwargs 11 | (приписывает новые именованные аргументы и переопределяет значения старых) 12 | и далее повторяет поведение func с этим новым набором аргументов. 13 | 3.Имеет __name__ вида partial_<имя функции func> 14 | 4.Имеет docstring вида: 15 | 16 | 17 | A func implementation of <имя функции func> 18 | with pre-applied arguments being: 19 | <перечисление имен и значений переданных fixated_args и fixated_kwargs; 20 | если ключевые аргументы переданны как позиционные - соотнести их с именами 21 | из function definition 22 | > 23 | source_code: 24 | ... 25 | 26 | Пояснение. 27 | partial - удобный способ получать новые функции, реализующие ограниченную 28 | или специфическую функциональность других функций. 29 | Например, мы хотим округлять числа с помощью функции round, но 30 | нас интересует округление всегда только до двух знаков после запятой, поэтому 31 | мы могли бы сделать так: 32 | round = partial(round, ndigits=2) 33 | И теперь round всегда округляет числа так, как нам надо и нам не нужно 34 | постоянно писать в коде выражения вроде round(n, ndigits=2). 35 | Конечно в Python уже есть реализация partial, однако в рамках курса, 36 | мы просим вас сделать собственную реализацию =). 37 | 38 | Для того, чтобы получить имена позиционных аргументов и исходный код, советую 39 | использовать возможности модуля inspect. 40 | 41 | Попробуйте применить эту функцию на написанных функциях из дз1, дз2, дз3. 42 | К функциям min, max, any() ? 43 | """ 44 | -------------------------------------------------------------------------------- /02-Functions/slides/files/example.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/files/example.txt -------------------------------------------------------------------------------- /02-Functions/slides/images/life.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/images/life.png -------------------------------------------------------------------------------- /02-Functions/slides/images/namespaces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/images/namespaces.png -------------------------------------------------------------------------------- /02-Functions/slides/images/python_namespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/images/python_namespace.png -------------------------------------------------------------------------------- /02-Functions/slides/images/python_namespaces_code.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/images/python_namespaces_code.jpg -------------------------------------------------------------------------------- /02-Functions/slides/images/python_namespaces_legb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/02-Functions/slides/images/python_namespaces_legb.jpg -------------------------------------------------------------------------------- /02-Functions/slides/test_print_vs_write.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | ARG = sys.argv[1] 6 | 7 | NAME = "test_" + ARG 8 | TEXT = "test_message" 9 | 10 | with open(NAME, "w") as f: 11 | if ARG == "print": 12 | for _ in range(10000000): 13 | print(TEXT, file=f) 14 | else: 15 | for _ in range(10000000): 16 | f.write(TEXT + "\n") 17 | f.flush() 18 | 19 | os.remove(NAME) 20 | 21 | # time python test_print_vs_write.py print 22 | # time python test_print_vs_write.py write 23 | -------------------------------------------------------------------------------- /03-fp-decorator/hw/task1/hw1.py: -------------------------------------------------------------------------------- 1 | """Решить несколько задач из projecteuler.net 2 | 3 | Ваши решения должны быть максимально лаконичными - используйте list comprehensions, reduce, etc. 4 | Все решения оформить в виде функций. 5 | 6 | problem9 - list comprehension : one line 7 | problem6 - list comprehension : one line 8 | problem48 - list comprehension : one line 9 | problem40 - list comprehension, reduce 10 | 11 | """ 12 | 13 | -------------------------------------------------------------------------------- /03-fp-decorator/hw/task2/hw2.py: -------------------------------------------------------------------------------- 1 | """ 2 | [Число Армстронга]( 3 | https://www.wikiwand.com/ru/%D0%A7%D0%B8%D1%81%D0%BB%D0%BE_%D0%90%D1%80%D0%BC%D1%81%D1%82%D1%80%D0%BE%D0%BD%D0%B3%D0%B0) 4 | — натуральное число, 5 | которое в данной системе счисления равно сумме своих цифр, 6 | возведённых в степень, равную количеству его цифр. 7 | 8 | К примеру: 9 | 10 | - 9 это число Армстронга, т.к. 9 = 9^1 = 9 11 | - 10 не является подобным числом: 10 != 1^2 + 0^2 = 1 12 | - 153 является числом Армстронга: 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153 13 | 14 | Задача 15 | 16 | Написать функцию определения числа Армстронга в функциональном виде: 17 | - использовать map или другие утилиты functools, 18 | - задействовать анонимные функций (или использовать функцию в качестве аргумента) 19 | - не использовать циклы, предпочитая list comprehensions 20 | 21 | ### Пример сигнатуры и вызовов функции 22 | """ 23 | def is_armstrong(number): 24 | ... 25 | 26 | assert is_armstrong(153) == True, 'Число Армстронга' 27 | assert is_armstrong(10) == False, 'Не число Армстронга' 28 | -------------------------------------------------------------------------------- /03-fp-decorator/hw/task3/hw3.py: -------------------------------------------------------------------------------- 1 | """# Гипотеза Коллатца 2 | 3 | [Гипотеза](https://www.wikiwand.com/ru/%D0%93%D0%B8%D0%BF%D0%BE%D1%82%D0%B5%D0%B7%D0%B0_%D0%9A%D0%BE%D0%BB%D0%BB%D0%B0%D1%82%D1%86%D0%B0) 4 | может быть кратко выражена следующим образом: 5 | 6 | - берём любое натуральное число n, если оно чётное, то делим его на 2 7 | - если нечётное, то умножаем на 3 и прибавляем 1 (получаем 3n + 1) 8 | - над полученным числом выполняем те же самые действия, и так далее 9 | 10 | Гипотеза Коллатца заключается в том, что какое бы начальное число n мы ни взяли, рано или поздно мы получим единицу. 11 | 12 | ### Пример 13 | 14 | Для числа 12: 15 | 16 | 12 17 | 6 18 | 3 19 | 10 20 | 5 21 | 16 22 | 8 23 | 4 24 | 2 25 | 1 26 | 27 | Всего получаем 9 шагов. 28 | 29 | ### Задача 30 | 31 | Вычислить число шагов для числа n, согласно гипотезе Коллатца необходимых для достижения этим числом единицы, используя функциональный стиль. По возможности не забыть проверить пограничные условия и входные данные. 32 | """ 33 | 34 | def collatz_steps(n): 35 | ... 36 | 37 | assert collatz_steps(16) == 4 38 | assert collatz_steps(12) == 9 39 | assert collatz_steps(1000000) == 152 40 | -------------------------------------------------------------------------------- /03-fp-decorator/hw/task4/hw4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Написать applydecorator который работает так, что избавляет нас от 3 | необходимости писать wrapper самим каждый раз. 4 | """ 5 | import sys 6 | from io import StringIO 7 | 8 | 9 | 10 | @applydecorator 11 | def saymyname(f, *args, **kwargs): 12 | print('Name is', f.__name__) 13 | return f(*args, **kwargs) 14 | 15 | # saymyname is now a decorator 16 | @saymyname 17 | def foo(*whatever): 18 | return whatever 19 | 20 | 21 | if __name__ == '__main__': 22 | sys.stdout = StringIO() 23 | foo(40, 2) 24 | assert sys.stdout.getvalue() == 'Name is foo\n' 25 | -------------------------------------------------------------------------------- /03-fp-decorator/hw/task5/hw5.py: -------------------------------------------------------------------------------- 1 | """Написать профилирующий декоратор. 2 | 3 | Написать функцию-декоратор, которая подсчитывает суммарное 4 | количество вызовов декорируемой функции и общее время, 5 | затраченное на исполнение этой функции по всем вызовам и 6 | сохраняет эти данные в глобальную переменную, имя которой 7 | передаётся декоратору. 8 | 9 | Проверить при помощи этой функции время исполнения различных 10 | алгоритмов поиска чисел фиббоначи. 11 | Найти наиболее оптимальный метод. 12 | """ -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/Structure-of-scientific-revolutions-3rd-ed-pb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/Structure-of-scientific-revolutions-3rd-ed-pb.jpg -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/fp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/fp.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/func.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/func.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/prob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/prob.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/prolog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/prolog.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/recurse_meme.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/recurse_meme.jpg -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/recursion.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/recursion.jpg -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/recursion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/recursion.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/reduce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/reduce.png -------------------------------------------------------------------------------- /03-fp-decorator/slides/images/черч.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/03-fp-decorator/slides/images/черч.jpeg -------------------------------------------------------------------------------- /04-OOP/hw/oop_1/oop_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Необходимо создать 3 класса и взаимосвязь между ними (Student, Teacher, 3 | Homework) 4 | Наследование в этой задаче использовать не нужно. 5 | Для работы с временем использовать модуль datetime 6 | 7 | 1. Homework принимает на вход 2 атрибута: текст задания и количество дней 8 | на это задание 9 | Атрибуты: 10 | text - текст задания 11 | deadline - хранит объект datetime.timedelta с количеством 12 | дней на выполнение 13 | created - c точной датой и временем создания 14 | Методы: 15 | is_active - проверяет не истело ли время на выполнение задания, 16 | возвращает boolean 17 | 18 | 2. Student 19 | Атрибуты: 20 | last_name 21 | first_name 22 | Методы: 23 | do_homework - принимает объект Homework и возвращает его же, 24 | если задание уже просрочено, то печатет 'You are late' и возвращает None 25 | 26 | 3. Teacher 27 | Атрибуты: 28 |  last_name 29 | first_name 30 | Методы: 31 | create_homework - текст задания и количество дней на это задание, 32 | возвращает экземпляр Homework 33 | Обратите внимание, что для работы этого метода не требуется сам объект. 34 | 35 | PEP8 соблюдать строго, проверку делаю автотестами и просмотром кода. 36 | Всем перечисленным выше атрибутам и методам классов сохранить названия. 37 | К названием остальных переменных, классов и тд. подходить ответственно - 38 | давать логичные подходящие имена. 39 | """ 40 | import datetime 41 | 42 | 43 | if __name__ == "__main__": 44 | teacher = Teacher("Daniil", "Shadrin") 45 | student = Student("Roman", "Petrov") 46 | teacher.last_name # Daniil 47 | student.first_name # Petrov 48 | 49 | expired_homework = teacher.create_homework("Learn functions", 0) 50 | expired_homework.created # Example: 2019-05-26 16:44:30.688762 51 | expired_homework.deadline # 0:00:00 52 | expired_homework.text # 'Learn functions' 53 | 54 | # create function from method and use it 55 | create_homework_too = teacher.create_homework 56 | oop_homework = create_homework_too("create 2 simple classes", 5) 57 | oop_homework.deadline # 5 days, 0:00:00 58 | 59 | student.do_homework(oop_homework) 60 | student.do_homework(expired_homework) # You are late 61 | -------------------------------------------------------------------------------- /04-OOP/hw/save_original_info/save_original_info.py: -------------------------------------------------------------------------------- 1 | """ 2 | Написать декоратор который позволит сохранять информацию из 3 | исходной функции (__name__ and __doc__), а так же сохранит саму 4 | исходную функцию в атрибуте __original_func 5 | 6 | print_result изменять нельзя, за исключением добавления вашего 7 | декоратора на строку отведенную под него - замените комментарий 8 | 9 | До применения вашего декоратор будет вызываться AttributeError при custom_sum.__original_func 10 | Это корректное поведение 11 | После применения там должна быть исходная функция 12 | 13 | Ожидаемый результат: 14 | print(custom_sum.__doc__) # 'This function can sum any objects which have __add___' 15 | print(custom_sum.__name__) # 'custom_sum' 16 | print(custom_sum.__original_func) # > 17 | """ 18 | 19 | import functools 20 | 21 | 22 | def print_result(func): 23 | # Place for new decorator 24 | def wrapper(*args, **kwargs): 25 | """Function-wrapper which print result of an original function""" 26 | result = func(*args, **kwargs) 27 | print(result) 28 | return result 29 | 30 | return wrapper 31 | 32 | 33 | @print_result 34 | def custom_sum(*args): 35 | """This function can sum any objects which have __add___""" 36 | return functools.reduce(lambda x, y: x + y, args) 37 | 38 | 39 | if __name__ == "__main__": 40 | custom_sum([1, 2, 3], [4, 5]) 41 | custom_sum(1, 2, 3, 4) 42 | 43 | print(custom_sum.__doc__) 44 | print(custom_sum.__name__) 45 | without_print = custom_sum.__original_func 46 | 47 | # the result returns without printing 48 | without_print(1, 2, 3, 4) 49 | -------------------------------------------------------------------------------- /04-OOP/slides/OOP_1.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/04-OOP/slides/OOP_1.pptx -------------------------------------------------------------------------------- /05-OOP_Exceptions/hw/counter/counter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Написать декоратор instances_counter, который применяется к любому классу 3 | и добавляет ему 2 метода: 4 | get_created_instances - возвращает количество созданых экземпляров класса 5 | reset_instances_counter - сбросить счетчик экземпляров, 6 | возвращает значение до сброса 7 | Имя декоратора и методов не менять 8 | 9 | Ниже пример использования 10 | """ 11 | 12 | 13 | def instances_counter(cls): 14 | """Some code""" 15 | return cls 16 | 17 | 18 | @instances_counter 19 | class User: 20 | pass 21 | 22 | 23 | if __name__ == "__main__": 24 | 25 | User.get_created_instances() # 0 26 | user, _, _ = User(), User(), User() 27 | user.get_created_instances() # 3 28 | user.reset_instances_counter() # 3 29 | -------------------------------------------------------------------------------- /05-OOP_Exceptions/hw/oop_2/oop_2.py: -------------------------------------------------------------------------------- 1 | """ 2 | В этом задании будем улучшать нашу систему классов из задания прошлой лекции 3 | (Student, Teacher, Homework) 4 | Советую обратить внимание на defaultdict из модуля collection для 5 | использования как общую переменную 6 | 7 | 8 | 1. Как то не правильно, что после do_homework мы возвращаем все тот же 9 | объект - будем возвращать какой-то результат работы (HomeworkResult) 10 | 11 | HomeworkResult принимает объект автора задания, принимает исходное задание 12 | и его решение в виде строки 13 | Атрибуты: 14 | homework - для объекта Homework, если передан не этот класс - выкинуть 15 | подходящие по смыслу исключение с сообщением: 16 | 'You gave a not Homework object' 17 | 18 | solution - хранит решение ДЗ как строку 19 | author - хранит объект Student 20 | created - c точной датой и временем создания 21 | 22 | 2. Если задание уже просрочено хотелось бы видеть исключение при do_homework, 23 | а не просто принт 'You are late'. 24 | Поднимайте исключение DeadlineError с сообщением 'You are late' вместо print. 25 | 26 | 3. Student и Teacher имеют одинаковые по смыслу атрибуты 27 | (last_name, first_name) - избавиться от дублирования с помощью наследования 28 | 29 | 4. 30 | Teacher 31 | Атрибут: 32 | homework_done - структура с интерфейсом как в словаря, сюда поподают все 33 | HomeworkResult после успешного прохождения check_homework 34 | (нужно гаранитровать остутствие повторяющихся результатов по каждому 35 | заданию), группировать по экземплярам Homework. 36 | Общий для всех учителей. Вариант ипользования смотри в блоке if __main__... 37 | Методы: 38 | check_homework - принимает экземпляр HomeworkResult и возвращает True если 39 | ответ студента больше 5 символов, так же при успешной проверке добавить в 40 | homework_done. 41 | Если меньше 5 символов - никуда не добавлять и вернуть False. 42 | 43 | reset_results - если передать экземпряр Homework - удаляет только 44 | результаты этого задания из homework_done, если ничего не передавать, 45 | то полностью обнулит homework_done. 46 | 47 | PEP8 соблюдать строго, проверку делаю автотестами и просмотром кода. 48 | Всем перечисленным выше атрибутам и методам классов сохранить названия. 49 | К названием остальных переменных, классов и тд. подходить ответственно - 50 | давать логичные подходящие имена. 51 | """ 52 | import datetime 53 | from collections import defaultdict 54 | 55 | 56 | if __name__ == "__main__": 57 | opp_teacher = Teacher("Daniil", "Shadrin") 58 | advanced_python_teacher = Teacher("Aleksandr", "Smetanin") 59 | 60 | lazy_student = Student("Roman", "Petrov") 61 | good_student = Student("Lev", "Sokolov") 62 | 63 | oop_hw = opp_teacher.create_homework("Learn OOP", 1) 64 | docs_hw = opp_teacher.create_homework("Read docs", 5) 65 | 66 | result_1 = good_student.do_homework(oop_hw, "I have done this hw") 67 | result_2 = good_student.do_homework(docs_hw, "I have done this hw too") 68 | result_3 = lazy_student.do_homework(docs_hw, "done") 69 | try: 70 | result_4 = HomeworkResult(good_student, "fff", "Solution") 71 | except Exception: 72 | print("There was an exception here") 73 | opp_teacher.check_homework(result_1) 74 | temp_1 = opp_teacher.homework_done 75 | 76 | advanced_python_teacher.check_homework(result_1) 77 | temp_2 = Teacher.homework_done 78 | assert temp_1 == temp_2 79 | 80 | opp_teacher.check_homework(result_2) 81 | opp_teacher.check_homework(result_3) 82 | 83 | print(Teacher.homework_done[oop_hw]) 84 | Teacher.reset_results() 85 | -------------------------------------------------------------------------------- /05-OOP_Exceptions/slides/Exceptions.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/05-OOP_Exceptions/slides/Exceptions.pptx -------------------------------------------------------------------------------- /05-OOP_Exceptions/slides/OOP_2.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/05-OOP_Exceptions/slides/OOP_2.pptx -------------------------------------------------------------------------------- /06-advanced-python/advanced-python-1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "slideshow": { 8 | "slide_type": "skip" 9 | } 10 | }, 11 | "outputs": [], 12 | "source": [ 13 | "import warnings\n", 14 | "\n", 15 | "\n", 16 | "warnings.filterwarnings('ignore')" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": { 22 | "slideshow": { 23 | "slide_type": "slide" 24 | } 25 | }, 26 | "source": [ 27 | "# Advanced Python" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": { 33 | "slideshow": { 34 | "slide_type": "slide" 35 | } 36 | }, 37 | "source": [ 38 | "## Python Data Model" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": 3, 44 | "metadata": {}, 45 | "outputs": [ 46 | { 47 | "name": "stdout", 48 | "output_type": "stream", 49 | "text": [ 50 | "Object id: 140713537457360\n", 51 | "Object type: \n", 52 | "Object value: NotImplemented\n", 53 | "__class__ __delattr__ __dir__ __doc__ __eq__\n", 54 | "__format__ __ge__ __getattribute__ __gt__ __hash__\n", 55 | "__init__ __init_subclass__ __le__ __lt__ __ne__\n", 56 | "__new__ __reduce__ __reduce_ex__ __repr__ __setattr__\n", 57 | "__sizeof__ __str__ __subclasshook__\n" 58 | ] 59 | } 60 | ], 61 | "source": [ 62 | "obj = NotImplemented\n", 63 | "print(f\"Object id: {id(obj)}\")\n", 64 | "print(f\"Object type: {type(obj)}\")\n", 65 | "print(f\"Object value: {obj}\") # actually a string representation of the object\n", 66 | "cols = 5\n", 67 | "print(\"\\n\".join([\" \".join(method for method in dir(obj)[line*cols: (line+1)*cols]) for line in range(len(dir(obj))//cols + 1)])) # do not do that at home!" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": { 73 | "slideshow": { 74 | "slide_type": "subslide" 75 | } 76 | }, 77 | "source": [ 78 | "### Special (magic, dunder) methods\n", 79 | "\n", 80 | "- Implement certain operations that are invoked by special syntax or function\n", 81 | "- Python’s approach to operator overloading\n", 82 | "- Setting to `None` indicates that the corresponding operation is not available\n", 83 | "\n", 84 | "Examples:\n", 85 | "- `__new__`- called to create a new instance of a class\n", 86 | "- `__init__` - called after the instance has been created, but before returning\n", 87 | "- `__del__` - called when the instance is about to be deleted" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": { 93 | "slideshow": { 94 | "slide_type": "subslide" 95 | } 96 | }, 97 | "source": [ 98 | "### `__str__` and `__repr__`" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": 4, 104 | "metadata": {}, 105 | "outputs": [ 106 | { 107 | "data": { 108 | "text/plain": [ 109 | "('I am a string.', \"'I am a string.'\")" 110 | ] 111 | }, 112 | "execution_count": 4, 113 | "metadata": {}, 114 | "output_type": "execute_result" 115 | } 116 | ], 117 | "source": [ 118 | "s = \"I am a string.\"\n", 119 | "s_str = str(s)\n", 120 | "s_repr = repr(s)\n", 121 | "s_str, s_repr" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": { 127 | "slideshow": { 128 | "slide_type": "fragment" 129 | } 130 | }, 131 | "source": [ 132 | "If at all possible, `__repr__` should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment)." 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 5, 138 | "metadata": { 139 | "slideshow": { 140 | "slide_type": "subslide" 141 | } 142 | }, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "repr: I am a string.\n" 149 | ] 150 | }, 151 | { 152 | "ename": "SyntaxError", 153 | "evalue": "invalid syntax (, line 1)", 154 | "output_type": "error", 155 | "traceback": [ 156 | "Traceback \u001b[1;36m(most recent call last)\u001b[0m:\n", 157 | " File \u001b[0;32m\"C:\\Users\\Artur_Paniukov\\AppData\\Local\\Continuum\\anaconda3\\lib\\site-packages\\IPython\\core\\interactiveshell.py\"\u001b[0m, line \u001b[0;32m3296\u001b[0m, in \u001b[0;35mrun_code\u001b[0m\n exec(code_obj, self.user_global_ns, self.user_ns)\n", 158 | "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m2\u001b[1;36m, in \u001b[1;35m\u001b[1;36m\u001b[0m\n\u001b[1;33m print(f\"str: {eval(s_str)}\")\u001b[0m\n", 159 | "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m I am a string.\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "print(f\"repr: {eval(s_repr)}\")\n", 165 | "print(f\"str: {eval(s_str)}\")" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": { 171 | "slideshow": { 172 | "slide_type": "subslide" 173 | } 174 | }, 175 | "source": [ 176 | "### Ordering\n", 177 | "\n", 178 | "- `__eq__` $\\Longleftrightarrow$ `==`\n", 179 | "- `__ne__` $\\Longleftrightarrow$ `!=`\n", 180 | "- `__lt__` $\\Longleftrightarrow$ `<`\n", 181 | "- `__gt__` $\\Longleftrightarrow$ `>`\n", 182 | "- `__le__` $\\Longleftrightarrow$ `<=`\n", 183 | "- `__ge__` $\\Longleftrightarrow$ `>=`\n", 184 | "\n", 185 | "### Arithmetic Operations\n", 186 | "- `__add__(self, other)` $\\Longleftrightarrow$ `self + other`\n", 187 | "- `__radd__(self, other)` $\\Longleftrightarrow$ `other + self`\n", 188 | "- `__iadd__(self, other)` $\\Longleftrightarrow$ `self += other`\n", 189 | "```\n", 190 | "...and others\n", 191 | "```" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 6, 197 | "metadata": { 198 | "slideshow": { 199 | "slide_type": "subslide" 200 | } 201 | }, 202 | "outputs": [ 203 | { 204 | "data": { 205 | "text/plain": [ 206 | "(False, True, True, True, False)" 207 | ] 208 | }, 209 | "execution_count": 6, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "class OrderedComplex(complex): \n", 216 | " def __eq__(self, other):\n", 217 | " if isinstance(other, OrderedComplex):\n", 218 | " return (self * self.conjugate()).real == (other * other.conjugate()).real\n", 219 | " \n", 220 | " def __lt__(self, other):\n", 221 | " if isinstance(other, OrderedComplex):\n", 222 | " return (self * self.conjugate()).real < (other * other.conjugate()).real\n", 223 | " \n", 224 | " def __le__(self, other):\n", 225 | " if isinstance(other, OrderedComplex):\n", 226 | " return (self * self.conjugate()).real <= (other * other.conjugate()).real\n", 227 | "\n", 228 | "\n", 229 | "a = OrderedComplex(1 + 2j)\n", 230 | "b = OrderedComplex(2 + 1j)\n", 231 | "\n", 232 | "a < b, a <= b, a == b, a >=b, a > b" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 7, 238 | "metadata": { 239 | "slideshow": { 240 | "slide_type": "subslide" 241 | } 242 | }, 243 | "outputs": [], 244 | "source": [ 245 | "import functools\n", 246 | "\n", 247 | "??functools.total_ordering" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": { 253 | "slideshow": { 254 | "slide_type": "subslide" 255 | } 256 | }, 257 | "source": [ 258 | "### Attribute access\n", 259 | "\n", 260 | "- `__getattr__` and `__getattribute__`\n", 261 | "- `__setattr__`\n", 262 | "- `__delattr__`" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "slideshow": { 269 | "slide_type": "fragment" 270 | } 271 | }, 272 | "source": [ 273 | "`__getattr__` called when the default attribute access fails with an `AttributeError`" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 8, 279 | "metadata": { 280 | "slideshow": { 281 | "slide_type": "subslide" 282 | } 283 | }, 284 | "outputs": [ 285 | { 286 | "name": "stdout", 287 | "output_type": "stream", 288 | "text": [ 289 | "weights: [[1, 2], [3, 4]]\n", 290 | "bias: [0, 1]\n" 291 | ] 292 | } 293 | ], 294 | "source": [ 295 | "class Layer:\n", 296 | " def __init__(self):\n", 297 | " self._params = {}\n", 298 | " \n", 299 | " def __setattr__(self, key, value):\n", 300 | " if not key.startswith(\"_\"):\n", 301 | " self._params[key]=value\n", 302 | " super().__setattr__(key, value)\n", 303 | " \n", 304 | " @property\n", 305 | " def parameters(self):\n", 306 | " return self._params\n", 307 | " \n", 308 | "\n", 309 | "class LinearLayer(Layer):\n", 310 | " def __init__(self, weights, bias):\n", 311 | " super().__init__()\n", 312 | " \n", 313 | " self.weights = weights\n", 314 | " self.bias = bias\n", 315 | " \n", 316 | " \n", 317 | "layer_1 = LinearLayer([[1, 2], [3, 4]], [0, 1])\n", 318 | "\n", 319 | "for key, value in layer_1.parameters.items():\n", 320 | " print(f\"{key}: {value}\")" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": { 326 | "slideshow": { 327 | "slide_type": "slide" 328 | } 329 | }, 330 | "source": [ 331 | "## Containers\n", 332 | "\n", 333 | "Containers usually are **sequences** (such as lists or tuples) or **mappings** (like dictionaries), but can represent other containers as well.\n", 334 | "\n", 335 | "- `__len__`\n", 336 | "```\n", 337 | "CPython implementation detail: In CPython, the length is required to be at most sys.maxsize.\n", 338 | "```\n", 339 | "- `__length_hint__` # called by `operator.length_hint(obj, default=0)`\n", 340 | "```\n", 341 | "Return an estimated length for the object o. First try to return its actual length, then an estimate using object.__length_hint__(), and finally return the default value.\n", 342 | "```" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": 9, 348 | "metadata": { 349 | "slideshow": { 350 | "slide_type": "subslide" 351 | } 352 | }, 353 | "outputs": [ 354 | { 355 | "name": "stdout", 356 | "output_type": "stream", 357 | "text": [ 358 | "9223372036854775807, True\n" 359 | ] 360 | }, 361 | { 362 | "ename": "OverflowError", 363 | "evalue": "Python int too large to convert to C ssize_t", 364 | "output_type": "error", 365 | "traceback": [ 366 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 367 | "\u001b[1;31mOverflowError\u001b[0m Traceback (most recent call last)", 368 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34mf\"{sys.maxsize}, {sys.maxsize == 2**63 - 1}\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m**\u001b[0m\u001b[1;36m63\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 369 | "\u001b[1;31mOverflowError\u001b[0m: Python int too large to convert to C ssize_t" 370 | ] 371 | } 372 | ], 373 | "source": [ 374 | "import sys\n", 375 | "\n", 376 | "\n", 377 | "print(f\"{sys.maxsize}, {sys.maxsize == 2**63 - 1}\")\n", 378 | "\n", 379 | "len(range(2**63))" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": { 385 | "slideshow": { 386 | "slide_type": "subslide" 387 | } 388 | }, 389 | "source": [ 390 | "## Containers\n", 391 | "\n", 392 | "- `__getitem__`, `__setitem__`, `__delitem__`, `__missing__`\n", 393 | "\n", 394 | "Implements `self[key]`\n", 395 | "\n", 396 | "- `__iter__` and `__reversed__`\n", 397 | "\n", 398 | "If the `__reversed__` method is not provided, the reversed() built-in will fall back to using the sequence protocol (`__len__` and `__getitem__`)\n", 399 | "\n", 400 | "- `__contains__(self, item)` $\\Longleftrightarrow$ `item in self`\n", 401 | "\n", 402 | "How to create an immutable container?" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "metadata": { 408 | "slideshow": { 409 | "slide_type": "fragment" 410 | } 411 | }, 412 | "source": [ 413 | "```\n", 414 | "__len__ + __getitem__\n", 415 | "```" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": 10, 421 | "metadata": { 422 | "slideshow": { 423 | "slide_type": "subslide" 424 | } 425 | }, 426 | "outputs": [], 427 | "source": [ 428 | "class ListContainer():\n", 429 | " def __init__(self, items): self.items = self.listify(items)\n", 430 | " def __bool__(self): return bool(self.items)\n", 431 | " def __getitem__(self, idx):\n", 432 | " if isinstance(idx, (int,slice)): return self.items[idx]\n", 433 | " if isinstance(idx[0], bool):\n", 434 | " assert len(idx)==len(self) # bool mask\n", 435 | " return [obj for include, obj in zip(idx, self.items) if include]\n", 436 | " return [self.items[i] for i in idx]\n", 437 | " def __len__(self): return len(self.items)\n", 438 | " def __iter__(self): return iter(self.items)\n", 439 | " def __setitem__(self, i, obj): self.items[i] = obj\n", 440 | " def __delitem__(self, i): del(self.items[i])\n", 441 | " def __repr__(self):\n", 442 | " res = f'{self.__class__.__name__} ({len(self)} items)\\n{self.items[:10]}'\n", 443 | " if len(self)>10: res = res[:-1]+ '...]'\n", 444 | " return res\n", 445 | " \n", 446 | " @staticmethod\n", 447 | " def listify(obj):\n", 448 | " if obj is None: return []\n", 449 | " if isinstance(obj, list): return obj\n", 450 | " if isinstance(obj, (str, int, float, complex)): return [obj]\n", 451 | " if isinstance(obj, Iterable): return list(obj)" 452 | ] 453 | }, 454 | { 455 | "cell_type": "markdown", 456 | "metadata": { 457 | "slideshow": { 458 | "slide_type": "slide" 459 | } 460 | }, 461 | "source": [ 462 | "## Callable" 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "execution_count": 11, 468 | "metadata": {}, 469 | "outputs": [ 470 | { 471 | "data": { 472 | "text/plain": [ 473 | "(9, 22, 22)" 474 | ] 475 | }, 476 | "execution_count": 11, 477 | "metadata": {}, 478 | "output_type": "execute_result" 479 | } 480 | ], 481 | "source": [ 482 | "import random\n", 483 | "\n", 484 | "\n", 485 | "class Die:\n", 486 | " def __init__(self, num_of_faces=6):\n", 487 | " self.faces = range(1, num_of_faces+1)\n", 488 | " \n", 489 | " def __call__(self):\n", 490 | " return random.choice(self.faces)\n", 491 | " \n", 492 | " def __repr__(self):\n", 493 | " return f\"Die({len(self.faces)})\"\n", 494 | "\n", 495 | " \n", 496 | "d24 = Die(24)\n", 497 | "d24(), d24(), d24()" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": { 503 | "slideshow": { 504 | "slide_type": "slide" 505 | } 506 | }, 507 | "source": [ 508 | "## List of dice" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": 12, 514 | "metadata": {}, 515 | "outputs": [], 516 | "source": [ 517 | "class Dice(ListContainer):\n", 518 | " def __init__(self, dice_list):\n", 519 | " super().__init__(dice_list)\n", 520 | " \n", 521 | " def __call__(self):\n", 522 | " return [die() for die in self]" 523 | ] 524 | }, 525 | { 526 | "cell_type": "code", 527 | "execution_count": 13, 528 | "metadata": {}, 529 | "outputs": [ 530 | { 531 | "name": "stdout", 532 | "output_type": "stream", 533 | "text": [ 534 | "Dice (3 items)\n", 535 | "[Die(6), Die(18), Die(24)]\n", 536 | "[True, True, False]\n", 537 | "[Die(6), Die(18)]\n" 538 | ] 539 | }, 540 | { 541 | "data": { 542 | "text/plain": [ 543 | "[4, 10, 18]" 544 | ] 545 | }, 546 | "execution_count": 13, 547 | "metadata": {}, 548 | "output_type": "execute_result" 549 | } 550 | ], 551 | "source": [ 552 | "d_pack = Dice([Die(6), Die(18), Die(24)])\n", 553 | "\n", 554 | "print(d_pack)\n", 555 | "bool_idx = [len(die.faces) <= 18 for die in d_pack]\n", 556 | "print(bool_idx)\n", 557 | "print(d_pack[bool_idx])\n", 558 | "d_pack()" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": { 564 | "slideshow": { 565 | "slide_type": "slide" 566 | } 567 | }, 568 | "source": [ 569 | "## Iterators and Iterable\n", 570 | "\n", 571 | "An object representing a stream of data.\n", 572 | "\n", 573 | "### Iterator protocol\n", 574 | "\n", 575 | "- `__iter__` - return the iterator object itself\n", 576 | "- `__next__` - return the next item from the iterator\n", 577 | "\n", 578 | "Return the next item from the container. If there are no further items, raise the `StopIteration` exception. Once an iterator’s `__next__` method raises `StopIteration`, it must continue to do so on subsequent calls.\n", 579 | "\n", 580 | "**Iterable** - object that returns an iterator.\n" 581 | ] 582 | }, 583 | { 584 | "cell_type": "markdown", 585 | "metadata": { 586 | "slideshow": { 587 | "slide_type": "fragment" 588 | } 589 | }, 590 | "source": [ 591 | "$$\n", 592 | "iterators \\supset iterable\n", 593 | "$$\n", 594 | "\n", 595 | "$$\n", 596 | "iterators \\subset iterable\n", 597 | "$$\n" 598 | ] 599 | }, 600 | { 601 | "cell_type": "code", 602 | "execution_count": 14, 603 | "metadata": { 604 | "slideshow": { 605 | "slide_type": "subslide" 606 | } 607 | }, 608 | "outputs": [], 609 | "source": [ 610 | "s = \"string\"\n", 611 | "s_iter = iter(s)\n", 612 | "\n", 613 | "# print(list(zip(s, s, s)))\n", 614 | "# print(list(zip(s_iter, s_iter, s_iter)))\n", 615 | "# print(list(zip(iter(s), iter(s), iter(s))))\n", 616 | "# print(type(s_iter))" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": 15, 622 | "metadata": { 623 | "slideshow": { 624 | "slide_type": "subslide" 625 | } 626 | }, 627 | "outputs": [], 628 | "source": [ 629 | "import collections\n", 630 | "\n", 631 | "\n", 632 | "class DiceIterator(collections.abc.Iterator):\n", 633 | " def __init__(self, dice, cursor=-1):\n", 634 | " self.dice = dice\n", 635 | " self._cursor = cursor\n", 636 | " \n", 637 | " def __next__(self):\n", 638 | " self._cursor += 1\n", 639 | " if self._cursor >= len(self.dice):\n", 640 | " raise StopIteration\n", 641 | " return self.dice[self._cursor]\n", 642 | " \n", 643 | "\n", 644 | "class DiceCollection(collections.abc.Iterable):\n", 645 | " def __init__(self, list_of_faces):\n", 646 | " self.dice = [Die(num_of_faces) for num_of_faces in list_of_faces]\n", 647 | " \n", 648 | " def __iter__(self):\n", 649 | " return DiceIterator(self.dice, -1)" 650 | ] 651 | }, 652 | { 653 | "cell_type": "code", 654 | "execution_count": 16, 655 | "metadata": { 656 | "slideshow": { 657 | "slide_type": "subslide" 658 | } 659 | }, 660 | "outputs": [ 661 | { 662 | "name": "stdout", 663 | "output_type": "stream", 664 | "text": [ 665 | "\n", 666 | "\n", 667 | "\n" 668 | ] 669 | } 670 | ], 671 | "source": [ 672 | "d_pack = DiceCollection([6, 8, 9, 4])\n", 673 | "\n", 674 | "print(type(d_pack))\n", 675 | "\n", 676 | "d_iter = iter(d_pack)\n", 677 | "print(type(d_iter))\n", 678 | "d_iter_2 = iter(d_iter)\n", 679 | "print(type(d_iter_2))" 680 | ] 681 | }, 682 | { 683 | "cell_type": "code", 684 | "execution_count": 17, 685 | "metadata": { 686 | "slideshow": { 687 | "slide_type": "fragment" 688 | } 689 | }, 690 | "outputs": [], 691 | "source": [ 692 | "??DiceIterator.__iter__" 693 | ] 694 | }, 695 | { 696 | "cell_type": "code", 697 | "execution_count": 18, 698 | "metadata": { 699 | "slideshow": { 700 | "slide_type": "fragment" 701 | } 702 | }, 703 | "outputs": [ 704 | { 705 | "name": "stdout", 706 | "output_type": "stream", 707 | "text": [ 708 | "2, 4, 6, 2, " 709 | ] 710 | } 711 | ], 712 | "source": [ 713 | "for die in DiceCollection([6, 8, 9, 4]):\n", 714 | " print(die(), end=\", \")\n", 715 | " " 716 | ] 717 | }, 718 | { 719 | "cell_type": "markdown", 720 | "metadata": { 721 | "slideshow": { 722 | "slide_type": "subslide" 723 | } 724 | }, 725 | "source": [ 726 | "## Iterators form `__getitem__`" 727 | ] 728 | }, 729 | { 730 | "cell_type": "code", 731 | "execution_count": 19, 732 | "metadata": {}, 733 | "outputs": [ 734 | { 735 | "ename": "TypeError", 736 | "evalue": "'HiddenList' object is not iterable", 737 | "output_type": "error", 738 | "traceback": [ 739 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 740 | "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", 741 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[0mh_list\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mHiddenList\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m \u001b[0miter\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mh_list\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 742 | "\u001b[1;31mTypeError\u001b[0m: 'HiddenList' object is not iterable" 743 | ] 744 | } 745 | ], 746 | "source": [ 747 | "class HiddenList:\n", 748 | " def __init__(self, lst):\n", 749 | " self.lst = lst\n", 750 | "\n", 751 | "\n", 752 | "h_list = HiddenList([1, 2, 3])\n", 753 | "iter(h_list)" 754 | ] 755 | }, 756 | { 757 | "cell_type": "code", 758 | "execution_count": 20, 759 | "metadata": { 760 | "slideshow": { 761 | "slide_type": "subslide" 762 | } 763 | }, 764 | "outputs": [ 765 | { 766 | "name": "stdout", 767 | "output_type": "stream", 768 | "text": [ 769 | "1\n", 770 | "2\n", 771 | "3\n" 772 | ] 773 | } 774 | ], 775 | "source": [ 776 | "class IterableHiddenList(HiddenList):\n", 777 | " def __getitem__(self, i):\n", 778 | " return self.lst[i]\n", 779 | " \n", 780 | "\n", 781 | "ih_list = IterableHiddenList([1, 2, 3])\n", 782 | "iter(ih_list)\n", 783 | "\n", 784 | "for i in ih_list:\n", 785 | " print(i)" 786 | ] 787 | }, 788 | { 789 | "cell_type": "markdown", 790 | "metadata": { 791 | "slideshow": { 792 | "slide_type": "slide" 793 | } 794 | }, 795 | "source": [ 796 | "## Generators\n", 797 | "\n", 798 | "Python’s generators provide a convenient way to implement the iterator protocol.\n", 799 | "\n", 800 | "Generator is a **function** which returns a _generator iterator_." 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": 21, 806 | "metadata": { 807 | "slideshow": { 808 | "slide_type": "fragment" 809 | } 810 | }, 811 | "outputs": [ 812 | { 813 | "name": "stdout", 814 | "output_type": "stream", 815 | "text": [ 816 | "\n", 817 | "\n", 818 | "Here!\n", 819 | "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074\n" 820 | ] 821 | } 822 | ], 823 | "source": [ 824 | "def fib():\n", 825 | " a, b = 0, 1\n", 826 | " print(\"Here!\")\n", 827 | " while True:\n", 828 | " yield b\n", 829 | " a, b = b, a + b\n", 830 | "\n", 831 | "print(type(fib))\n", 832 | "fib_gen = fib()\n", 833 | "print(type(fib_gen))\n", 834 | "\n", 835 | "for _, i in zip(range(50), fib_gen):\n", 836 | " print(i, end=\", \")\n", 837 | "print(next(fib_gen))" 838 | ] 839 | }, 840 | { 841 | "cell_type": "markdown", 842 | "metadata": { 843 | "slideshow": { 844 | "slide_type": "subslide" 845 | } 846 | }, 847 | "source": [ 848 | "## Generators\n", 849 | "\n", 850 | "A generator function can also contain return statements of the form: `return`\n", 851 | "\n", 852 | "Each `yield` temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator iterator resumes, it picks up where it left off (in contrast to functions which start fresh on every invocation)" 853 | ] 854 | }, 855 | { 856 | "cell_type": "code", 857 | "execution_count": 22, 858 | "metadata": { 859 | "slideshow": { 860 | "slide_type": "fragment" 861 | } 862 | }, 863 | "outputs": [ 864 | { 865 | "name": "stdout", 866 | "output_type": "stream", 867 | "text": [ 868 | "Do first, 1\n", 869 | "Do second, 2\n", 870 | "Do third, 4\n" 871 | ] 872 | } 873 | ], 874 | "source": [ 875 | "def do_in_order():\n", 876 | " x = 1\n", 877 | " print(f\"Do first, {x}\")\n", 878 | " yield\n", 879 | " x += 1\n", 880 | " print(f\"Do second, {x}\")\n", 881 | " yield\n", 882 | " x *= x\n", 883 | " print(f\"Do third, {x}\")\n", 884 | "\n", 885 | "gen = do_in_order()\n", 886 | "next(gen)\n", 887 | "next(gen)\n", 888 | "next(gen, None)" 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": { 894 | "slideshow": { 895 | "slide_type": "subslide" 896 | } 897 | }, 898 | "source": [ 899 | "### Send" 900 | ] 901 | }, 902 | { 903 | "cell_type": "code", 904 | "execution_count": 23, 905 | "metadata": { 906 | "slideshow": { 907 | "slide_type": "-" 908 | } 909 | }, 910 | "outputs": [ 911 | { 912 | "name": "stdout", 913 | "output_type": "stream", 914 | "text": [ 915 | "Do first, 1\n", 916 | "Do second, None\n", 917 | "Do third, None\n" 918 | ] 919 | } 920 | ], 921 | "source": [ 922 | "def do_in_order_2():\n", 923 | " x = 1\n", 924 | " print(f\"Do first, {x}\")\n", 925 | " x = yield\n", 926 | " print(f\"Do second, {x}\")\n", 927 | " x = yield 42\n", 928 | " print(f\"Do third, {x}\")\n", 929 | "\n", 930 | "gen = do_in_order_2()\n", 931 | "for _ in gen:\n", 932 | " pass" 933 | ] 934 | }, 935 | { 936 | "cell_type": "code", 937 | "execution_count": 24, 938 | "metadata": { 939 | "slideshow": { 940 | "slide_type": "fragment" 941 | } 942 | }, 943 | "outputs": [ 944 | { 945 | "name": "stdout", 946 | "output_type": "stream", 947 | "text": [ 948 | "Do first, 1\n" 949 | ] 950 | } 951 | ], 952 | "source": [ 953 | "gen = do_in_order_2()\n", 954 | "gen.send(None)" 955 | ] 956 | }, 957 | { 958 | "cell_type": "code", 959 | "execution_count": 25, 960 | "metadata": { 961 | "slideshow": { 962 | "slide_type": "fragment" 963 | } 964 | }, 965 | "outputs": [ 966 | { 967 | "name": "stdout", 968 | "output_type": "stream", 969 | "text": [ 970 | "Do second, I am in generator!\n", 971 | "42\n" 972 | ] 973 | } 974 | ], 975 | "source": [ 976 | "out = gen.send(\"I am in generator!\")\n", 977 | "print(out)" 978 | ] 979 | }, 980 | { 981 | "cell_type": "code", 982 | "execution_count": 26, 983 | "metadata": { 984 | "slideshow": { 985 | "slide_type": "fragment" 986 | } 987 | }, 988 | "outputs": [ 989 | { 990 | "name": "stdout", 991 | "output_type": "stream", 992 | "text": [ 993 | "Do third, lol\n", 994 | "I'm out!\n" 995 | ] 996 | } 997 | ], 998 | "source": [ 999 | "try:\n", 1000 | " out = gen.send(\"lol\")\n", 1001 | "except StopIteration:\n", 1002 | " print(\"I'm out!\")" 1003 | ] 1004 | }, 1005 | { 1006 | "cell_type": "markdown", 1007 | "metadata": { 1008 | "slideshow": { 1009 | "slide_type": "subslide" 1010 | } 1011 | }, 1012 | "source": [ 1013 | "### Throw" 1014 | ] 1015 | }, 1016 | { 1017 | "cell_type": "code", 1018 | "execution_count": 27, 1019 | "metadata": { 1020 | "slideshow": { 1021 | "slide_type": "-" 1022 | } 1023 | }, 1024 | "outputs": [ 1025 | { 1026 | "data": { 1027 | "text/plain": [ 1028 | "42" 1029 | ] 1030 | }, 1031 | "execution_count": 27, 1032 | "metadata": {}, 1033 | "output_type": "execute_result" 1034 | } 1035 | ], 1036 | "source": [ 1037 | "def g():\n", 1038 | " try:\n", 1039 | " yield 42\n", 1040 | " except Exception as e:\n", 1041 | " yield e\n", 1042 | "\n", 1043 | "gen = g()\n", 1044 | "next(gen)" 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "code", 1049 | "execution_count": 28, 1050 | "metadata": { 1051 | "slideshow": { 1052 | "slide_type": "fragment" 1053 | } 1054 | }, 1055 | "outputs": [ 1056 | { 1057 | "data": { 1058 | "text/plain": [ 1059 | "NotImplementedError('WTF?!')" 1060 | ] 1061 | }, 1062 | "execution_count": 28, 1063 | "metadata": {}, 1064 | "output_type": "execute_result" 1065 | } 1066 | ], 1067 | "source": [ 1068 | "gen.throw(NotImplementedError, \"WTF?!\")" 1069 | ] 1070 | }, 1071 | { 1072 | "cell_type": "code", 1073 | "execution_count": 29, 1074 | "metadata": { 1075 | "slideshow": { 1076 | "slide_type": "fragment" 1077 | } 1078 | }, 1079 | "outputs": [], 1080 | "source": [ 1081 | "# gen.throw(NotImplementedError, \"WTF?!\")" 1082 | ] 1083 | }, 1084 | { 1085 | "cell_type": "markdown", 1086 | "metadata": { 1087 | "slideshow": { 1088 | "slide_type": "subslide" 1089 | } 1090 | }, 1091 | "source": [ 1092 | "### Close" 1093 | ] 1094 | }, 1095 | { 1096 | "cell_type": "code", 1097 | "execution_count": 30, 1098 | "metadata": {}, 1099 | "outputs": [ 1100 | { 1101 | "name": "stdout", 1102 | "output_type": "stream", 1103 | "text": [ 1104 | "Do first, 1\n" 1105 | ] 1106 | } 1107 | ], 1108 | "source": [ 1109 | "gen = do_in_order_2()\n", 1110 | "\n", 1111 | "gen.send(None)" 1112 | ] 1113 | }, 1114 | { 1115 | "cell_type": "code", 1116 | "execution_count": 31, 1117 | "metadata": {}, 1118 | "outputs": [], 1119 | "source": [ 1120 | "gen.close()" 1121 | ] 1122 | }, 1123 | { 1124 | "cell_type": "code", 1125 | "execution_count": 32, 1126 | "metadata": { 1127 | "scrolled": true 1128 | }, 1129 | "outputs": [ 1130 | { 1131 | "ename": "StopIteration", 1132 | "evalue": "", 1133 | "output_type": "error", 1134 | "traceback": [ 1135 | "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", 1136 | "\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)", 1137 | "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mgen\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"Smth\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", 1138 | "\u001b[1;31mStopIteration\u001b[0m: " 1139 | ] 1140 | } 1141 | ], 1142 | "source": [ 1143 | "gen.send(\"Smth\")" 1144 | ] 1145 | }, 1146 | { 1147 | "cell_type": "markdown", 1148 | "metadata": { 1149 | "slideshow": { 1150 | "slide_type": "subslide" 1151 | } 1152 | }, 1153 | "source": [ 1154 | "## Coroutines (via generators)\n", 1155 | "\n", 1156 | "- generalization of subroutines\n", 1157 | "- can be entered, exited, and resumed at many different points\n", 1158 | "- from Python 3.7 can be implement with `async` and `await` syntax" 1159 | ] 1160 | }, 1161 | { 1162 | "cell_type": "code", 1163 | "execution_count": 33, 1164 | "metadata": { 1165 | "slideshow": { 1166 | "slide_type": "subslide" 1167 | } 1168 | }, 1169 | "outputs": [], 1170 | "source": [ 1171 | "def check_number(number):\n", 1172 | " print(f\"Ready for check {number}s\")\n", 1173 | " while True:\n", 1174 | " new_number = yield\n", 1175 | " if number == new_number:\n", 1176 | " print(f\"Got {number}!\")" 1177 | ] 1178 | }, 1179 | { 1180 | "cell_type": "code", 1181 | "execution_count": 34, 1182 | "metadata": { 1183 | "slideshow": { 1184 | "slide_type": "-" 1185 | } 1186 | }, 1187 | "outputs": [ 1188 | { 1189 | "name": "stdout", 1190 | "output_type": "stream", 1191 | "text": [ 1192 | "Ready for check 6s\n", 1193 | "Got 6!\n", 1194 | "Got 6!\n", 1195 | "Got 6!\n", 1196 | "Got 6!\n" 1197 | ] 1198 | } 1199 | ], 1200 | "source": [ 1201 | "is_six = check_number(6)\n", 1202 | "d6 = Die(6)\n", 1203 | "\n", 1204 | "# initialize the generator\n", 1205 | "is_six.send(None)\n", 1206 | "\n", 1207 | "for _ in range(30):\n", 1208 | " is_six.send(d6())" 1209 | ] 1210 | }, 1211 | { 1212 | "cell_type": "markdown", 1213 | "metadata": { 1214 | "slideshow": { 1215 | "slide_type": "subslide" 1216 | } 1217 | }, 1218 | "source": [ 1219 | "## yield from\n", 1220 | "\n", 1221 | "- pass the execution to another generator\n", 1222 | "- pass `send` and `throw`" 1223 | ] 1224 | }, 1225 | { 1226 | "cell_type": "code", 1227 | "execution_count": 35, 1228 | "metadata": {}, 1229 | "outputs": [], 1230 | "source": [ 1231 | "def chain(*iterables):\n", 1232 | " for iterable in iterables:\n", 1233 | " yield from iterable" 1234 | ] 1235 | }, 1236 | { 1237 | "cell_type": "code", 1238 | "execution_count": 36, 1239 | "metadata": {}, 1240 | "outputs": [ 1241 | { 1242 | "data": { 1243 | "text/plain": [ 1244 | "[1, 2, '4', '5', 'key1', 'key2', 'i', 't', 'e', 'r', ('b',), ('a',)]" 1245 | ] 1246 | }, 1247 | "execution_count": 36, 1248 | "metadata": {}, 1249 | "output_type": "execute_result" 1250 | } 1251 | ], 1252 | "source": [ 1253 | "i1, i2, i3, i4, i5 = (\n", 1254 | " [1, 2], (\"4\", \"5\"), {\"key1\": \"val1\", \"key2\": \"val2\"}, \"iter\", {(\"a\",), (\"b\",)}\n", 1255 | ")\n", 1256 | "\n", 1257 | "list(chain(i1, i2, i3, i4, i5))" 1258 | ] 1259 | }, 1260 | { 1261 | "cell_type": "markdown", 1262 | "metadata": { 1263 | "slideshow": { 1264 | "slide_type": "subslide" 1265 | } 1266 | }, 1267 | "source": [ 1268 | "## Context Manager\n", 1269 | "\n", 1270 | "- `__enter__(self)`\n", 1271 | "- `__exit__(self, exception_type, exception_value, traceback)`\n", 1272 | "\n", 1273 | "Patterns:\n", 1274 | "- acquisition/release of the resource\n", 1275 | "- doing something in different context" 1276 | ] 1277 | }, 1278 | { 1279 | "cell_type": "code", 1280 | "execution_count": 37, 1281 | "metadata": { 1282 | "slideshow": { 1283 | "slide_type": "subslide" 1284 | } 1285 | }, 1286 | "outputs": [ 1287 | { 1288 | "name": "stdout", 1289 | "output_type": "stream", 1290 | "text": [ 1291 | "C:\\Users\\Artur_Paniukov\\Documents\\Python\\lectures\\advanced_python\n", 1292 | "C:\\Users\\Artur_Paniukov\\Documents\\Python\\lectures\n" 1293 | ] 1294 | } 1295 | ], 1296 | "source": [ 1297 | "import os\n", 1298 | "\n", 1299 | "\n", 1300 | "class cd:\n", 1301 | " def __init__(self, path):\n", 1302 | " self.path = path\n", 1303 | " \n", 1304 | " def __enter__(self):\n", 1305 | " self.cwd = os.getcwd()\n", 1306 | " os.chdir(self.path)\n", 1307 | " \n", 1308 | " def __exit__(self, *args):\n", 1309 | " os.chdir(self.cwd)\n", 1310 | " \n", 1311 | "\n", 1312 | "print(os.getcwd())\n", 1313 | "with cd(\"..\"):\n", 1314 | " print(os.getcwd())" 1315 | ] 1316 | }, 1317 | { 1318 | "cell_type": "markdown", 1319 | "metadata": { 1320 | "slideshow": { 1321 | "slide_type": "subslide" 1322 | } 1323 | }, 1324 | "source": [ 1325 | "## Context Manager\n", 1326 | "\n", 1327 | "```\n", 1328 | "with resource_1() as r1, \\\n", 1329 | " resource_2() as r2:\n", 1330 | " \n", 1331 | " some_work()\n", 1332 | "```\n", 1333 | "\n", 1334 | "`__enter__` can return a resource.\n", 1335 | "\n", 1336 | "If an `__exit__` returns `True`, than an exception will be suppressed." 1337 | ] 1338 | }, 1339 | { 1340 | "cell_type": "markdown", 1341 | "metadata": { 1342 | "slideshow": { 1343 | "slide_type": "subslide" 1344 | } 1345 | }, 1346 | "source": [ 1347 | "## Context Manager and Generators" 1348 | ] 1349 | }, 1350 | { 1351 | "cell_type": "code", 1352 | "execution_count": 38, 1353 | "metadata": {}, 1354 | "outputs": [ 1355 | { 1356 | "name": "stdout", 1357 | "output_type": "stream", 1358 | "text": [ 1359 | "C:\\Users\\Artur_Paniukov\\Documents\\Python\\lectures\\advanced_python\n", 1360 | "C:\\Users\\Artur_Paniukov\\Documents\\Python\\lectures\n" 1361 | ] 1362 | } 1363 | ], 1364 | "source": [ 1365 | "from contextlib import contextmanager\n", 1366 | "\n", 1367 | "\n", 1368 | "@contextmanager\n", 1369 | "def cd_gen(path):\n", 1370 | " cwd = os.getcwd()\n", 1371 | " try:\n", 1372 | " # <__enter__>\n", 1373 | " os.chdir(path)\n", 1374 | " # \n", 1375 | " yield\n", 1376 | " finally:\n", 1377 | " # <__exit__>\n", 1378 | " os.chdir(cwd)\n", 1379 | " # \n", 1380 | " \n", 1381 | "print(os.getcwd())\n", 1382 | "with cd_gen(\"..\"):\n", 1383 | " print(os.getcwd())" 1384 | ] 1385 | }, 1386 | { 1387 | "cell_type": "markdown", 1388 | "metadata": { 1389 | "slideshow": { 1390 | "slide_type": "slide" 1391 | } 1392 | }, 1393 | "source": [ 1394 | "# Bonus" 1395 | ] 1396 | }, 1397 | { 1398 | "cell_type": "code", 1399 | "execution_count": 39, 1400 | "metadata": {}, 1401 | "outputs": [], 1402 | "source": [ 1403 | "def deco(*, func=None, **kwargs):\n", 1404 | " if not func:\n", 1405 | " return lambda f: deco(func=f, **kwargs)\n", 1406 | " \n", 1407 | " def inner(*args, **inner_kwargs):\n", 1408 | " print(kwargs)\n", 1409 | " return func(*args, **inner_kwargs)\n", 1410 | " return inner\n", 1411 | " " 1412 | ] 1413 | }, 1414 | { 1415 | "cell_type": "code", 1416 | "execution_count": 40, 1417 | "metadata": {}, 1418 | "outputs": [ 1419 | { 1420 | "name": "stdout", 1421 | "output_type": "stream", 1422 | "text": [ 1423 | "{'a': 1, 'b': 2}\n", 1424 | "1\n" 1425 | ] 1426 | } 1427 | ], 1428 | "source": [ 1429 | "@deco(a=1, b=2)\n", 1430 | "def f():\n", 1431 | " print(1)\n", 1432 | " \n", 1433 | "f()" 1434 | ] 1435 | } 1436 | ], 1437 | "metadata": { 1438 | "celltoolbar": "Slideshow", 1439 | "kernelspec": { 1440 | "display_name": "Python 3", 1441 | "language": "python", 1442 | "name": "python3" 1443 | }, 1444 | "language_info": { 1445 | "codemirror_mode": { 1446 | "name": "ipython", 1447 | "version": 3 1448 | }, 1449 | "file_extension": ".py", 1450 | "mimetype": "text/x-python", 1451 | "name": "python", 1452 | "nbconvert_exporter": "python", 1453 | "pygments_lexer": "ipython3", 1454 | "version": "3.7.3" 1455 | }, 1456 | "rise": { 1457 | "scroll": true 1458 | } 1459 | }, 1460 | "nbformat": 4, 1461 | "nbformat_minor": 2 1462 | } 1463 | -------------------------------------------------------------------------------- /06-advanced-python/hw/task1.py: -------------------------------------------------------------------------------- 1 | """ 2 | E - dict( : [, , ...]) 3 | Ключ - строка, идентифицирующая вершину графа 4 | значение - список вершин, достижимых из данной 5 | Сделать так, чтобы по графу можно было итерироваться(обходом в ширину) 6 | """ 7 | 8 | 9 | class Graph: 10 | def __init__(self, E): 11 | self.E = E 12 | 13 | 14 | E = {"A": ["B", "C", "D"], "B": ["C"], "C": [], "D": ["A"]} 15 | graph = Graph(E) 16 | 17 | for vertex in graph: 18 | print(vertex) 19 | -------------------------------------------------------------------------------- /06-advanced-python/hw/task2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Реализовать класс Quaternion, позволяющий работать с кватернионами 3 | https://ru.wikipedia.org/wiki/%D0%9A%D0%B2%D0%B0%D1%82%D0%B5%D1%80%D0%BD%D0%B8%D0%BE%D0%BD 4 | Функциональность (магическими методами): 5 | - сложение 6 | - умножение 7 | - деление 8 | - сравнение 9 | - нахождение модуля 10 | - строковое представление и repr 11 | По желанию: 12 | - взаимодействие с числами других типов 13 | """ 14 | 15 | 16 | class Quaternion: 17 | pass 18 | -------------------------------------------------------------------------------- /06-advanced-python/hw/task3.py: -------------------------------------------------------------------------------- 1 | """" 2 | Реализовать контекстный менеджер, который подавляет переданные исключения 3 | with Suppressor(ZeroDivisionError): 4 | 1/0 5 | print("It's fine") 6 | """ 7 | 8 | 9 | class Suppressor: 10 | pass 11 | -------------------------------------------------------------------------------- /06-advanced-python/hw/task4.py: -------------------------------------------------------------------------------- 1 | """ 2 | Реализовать метод __str__, позволяющий выводить все папки и файлы из данной, например так: 3 | > print(folder1) 4 | V folder1 5 | |-> V folder2 6 | | |-> V folder3 7 | | | |-> file3 8 | | |-> file2 9 | |-> file1 10 | А так же возможность проверить, находится ли файл или папка в другой папке: 11 | > print(file3 in folder2) 12 | True 13 | """ 14 | 15 | 16 | class PrintableFolder: 17 | def __init__(self, name, content): 18 | self.name = name 19 | self.content = content 20 | 21 | def __str__(self): 22 | pass 23 | 24 | 25 | class PrintableFile: 26 | def __init__(self, name): 27 | self.name = name 28 | 29 | def __str__(self): 30 | pass 31 | -------------------------------------------------------------------------------- /07-python-for-advanced/hw/task1/task1.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Реализовать такой метакласс, что экземпляры класса созданного с помощью него 4 | будут удовлетворять следующим требованиям: 5 | 6 | * объекты созданные с одинаковыми аттрибутами будут одним и тем же объектом 7 | * объекты созданные с разными аттрибутами будут разными объектами 8 | * у любого объекта есть мозможность получить доступ к другим объектам 9 | того же класса 10 | 11 | 12 | >>> unit1 = SiamObj('1', '2', a=1) 13 | >>> unit2 = SiamObj('1', '2', a=1) 14 | >>> unit1 is unit2 15 | True 16 | >>> unit3 = SiamObj('2', '2', a=1) 17 | >>> unit3.connect('1', '2', 1).a = 2 18 | >>> unit2.a == 2 19 | True 20 | >>> pool = unit3.pool 21 | >>> print(len(pool)) 22 | 2 23 | >>> del unit3 24 | >>> print(len(pool)) 25 | 1 26 | 27 | """ 28 | -------------------------------------------------------------------------------- /07-python-for-advanced/hw/task2/task2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Написать свое property c кэшем и таймаутом 3 | полностью повторяет поведение стандартной property за исключением: 4 | * хранит результат работы метода некоторое время, которое передается 5 | параметром в инициализацию проперти 6 | * пересчитывает значение, если таймер истек 7 | """ 8 | 9 | import time 10 | import uuid 11 | 12 | 13 | class Message: 14 | @timer_property(t=10) 15 | def msg(self): 16 | self._msg = self.get_message() 17 | return self._msg 18 | 19 | @msg.setter # reset timer also 20 | def msg(self, param): 21 | self._msg = param 22 | 23 | def get_message(self): 24 | """ 25 | Return random string 26 | """ 27 | return uuid.uuid4().get_hex() 28 | 29 | 30 | if __name__ == "__main__": 31 | m = Message() 32 | initial = m.msg 33 | assert initial is m.msg 34 | time.sleep(10) 35 | assert initial is not m.msg 36 | -------------------------------------------------------------------------------- /07-python-for-advanced/hw/task3/task3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Написать тесты(pytest or unittest) к предыдущим 2 заданиям, запустив которые, я бы смог бы проверить их корректность 3 | Обязательно проверить всю критическую функциональность 4 | """ 5 | -------------------------------------------------------------------------------- /08-linux/Linux.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/08-linux/Linux.pptx -------------------------------------------------------------------------------- /08-linux/hw.txt: -------------------------------------------------------------------------------- 1 | Read documentation about os, sys, shutil, psutil python modules. 2 | -------------------------------------------------------------------------------- /09-networks-basics/Networks_tcp_ip_socket.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/09-networks-basics/Networks_tcp_ip_socket.pptx -------------------------------------------------------------------------------- /09-networks-basics/dab.1.1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from socket import * 4 | 5 | s = socket(AF_INET, SOCK_STREAM) 6 | s.bind(("", 9000)) 7 | s.listen(5) 8 | while True: 9 | c, a = s.accept() 10 | print "Received connection from", a 11 | c.send("Hello %s\n" % a[0]) 12 | c.close() 13 | -------------------------------------------------------------------------------- /09-networks-basics/echo-server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import socket 4 | 5 | host = "" 6 | port = 50000 7 | backlog = 1 8 | size = 1024 9 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | s.bind((host, port)) 11 | s.listen(backlog) 12 | while 1: 13 | client, address = s.accept() 14 | data = client.recv(size) 15 | if data: 16 | client.send(data) 17 | client.close() 18 | -------------------------------------------------------------------------------- /09-networks-basics/hw1.txt: -------------------------------------------------------------------------------- 1 | TCP чат. 2 | Написать сервер и клиент для простого чата-мессенджера. 3 | 1. Возможность посмотреть список участников. 4 | 2. Возможность написать конкретному участнику. 5 | 3. Выйти из чата. 6 | 4. Отправить сообщение всем участникам. 7 | 8 | Задание творческое. Примените свою фантазию для ответа на вопрос, как это должно выглядеть. 9 | -------------------------------------------------------------------------------- /09-networks-basics/prefork.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Simple preforking echo server in Python. 4 | Python port of http://tomayko.com/writings/unicorn-is-unix. 5 | """ 6 | 7 | import os 8 | import sys 9 | import socket 10 | 11 | # Create a socket, bind it to localhost:4242, and start 12 | # listening. Runs once in the parent; all forked children 13 | # inherit the socket's file descriptor. 14 | acceptor = socket.socket() 15 | acceptor.bind(("localhost", 4242)) 16 | acceptor.listen(10) 17 | 18 | # Ryan's Ruby code here traps EXIT and closes the socket. This 19 | # isn't required in Python; the socket will be closed when the 20 | # socket object gets garbage collected. 21 | 22 | # Fork you some child processes. In the parent, the call to 23 | # fork returns immediately with the pid of the child process; 24 | # fork never returns in the child because we exit at the end 25 | # of the block. 26 | for i in range(3): 27 | pid = os.fork() 28 | 29 | # os.fork() returns 0 in the child process and the child's 30 | # process id in the parent. So if pid == 0 then we're in 31 | # the child process. 32 | if pid == 0: 33 | # now we're in the child process; trap (Ctrl-C) 34 | # interrupts by catching KeyboardInterrupt) and exit 35 | # immediately instead of dumping stack to stderr. 36 | childpid = os.getpid() 37 | print "Child %s listening on localhost:4242" % childpid 38 | try: 39 | while 1: 40 | # This is where the magic happens. accept(2) 41 | # blocks until a new connection is ready to be 42 | # dequeued. 43 | conn, addr = acceptor.accept() 44 | 45 | # For easier use, turn the socket connection 46 | # into a file-like object. 47 | flo = conn.makefile() 48 | flo.write("Child %s echo> " % childpid) 49 | flo.flush() 50 | message = flo.readline() 51 | flo.write(message) 52 | conn.close() 53 | print "Child %s echo'd: %r" % (childpid, message.strip()) 54 | except KeyboardInterrupt: 55 | sys.exit() 56 | 57 | # Sit back and wait for all child processes to exit. 58 | # 59 | # Trap interrupts, write a note, and exit immediately in 60 | # parent. This trap is not inherited by the forks because it 61 | # runs after forking has commenced. 62 | try: 63 | os.waitpid(-1, 0) 64 | except KeyboardInterrupt: 65 | print "\nbailing" 66 | sys.exit() 67 | -------------------------------------------------------------------------------- /09-networks-basics/select.txt: -------------------------------------------------------------------------------- 1 | https://gist.github.com/hduffddybz/daa4c931f6ea0bd7ed93#file-server-py 2 | -------------------------------------------------------------------------------- /09-networks-basics/tcp-client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from socket import * 4 | 5 | s = socket(AF_INET, SOCK_STREAM) 6 | s.connect(("httpbin.org", 80)) # Connect 7 | s.send("GET / HTTP/1.1\r\nHost: httpbin.org\r\n\r\n") # Send request 8 | data = s.recv(10000) # Get response 9 | s.close() 10 | print data 11 | 12 | # http://httpbin.org 13 | -------------------------------------------------------------------------------- /09-networks-basics/tcp-echoclient.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # simple tcp echo client 3 | 4 | import socket, sys 5 | 6 | c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | c.connect(("localhost", 5090)) 8 | while True: 9 | data = raw_input("Enter data to send to server: press q or Q to quit:\n") 10 | c.send(data) 11 | if data == "q" or data == "Q": 12 | c.close() 13 | break 14 | print c.recv(512) 15 | -------------------------------------------------------------------------------- /09-networks-basics/tcp-echoserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # A simple echo Server, send back the same message the client sent back to it. 3 | 4 | import socket, sys 5 | 6 | # Creat the socket 7 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | s.bind(("localhost", 5090)) 9 | s.listen(5) 10 | print "Server is running on port 5090:" 11 | while True: 12 | clientsocket, clientaddress = s.accept() 13 | print "Received the connection from:", clientaddress 14 | while True: 15 | data = clientsocket.recv(512) 16 | if data == "q" or data == "Q": 17 | clientsocket.close() 18 | print "Client quits:" 19 | break 20 | else: 21 | print "Data received", data 22 | newdata = "You Send:" + data 23 | clientsocket.send(newdata) 24 | -------------------------------------------------------------------------------- /09-networks-basics/udp-echoclient.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import socket 3 | 4 | client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 5 | while True: 6 | data = raw_input("Type Something(q or Q to exit:)") 7 | if data == "q" or data == "Q": 8 | client_socket.close() 9 | break 10 | else: 11 | client_socket.sendto(data, ("localhost", 5000)) 12 | newdata = client_socket.recvfrom(512) 13 | print "Received:", newdata[0] 14 | -------------------------------------------------------------------------------- /09-networks-basics/udp-echoserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # UDP echo Server 3 | import socket 4 | 5 | server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 6 | server_socket.bind(("localhost", 5000)) 7 | print "UDP -Echo Server listening on port 5000:" 8 | while True: 9 | data, address = server_socket.recvfrom(512) 10 | print address, ":said", data 11 | server_socket.sendto(data, address) 12 | -------------------------------------------------------------------------------- /10-HTTP/example1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 3 | 4 | PORT_NUMBER = 8080 5 | 6 | # This class will handles any incoming request from 7 | # the browser 8 | class myHandler(BaseHTTPRequestHandler): 9 | 10 | # Handler for the GET requests 11 | def do_GET(self): 12 | self.send_response(200) 13 | self.send_header("Content-type", "text/html") 14 | self.end_headers() 15 | # Send the html message 16 | self.wfile.write("Hello Helllo World !") 17 | return 18 | 19 | 20 | try: 21 | # Create a web server and define the handler to manage the 22 | # incoming request 23 | server = HTTPServer(("", PORT_NUMBER), myHandler) 24 | print "Started httpserver on port ", PORT_NUMBER 25 | 26 | # Wait forever for incoming htto requests 27 | server.serve_forever() 28 | 29 | except KeyboardInterrupt: 30 | print "^C received, shutting down the web server" 31 | server.socket.close() 32 | -------------------------------------------------------------------------------- /10-HTTP/example2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 3 | from os import curdir, sep 4 | 5 | PORT_NUMBER = 8080 6 | 7 | # This class will handles any incoming request from 8 | # the browser 9 | class myHandler(BaseHTTPRequestHandler): 10 | 11 | # Handler for the GET requests 12 | def do_GET(self): 13 | if self.path == "/": 14 | self.path = "/index_example2.html" 15 | 16 | try: 17 | # Check the file extension required and 18 | # set the right mime type 19 | 20 | sendReply = False 21 | if self.path.endswith(".html"): 22 | mimetype = "text/html" 23 | sendReply = True 24 | if self.path.endswith(".jpg"): 25 | mimetype = "image/jpg" 26 | sendReply = True 27 | if self.path.endswith(".gif"): 28 | mimetype = "image/gif" 29 | sendReply = True 30 | if self.path.endswith(".js"): 31 | mimetype = "application/javascript" 32 | sendReply = True 33 | if self.path.endswith(".css"): 34 | mimetype = "text/css" 35 | sendReply = True 36 | 37 | if sendReply == True: 38 | # Open the static file requested and send it 39 | f = open(curdir + sep + self.path) 40 | self.send_response(200) 41 | self.send_header("Content-type", mimetype) 42 | self.end_headers() 43 | self.wfile.write(f.read()) 44 | f.close() 45 | return 46 | 47 | except IOError: 48 | self.send_error(404, "File Not Found: %s" % self.path) 49 | 50 | 51 | try: 52 | # Create a web server and define the handler to manage the 53 | # incoming request 54 | server = HTTPServer(("", PORT_NUMBER), myHandler) 55 | print "Started httpserver on port ", PORT_NUMBER 56 | 57 | # Wait forever for incoming htto requests 58 | server.serve_forever() 59 | 60 | except KeyboardInterrupt: 61 | print "^C received, shutting down the web server" 62 | server.socket.close() 63 | -------------------------------------------------------------------------------- /10-HTTP/example3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 3 | from os import curdir, sep 4 | import cgi 5 | 6 | PORT_NUMBER = 8080 7 | 8 | # This class will handles any incoming request from 9 | # the browser 10 | class myHandler(BaseHTTPRequestHandler): 11 | 12 | # Handler for the GET requests 13 | def do_GET(self): 14 | if self.path == "/": 15 | self.path = "/index_example3.html" 16 | 17 | try: 18 | # Check the file extension required and 19 | # set the right mime type 20 | 21 | sendReply = False 22 | if self.path.endswith(".html"): 23 | mimetype = "text/html" 24 | sendReply = True 25 | if self.path.endswith(".jpg"): 26 | mimetype = "image/jpg" 27 | sendReply = True 28 | if self.path.endswith(".gif"): 29 | mimetype = "image/gif" 30 | sendReply = True 31 | if self.path.endswith(".js"): 32 | mimetype = "application/javascript" 33 | sendReply = True 34 | if self.path.endswith(".css"): 35 | mimetype = "text/css" 36 | sendReply = True 37 | 38 | if sendReply == True: 39 | # Open the static file requested and send it 40 | f = open(curdir + sep + self.path) 41 | self.send_response(200) 42 | self.send_header("Content-type", mimetype) 43 | self.end_headers() 44 | self.wfile.write(f.read()) 45 | f.close() 46 | return 47 | 48 | except IOError: 49 | self.send_error(404, "Your File Not Found: %s" % self.path) 50 | 51 | # Handler for the POST requests 52 | def do_POST(self): 53 | if self.path == "/send": 54 | form = cgi.FieldStorage( 55 | fp=self.rfile, 56 | headers=self.headers, 57 | environ={ 58 | "REQUEST_METHOD": "POST", 59 | "CONTENT_TYPE": self.headers["Content-Type"], 60 | }, 61 | ) 62 | 63 | print "Your name is: %s" % form["your_name"].value 64 | self.send_response(200) 65 | self.end_headers() 66 | self.wfile.write("Thanks %s !" % form["your_name"].value) 67 | return 68 | 69 | 70 | try: 71 | # Create a web server and define the handler to manage the 72 | # incoming request 73 | server = HTTPServer(("", PORT_NUMBER), myHandler) 74 | print "Started httpserver on port ", PORT_NUMBER 75 | 76 | # Wait forever for incoming htto requests 77 | server.serve_forever() 78 | 79 | except KeyboardInterrupt: 80 | print "^C received, shutting down the web server" 81 | server.socket.close() 82 | -------------------------------------------------------------------------------- /10-HTTP/http_requests.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/10-HTTP/http_requests.pptx -------------------------------------------------------------------------------- /11-programming-and-debugging/11_lecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/11-programming-and-debugging/11_lecture.pdf -------------------------------------------------------------------------------- /11-programming-and-debugging/hw/hw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import re 6 | import getpass 7 | import subprocess 8 | import socket 9 | import hashlib 10 | import uuid 11 | import base64 12 | 13 | MD5 = "f2b99b4214ca1d8a2a3bd8e25c98bd3d" 14 | 15 | exec( 16 | base64.b64decode( 17 | "ZGVmIGNoZWNrMCgpOgogICAgd2l0aCBvcGVuKHN5cy5hcmd2WzBdLCAncicpIGFzIGZpOgogICAgICAgIGRhdGEgPSBmaS5yZWFkbGluZXMoKVsxNDpdCiAgICAgICAgCiAgICAgICAgdGV4dCA9ICcnLmpvaW4oZGF0YSkKCiAgICAgICAgaCA9IGhhc2hsaWIubWQ1KCkKICAgICAgICBoLnVwZGF0ZSh0ZXh0LmVuY29kZSgpKQoKICAgICAgICBpZiBub3QgaC5oZXhkaWdlc3QoKSA9PSBNRDU6CiAgICAgICAgICAgIHByaW50KCdZb3UgY2Fubm90IG1vZGlmeSB0aGUgZmlsZScpCiAgICAgICAgICAgIHN5cy5leGl0KC02KQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIHByaW50KCdDaGVjayAwIHBhc3NlZCcpCgpkZWYgY2hlY2sxKG5hbWUpOiAgICAKICAgIGlmIG5hbWUgYW5kIGxlbihuYW1lKSA+IDQ6CiAgICAgICAgbmFtZSA9IG5hbWVbOjNdLnN0cmlwKCkudXBwZXIoKQogICAgZWxzZToKICAgICAgICBwcmludCgnSW5jb3JyZWN0IG5hbWUnKQogICAgICAgIHN5cy5leGl0KC0xKQogICAgcHJpbnQoJ0NoZWNrIDEgcGFzc2VkJykgICAgICAgICAgICAKCmRlZiBjaGVjazIobmFtZSk6CiAgICBpZiBub3QgKG5hbWUgaW4gb3MucGF0aC5hYnNwYXRoKHN5cy5hcmd2WzBdKS51cHBlcigpKToKICAgICAgICBwcmludCgnSW5jb3JyZWN0IG5hbWUnKQogICAgICAgIHN5cy5leGl0KC0yKQogICAgcHJpbnQoJ0NoZWNrIDIgcGFzc2VkJykgICAgICAgICAgICAKCgpkZWYgY2hlY2t2ZXIocHRoKToKICAgIGNoayA9IHN1YnByb2Nlc3MuY2hlY2tfb3V0cHV0KFtwdGhdLCBlbmNvZGluZz0nVVRGLTgnKQoKICAgIHYgPSByZS5maW5kYWxsKCdcZFwuXGRcZCcsIGNoay5zcGxpdCgnXG4nKVswXS5zdHJpcCgpKVswXQogICAgcmV0dXJuIHYgPT0gJzIuMjQnCgpkZWYgY2hlY2szKCk6CiAgICBnZXR0cmFjZSA9IGdldGF0dHIoc3lzLCAnZ2V0dHJhY2UnLCBOb25lKQogICAgaWYgZ2V0dHJhY2UgYW5kIGdldHRyYWNlKCkgb3Igb3MuZ2V0ZW52KCdBVVNFUicsICcnKToKICAgICAgICBwcmludCgnQ2hlYXRlcicpCiAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgb3MuZm9yaygpCiAgICBwcmludCgnQ2hlY2sgMyBwYXNzZWQnKQoKZGVmIGNoZWNrNCgpOgogICAgcCA9IG9zLnJlYWRsaW5rKCcvcHJvYy9zZWxmL2V4ZScpCgogICAgY2hrID0gc3VicHJvY2Vzcy5jaGVja19vdXRwdXQoWydsZGQnLCBwXSwgZW5jb2Rpbmc9J1VURi04JykKCiAgICBmb3IgcGxpbmUgaW4gY2hrLnNwbGl0KCdcbicpOgogICAgICAgIGlmICdsaWJjLnNvLjYnIGluIHBsaW5lOgogICAgICAgICAgICBsaW5lID0gcGxpbmUuc3RyaXAoKS5zcGxpdCgnICcpWzJdCiAgICAgICAgICAgIAogICAgICAgICAgICBpZiBub3QgY2hlY2t2ZXIobGluZSk6CiAgICAgICAgICAgICAgICBwcmludCgnSW5jb3JyZWN0IHN5c3RlbSB2ZXJzaW9uJykKICAgICAgICAgICAgICAgIHN5cy5leGl0KC00KQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcHJpbnQoJ0NoZWNrIDQgcGFzc2VkJykKICAgICAgICAgICAgICAgIHJldHVybgoKICAgIHByaW50KCdJbmNvcnJlY3Qgc3lzdGVtIHZlcnNpb24nKQogICAgc3lzLmV4aXQoLTQpCgpkZWYgY2hlY2s1KCk6CiAgICB0cnk6CiAgICAgICAgc29ja2V0LnNldGRlZmF1bHR0aW1lb3V0KDEpCiAgICAgICAgc29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCwgc29ja2V0LlNPQ0tfU1RSRUFNKS5jb25uZWN0KCgneWEucnUnLCA4MCkpCiAgICAgICAgcHJpbnQoJ0NoZWNrIDUgcGFzc2VkJykKCiAgICAgICAgcmV0dXJuIFRydWUKICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXg6CiAgICAgICAgcHJpbnQoJ0luY29ycmVjdCBpbmV0IHNldHRpbmdzJykKICAgICAgICBzeXMuZXhpdCgtNSkKCgpkZWYgY2hlY2s2KG5hbWUpOgogICAgaW1wb3J0IGh3X21vZHVsZQoKICAgIGlmIGh3X21vZHVsZS5kb193b3JrKCkgPiAwOgogICAgICAgIHdpdGggb3BlbignL3Zhci9mbGFnJywgJ3InKSBhcyBmaToKICAgICAgICAgICAgZGF0YSA9IGZpLnJlYWQoKS5zdHJpcCgnXHgwMCcpCgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICB1dWlkX29iaiA9IHV1aWQuVVVJRChkYXRhLCB2ZXJzaW9uPTQpCgogICAgICAgICAgICAgICAgcHJpbnQoZidZb3UgcGFzc2VkIGNoZWNrNiA6IHtuYW1lfS17ZGF0YX0nKQogICAgICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgICAgIGV4Y2VwdCBWYWx1ZUVycm9yOgogICAgICAgICAgICAgICAgcmFpc2UKICAgICAgICAgICAgICAgIHBhc3MKCiAgICBwcmludCgnWW91IGRpZG5cJ3QgY29tcGxldGUgNiB0YXNrJykKICAgIHN5cy5leGl0KC02KQoKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICBjaGVjazAoKQogICAgX25hbWUgPSBpbnB1dCgnRW50ZXIgeW91ciBuYW1lOlxuPiAnKS51cHBlcigpCiAgICBjaGVjazEoX25hbWUpCiAgICBjaGVjazIoX25hbWUpCiAgICBjaGVjazMoKQogICAgY2hlY2s0KCkKICAgIGNoZWNrNSgpCiAgICBjaGVjazYoX25hbWUp" 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /11-programming-and-debugging/hw/hw_module.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/11-programming-and-debugging/hw/hw_module.so -------------------------------------------------------------------------------- /12-Data-Persistence/Data Engineering_Presentation_Introduction_Rasul Osmanov.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/12-Data-Persistence/Data Engineering_Presentation_Introduction_Rasul Osmanov.pptx -------------------------------------------------------------------------------- /12-Data-Persistence/PythonDataPersistence.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/12-Data-Persistence/PythonDataPersistence.pptx -------------------------------------------------------------------------------- /12-Data-Persistence/app1.py: -------------------------------------------------------------------------------- 1 | import pathlib, subprocess 2 | 3 | p = pathlib.Path("qwe.txt") 4 | if p.exists(): 5 | p.unlink() 6 | 7 | with p.open("a") as f1: 8 | print("a" * 999, file=f1) 9 | print("b" * 999, file=f1) 10 | f1.flush() 11 | # append to the same file from another process 12 | subprocess.call("echo ccc >> qwe.txt", shell=True) 13 | 14 | with p.open() as f1: 15 | # we expect 11000 16 | print(f1.read().index("ccc")) 17 | -------------------------------------------------------------------------------- /12-Data-Persistence/dump_json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | 5 | data = {} 6 | data["people"] = [] 7 | data["people"].append( 8 | {"name": "Scott", "website": "stackabuse.com", "from": "Nebraska"} 9 | ) 10 | data["people"].append({"name": "Larry", "website": "google.com", "from": "Michigan"}) 11 | data["people"].append({"name": "Tim", "website": "apple.com", "from": "Alabama"}) 12 | 13 | with open("data.txt", "w") as outfile: 14 | json.dump(data, outfile) 15 | 16 | with open("data.txt", "rb") as infile: 17 | data2 = json.load(infile) 18 | 19 | print(data2) 20 | -------------------------------------------------------------------------------- /12-Data-Persistence/dump_pickle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pickle 4 | 5 | data = {} 6 | data["people"] = [] 7 | data["people"].append( 8 | {"name": "Scott", "website": "stackabuse.com", "from": "Nebraska"} 9 | ) 10 | data["people"].append({"name": "Larry", "website": "google.com", "from": "Michigan"}) 11 | data["people"].append({"name": "Tim", "website": "apple.com", "from": "Alabama"}) 12 | 13 | with open("data.txt", "wb") as outfile: 14 | pickle.dump(data, outfile) 15 | 16 | 17 | with open("data.txt", "rb") as infile: 18 | data2 = pickle.load(infile) 19 | 20 | print(data2) 21 | -------------------------------------------------------------------------------- /12-Data-Persistence/hw.txt: -------------------------------------------------------------------------------- 1 | Диспетчер хранилищ: 2 | Написать библиотеку для хранения данных в произвольных хранилищах. 3 | Поддержка минимум 2-3 хранилищ из списка: файлы, s3, gcs, redis, postgresql, mongo. 4 | Библиотеки драйверы для взаимодействия с хранилищами писать не надо, надо использовать доступные: file, boto3, pymongo, redis, etc. 5 | Функция для сохранения объекта должна получать на вход параметры: ссылку на объект (который будет сохранен), протокол сериализации (json или pickle), ссылку на объект (куда будет сохранен) в удобном виде. То же самое и для чтения объекта из хранилища. 6 | Код должен быть готов к добавлению новых хранилищ данных (hbase, azure blob storage, etc.) при наличии соответствующих библиотек драйверов. 7 | -------------------------------------------------------------------------------- /12-object-oriented-design/hw/hw.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/12-object-oriented-design/hw/hw.pdf -------------------------------------------------------------------------------- /12-object-oriented-design/slides/1_metaclass.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Metaclass" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "\n", 20 | "\n" 21 | ] 22 | } 23 | ], 24 | "source": [ 25 | "a = 5\n", 26 | "\n", 27 | "print(type(a))\n", 28 | "print(a.__class__)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/plain": [ 39 | "(object,)" 40 | ] 41 | }, 42 | "execution_count": 2, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "a.__class__.__bases__" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": {}, 55 | "outputs": [ 56 | { 57 | "data": { 58 | "text/plain": [ 59 | "()" 60 | ] 61 | }, 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "output_type": "execute_result" 65 | } 66 | ], 67 | "source": [ 68 | "object.__bases__" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 4, 74 | "metadata": {}, 75 | "outputs": [ 76 | { 77 | "data": { 78 | "text/plain": [ 79 | "type" 80 | ] 81 | }, 82 | "execution_count": 4, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "type(int)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 5, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "data": { 98 | "text/plain": [ 99 | "type" 100 | ] 101 | }, 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "output_type": "execute_result" 105 | } 106 | ], 107 | "source": [ 108 | "type(object)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 6, 114 | "metadata": {}, 115 | "outputs": [ 116 | { 117 | "data": { 118 | "text/plain": [ 119 | "(object,)" 120 | ] 121 | }, 122 | "execution_count": 6, 123 | "metadata": {}, 124 | "output_type": "execute_result" 125 | } 126 | ], 127 | "source": [ 128 | "type.__bases__" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 7, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "type" 140 | ] 141 | }, 142 | "execution_count": 7, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "type(type)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 8, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "\n", 161 | "<__main__.MyClass object at 0x105c22fd0>\n" 162 | ] 163 | } 164 | ], 165 | "source": [ 166 | "MyClass = type('MyClass', (), {})\n", 167 | "\n", 168 | "print(MyClass)\n", 169 | "print(MyClass())" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 12, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "Creating MySpecialClass\n" 182 | ] 183 | } 184 | ], 185 | "source": [ 186 | "class Meta(type):\n", 187 | " def __new__(cls, name, parents, attrs):\n", 188 | " print(f'Creating {name}')\n", 189 | " if 'class_id' not in attrs:\n", 190 | " attrs['class_id'] = name.lower()\n", 191 | " return super().__new__(cls, name, parents, attrs)\n", 192 | "\n", 193 | "\n", 194 | "class MySpecialClass(metaclass=Meta):\n", 195 | " pass" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 13, 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "data": { 205 | "text/plain": [ 206 | "mappingproxy({'__dict__': ,\n", 207 | " '__doc__': None,\n", 208 | " '__module__': '__main__',\n", 209 | " '__weakref__': ,\n", 210 | " 'class_id': 'myspecialclass'})" 211 | ] 212 | }, 213 | "execution_count": 13, 214 | "metadata": {}, 215 | "output_type": "execute_result" 216 | } 217 | ], 218 | "source": [ 219 | "MySpecialClass.__dict__" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 14, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "data": { 229 | "text/plain": [ 230 | "__main__.MySpecialClass" 231 | ] 232 | }, 233 | "execution_count": 14, 234 | "metadata": {}, 235 | "output_type": "execute_result" 236 | } 237 | ], 238 | "source": [ 239 | "msp = MySpecialClass()\n", 240 | "type(msp)" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 15, 246 | "metadata": {}, 247 | "outputs": [ 248 | { 249 | "data": { 250 | "text/plain": [ 251 | "__main__.Meta" 252 | ] 253 | }, 254 | "execution_count": 15, 255 | "metadata": {}, 256 | "output_type": "execute_result" 257 | } 258 | ], 259 | "source": [ 260 | "type(MySpecialClass)" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 16, 266 | "metadata": {}, 267 | "outputs": [ 268 | { 269 | "data": { 270 | "text/plain": [ 271 | "type" 272 | ] 273 | }, 274 | "execution_count": 16, 275 | "metadata": {}, 276 | "output_type": "execute_result" 277 | } 278 | ], 279 | "source": [ 280 | "type(Meta)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": { 287 | "collapsed": true 288 | }, 289 | "outputs": [], 290 | "source": [ 291 | "class Singleton(type):\n", 292 | " instance = None\n", 293 | " def __call__(cls, *args, **kw):\n", 294 | " if not cls.instance:\n", 295 | " cls.instance = super(Singleton, cls).__call__(*args, **kw)\n", 296 | " return cls.instance\n", 297 | " \n", 298 | "\n", 299 | "class ASingleton(metaclass=Singleton):\n", 300 | " pass" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": null, 306 | "metadata": { 307 | "collapsed": true 308 | }, 309 | "outputs": [], 310 | "source": [ 311 | "a = ASingleton()\n", 312 | "b = ASingleton()" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": { 319 | "collapsed": true 320 | }, 321 | "outputs": [], 322 | "source": [ 323 | "a is b" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "metadata": { 330 | "collapsed": true 331 | }, 332 | "outputs": [], 333 | "source": [ 334 | "print(hex(id(a)))\n", 335 | "print(hex(id(b)))" 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "### Abstract classes" 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": 17, 348 | "metadata": { 349 | "collapsed": true 350 | }, 351 | "outputs": [], 352 | "source": [ 353 | "from abc import ABCMeta, abstractmethod\n", 354 | "\n", 355 | "class Sender(metaclass=ABCMeta):\n", 356 | " @abstractmethod\n", 357 | " def send(self):\n", 358 | " pass" 359 | ] 360 | }, 361 | { 362 | "cell_type": "code", 363 | "execution_count": 18, 364 | "metadata": {}, 365 | "outputs": [ 366 | { 367 | "ename": "TypeError", 368 | "evalue": "Can't instantiate abstract class MessageSender with abstract methods send", 369 | "output_type": "error", 370 | "traceback": [ 371 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 372 | "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", 373 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0msender\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMessageSender\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 374 | "\u001b[0;31mTypeError\u001b[0m: Can't instantiate abstract class MessageSender with abstract methods send" 375 | ] 376 | } 377 | ], 378 | "source": [ 379 | "class MessageSender(Sender):\n", 380 | " pass\n", 381 | "\n", 382 | "\n", 383 | "sender = MessageSender()" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 19, 389 | "metadata": {}, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "Sending message\n" 396 | ] 397 | } 398 | ], 399 | "source": [ 400 | "class MessageSender(Sender):\n", 401 | " def send(self):\n", 402 | " print('Sending message')\n", 403 | " \n", 404 | "sender = MessageSender()\n", 405 | "sender.send()" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "metadata": { 412 | "collapsed": true 413 | }, 414 | "outputs": [], 415 | "source": [ 416 | "# Pythonic way\n", 417 | "\n", 418 | "class Sender:\n", 419 | " def send(self):\n", 420 | " raise NotImplementedError" 421 | ] 422 | } 423 | ], 424 | "metadata": { 425 | "kernelspec": { 426 | "display_name": "Python 3", 427 | "language": "python", 428 | "name": "python3" 429 | }, 430 | "language_info": { 431 | "codemirror_mode": { 432 | "name": "ipython", 433 | "version": 3 434 | }, 435 | "file_extension": ".py", 436 | "mimetype": "text/x-python", 437 | "name": "python", 438 | "nbconvert_exporter": "python", 439 | "pygments_lexer": "ipython3", 440 | "version": "3.6.2" 441 | } 442 | }, 443 | "nbformat": 4, 444 | "nbformat_minor": 2 445 | } 446 | -------------------------------------------------------------------------------- /12-object-oriented-design/slides/2_inheritance.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from collections import OrderedDict" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "class LoggingDict(dict):\n", 23 | " def __setitem__(self, key, value):\n", 24 | " print(f'Setting {key} to {value}')\n", 25 | " super().__setitem__(key, value)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 3, 31 | "metadata": {}, 32 | "outputs": [ 33 | { 34 | "name": "stdout", 35 | "output_type": "stream", 36 | "text": [ 37 | "Setting name to Alex\n", 38 | "Alex\n" 39 | ] 40 | } 41 | ], 42 | "source": [ 43 | "log_dict = LoggingDict()\n", 44 | "log_dict['name'] = 'Alex'\n", 45 | "print(log_dict['name'])" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 4, 51 | "metadata": { 52 | "collapsed": true 53 | }, 54 | "outputs": [], 55 | "source": [ 56 | "class LoggingOD(LoggingDict, OrderedDict):\n", 57 | " pass" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 5, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "[, , , , ]\n" 70 | ] 71 | } 72 | ], 73 | "source": [ 74 | "print(LoggingOD.mro())" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 6, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "name": "stdout", 84 | "output_type": "stream", 85 | "text": [ 86 | "Setting 1 to a\n", 87 | "Setting 2 to b\n", 88 | "LoggingOD([(1, 'a'), (2, 'b')])\n" 89 | ] 90 | } 91 | ], 92 | "source": [ 93 | "log_order_dict = LoggingOD()\n", 94 | "log_order_dict[1] = 'a'\n", 95 | "log_order_dict[2] = 'b'\n", 96 | "print(log_order_dict)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "## Matching arguments pattern" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 7, 109 | "metadata": { 110 | "collapsed": true 111 | }, 112 | "outputs": [], 113 | "source": [ 114 | "class Shape:\n", 115 | " def __init__(self, shapename, **kwargs):\n", 116 | " self.shapename = shapename\n", 117 | " super().__init__(**kwargs)\n", 118 | "\n", 119 | "\n", 120 | "class ColoredShape(Shape):\n", 121 | " def __init__(self, color, **kwargs):\n", 122 | " self.color = color\n", 123 | " super().__init__(**kwargs)" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": 8, 129 | "metadata": {}, 130 | "outputs": [ 131 | { 132 | "name": "stdout", 133 | "output_type": "stream", 134 | "text": [ 135 | "red circle\n" 136 | ] 137 | } 138 | ], 139 | "source": [ 140 | "cs = ColoredShape(color='red', shapename='circle')\n", 141 | "print(cs.color, cs.shapename)" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "## Root class" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 10, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "name": "stdout", 158 | "output_type": "stream", 159 | "text": [ 160 | "Drawing. Setting color to: blue\n", 161 | "Drawing. Setting shape to: square\n" 162 | ] 163 | }, 164 | { 165 | "ename": "AttributeError", 166 | "evalue": "'super' object has no attribute 'draw'", 167 | "output_type": "error", 168 | "traceback": [ 169 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 170 | "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", 171 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mcs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mColoredShape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'blue'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshapename\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'square'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mcs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 172 | "\u001b[0;32m\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Drawing. Setting color to:'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcolor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\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 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mcs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mColoredShape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'blue'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshapename\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'square'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 173 | "\u001b[0;32m\u001b[0m in \u001b[0;36mdraw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdraw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Drawing. Setting shape to:'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshapename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdraw\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 10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mColoredShape\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mShape\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 174 | "\u001b[0;31mAttributeError\u001b[0m: 'super' object has no attribute 'draw'" 175 | ] 176 | } 177 | ], 178 | "source": [ 179 | "class Root:\n", 180 | " def draw(self):\n", 181 | " # the delegation chain stops here\n", 182 | " assert not hasattr(super(), 'draw')\n", 183 | "\n", 184 | "class Shape(Root):\n", 185 | " def __init__(self, shapename, **kwds):\n", 186 | " self.shapename = shapename\n", 187 | " super().__init__(**kwds)\n", 188 | " def draw(self):\n", 189 | " print('Drawing. Setting shape to:', self.shapename)\n", 190 | " super().draw()\n", 191 | "\n", 192 | "class ColoredShape(Shape):\n", 193 | " def __init__(self, color, **kwds):\n", 194 | " self.color = color\n", 195 | " super().__init__(**kwds)\n", 196 | " def draw(self):\n", 197 | " print('Drawing. Setting color to:', self.color)\n", 198 | " super().draw()\n", 199 | "\n", 200 | "cs = ColoredShape(color='blue', shapename='square')\n", 201 | "cs.draw()" 202 | ] 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "## Incorporate non-cooperative class" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": { 215 | "collapsed": true 216 | }, 217 | "outputs": [], 218 | "source": [ 219 | "class Moveable:\n", 220 | " def __init__(self, x, y):\n", 221 | " self.x = x\n", 222 | " self.y = y\n", 223 | " def draw(self):\n", 224 | " print('Drawing at position:', self.x, self.y)\n", 225 | " \n", 226 | "class MoveableAdapter(Root):\n", 227 | " def __init__(self, x, y, **kwds):\n", 228 | " self.movable = Moveable(x, y)\n", 229 | " super().__init__(**kwds)\n", 230 | " def draw(self):\n", 231 | " self.movable.draw()\n", 232 | " super().draw()\n", 233 | " \n", 234 | "class MovableColoredShape(ColoredShape, MoveableAdapter):\n", 235 | " pass" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": { 242 | "collapsed": true 243 | }, 244 | "outputs": [], 245 | "source": [ 246 | "MovableColoredShape(color='red', shapename='triangle', x=10, y=20).draw()" 247 | ] 248 | } 249 | ], 250 | "metadata": { 251 | "kernelspec": { 252 | "display_name": "Python 3", 253 | "language": "python", 254 | "name": "python3" 255 | }, 256 | "language_info": { 257 | "codemirror_mode": { 258 | "name": "ipython", 259 | "version": 3 260 | }, 261 | "file_extension": ".py", 262 | "mimetype": "text/x-python", 263 | "name": "python", 264 | "nbconvert_exporter": "python", 265 | "pygments_lexer": "ipython3", 266 | "version": "3.6.2" 267 | } 268 | }, 269 | "nbformat": 4, 270 | "nbformat_minor": 2 271 | } 272 | -------------------------------------------------------------------------------- /12-object-oriented-design/slides/3_SOLID.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Single Responsibility Principle " 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "# wrong sample\n", 19 | "\n", 20 | "class User:\n", 21 | " def __init__(self, name: str):\n", 22 | " self.name = name\n", 23 | " \n", 24 | " def get_name(self) -> str:\n", 25 | " pass\n", 26 | "\n", 27 | " def save(self, user: User):\n", 28 | " pass" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": { 35 | "collapsed": true 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "# right sample\n", 40 | "\n", 41 | "class User:\n", 42 | " def __init__(self, name: str):\n", 43 | " self.name = name\n", 44 | " \n", 45 | " def get_name(self):\n", 46 | " pass\n", 47 | "\n", 48 | "\n", 49 | "class UserDB:\n", 50 | " def get_user(self, id) -> User:\n", 51 | " pass\n", 52 | "\n", 53 | " def save(self, user: User):\n", 54 | " pass" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "## Open / closed principle" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": { 68 | "collapsed": true 69 | }, 70 | "outputs": [], 71 | "source": [ 72 | "# wrong sample\n", 73 | "\n", 74 | "class Discount:\n", 75 | " def __init__(self, customer):\n", 76 | " self.customer = customer\n", 77 | " \n", 78 | " def give_discount(self, price):\n", 79 | " if self.customer == 'fav':\n", 80 | " return price * 0.2\n", 81 | " if self.customer == 'vip':\n", 82 | " return price * 0.4" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": { 89 | "collapsed": true 90 | }, 91 | "outputs": [], 92 | "source": [ 93 | "# right sample\n", 94 | "\n", 95 | "class Discount:\n", 96 | " def get_discount(self, price):\n", 97 | " return price * 0.2\n", 98 | "\n", 99 | "\n", 100 | "class VIPDiscount(Discount):\n", 101 | " def get_discount(self, price):\n", 102 | " return super().get_discount(price) * 2" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": { 108 | "collapsed": true 109 | }, 110 | "source": [ 111 | "## Liskov Substitution Principle" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": { 118 | "collapsed": true 119 | }, 120 | "outputs": [], 121 | "source": [ 122 | "# wrong sample\n", 123 | "\n", 124 | "class Animal:\n", 125 | " pass\n", 126 | "\n", 127 | "\n", 128 | "class Lion(Animal):\n", 129 | " pass\n", 130 | "\n", 131 | "\n", 132 | "class Wolf(Animal):\n", 133 | " pass\n", 134 | "\n", 135 | "\n", 136 | "def get_animal_sound(animal: Animal):\n", 137 | " if isinstance(animal, Lion):\n", 138 | " return 'Lion sound'\n", 139 | " elif isinstance(animal, Wolf):\n", 140 | " return 'Wolf sound'" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 1, 146 | "metadata": { 147 | "collapsed": true 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "# right sample\n", 152 | "\n", 153 | "class Animal:\n", 154 | " def get_sound(self):\n", 155 | " return 'Animal sound'\n", 156 | " \n", 157 | "\n", 158 | "class Lion(Animal):\n", 159 | " def get_sound(self):\n", 160 | " return 'Lion sound'\n", 161 | "\n", 162 | "\n", 163 | "class Wolf(Animal):\n", 164 | " def get_sound(self):\n", 165 | " return 'Wolf sound'\n", 166 | "\n", 167 | "\n", 168 | "def get_animal_sound(animal: Animal):\n", 169 | " return animal.get_sound()" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 2, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "Lion sound\n", 182 | "Wolf sound\n" 183 | ] 184 | } 185 | ], 186 | "source": [ 187 | "lion = Lion()\n", 188 | "wolf = Wolf()\n", 189 | "print(get_animal_sound(lion))\n", 190 | "print(get_animal_sound(wolf))" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "#### Another example" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 3, 203 | "metadata": { 204 | "collapsed": true 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "class Account:\n", 209 | " def __init__(self, balance):\n", 210 | " self.balance = balance\n", 211 | " \n", 212 | " def withdraw(self, amount):\n", 213 | " self.balance -= amount\n", 214 | " print(f'Current balance is {self.balance}')\n", 215 | " \n", 216 | " \n", 217 | "class UserAccount(Account):\n", 218 | " def withdraw(self, amount):\n", 219 | " super().withdraw(amount)\n", 220 | " if self.balance < 0:\n", 221 | " print(f'User is in debt') " 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 4, 227 | "metadata": { 228 | "collapsed": true 229 | }, 230 | "outputs": [], 231 | "source": [ 232 | "class WrongUserAccount(Account):\n", 233 | " def withdraw(self, amount):\n", 234 | " remaining = self.balance - amount\n", 235 | " if remaining >= 0:\n", 236 | " super().withdraw(amount)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 5, 242 | "metadata": {}, 243 | "outputs": [ 244 | { 245 | "name": "stdout", 246 | "output_type": "stream", 247 | "text": [ 248 | "Right behaviour:\n", 249 | "Current balance is 10\n", 250 | "Current balance is -10\n", 251 | "User is in debt\n" 252 | ] 253 | } 254 | ], 255 | "source": [ 256 | "print('Right behaviour:')\n", 257 | "account = UserAccount(50)\n", 258 | "account.withdraw(40)\n", 259 | "account.withdraw(20)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 6, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "name": "stdout", 269 | "output_type": "stream", 270 | "text": [ 271 | "Wrong behaviour:\n", 272 | "Current balance is 10\n" 273 | ] 274 | } 275 | ], 276 | "source": [ 277 | "print('Wrong behaviour:')\n", 278 | "wrong_account = WrongUserAccount(50)\n", 279 | "wrong_account.withdraw(40)\n", 280 | "wrong_account.withdraw(20)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "## Interface Segregation Principle" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": { 294 | "collapsed": true 295 | }, 296 | "outputs": [], 297 | "source": [ 298 | "# wrong sample\n", 299 | "\n", 300 | "class IShape:\n", 301 | " def draw_circle(self):\n", 302 | " raise NotImplementedError\n", 303 | " \n", 304 | " def draw_square(self):\n", 305 | " raise NotImplementedError\n", 306 | " \n", 307 | " def draw_rectangle(self):\n", 308 | " raise NotImplementedError\n", 309 | " \n", 310 | " \n", 311 | "class Circle(IShape):\n", 312 | " def draw_circle(self):\n", 313 | " pass\n", 314 | " \n", 315 | " def draw_square(self):\n", 316 | " pass\n", 317 | " \n", 318 | " def draw_rectangle(self):\n", 319 | " pass" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "metadata": { 326 | "collapsed": true 327 | }, 328 | "outputs": [], 329 | "source": [ 330 | "# right sample\n", 331 | "\n", 332 | "class IShape:\n", 333 | " def draw(self):\n", 334 | " raise NotImplementedError\n", 335 | "\n", 336 | "\n", 337 | "class Circle(IShape):\n", 338 | " def draw(self):\n", 339 | " pass\n", 340 | "\n", 341 | "\n", 342 | "class Square(IShape):\n", 343 | " def draw(self):\n", 344 | " pass\n", 345 | "\n", 346 | "\n", 347 | "class Rectangle(IShape):\n", 348 | " def draw(self):\n", 349 | " pass" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "## Dependecy Inversion Principle" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [ 367 | "class AuthenticationForUser():\n", 368 | " def __init__(self, connector:Connector):\n", 369 | " self.connection = connector.connect()\n", 370 | "\n", 371 | " def authenticate(self, credentials):\n", 372 | " pass\n", 373 | " \n", 374 | " def is_authenticated(self):\n", 375 | " pass\n", 376 | " \n", 377 | " def last_login(self):\n", 378 | " pass\n", 379 | "\n", 380 | "\n", 381 | "class AnonymousAuth(AuthenticationForUser):\n", 382 | " pass\n", 383 | "\n", 384 | "\n", 385 | "class GithubAuth(AuthenticationForUser):\n", 386 | " pass\n", 387 | "\n", 388 | " \n", 389 | "class FacebookAuth(AuthenticationForUser):\n", 390 | " pass\n", 391 | "\n", 392 | "\n", 393 | "class Permissions()\n", 394 | " def __init__(self, auth: AuthenticationForUser)\n", 395 | " self.auth = auth\n", 396 | "\n", 397 | " def has_permissions():\n", 398 | " pass\n", 399 | " \n", 400 | " def last_login():\n", 401 | " return auth.last_log " 402 | ] 403 | } 404 | ], 405 | "metadata": { 406 | "kernelspec": { 407 | "display_name": "Python 3", 408 | "language": "python", 409 | "name": "python3" 410 | }, 411 | "language_info": { 412 | "codemirror_mode": { 413 | "name": "ipython", 414 | "version": 3 415 | }, 416 | "file_extension": ".py", 417 | "mimetype": "text/x-python", 418 | "name": "python", 419 | "nbconvert_exporter": "python", 420 | "pygments_lexer": "ipython3", 421 | "version": "3.6.2" 422 | } 423 | }, 424 | "nbformat": 4, 425 | "nbformat_minor": 2 426 | } 427 | -------------------------------------------------------------------------------- /12-object-oriented-design/slides/4_composition.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Inheritance example" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "class Door:\n", 19 | " def __init__(self, status):\n", 20 | " self.closed = status\n", 21 | " \n", 22 | " def door_open(self):\n", 23 | " self.closed = False\n", 24 | " \n", 25 | " def door_close(self):\n", 26 | " self.closed = True" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 3, 32 | "metadata": { 33 | "collapsed": true 34 | }, 35 | "outputs": [], 36 | "source": [ 37 | "class SecurityDoor(Door):\n", 38 | " def __init__(self, status):\n", 39 | " super().__init__(status)\n", 40 | " self.locked = True\n", 41 | "\n", 42 | " def door_open(self):\n", 43 | " if self.locked:\n", 44 | " return\n", 45 | " super().door_open()" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "### Composition example" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": { 59 | "collapsed": true 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "class SecurityDoor:\n", 64 | " def __init__(self, status):\n", 65 | " self.door = Door(status)\n", 66 | " self.locked = True\n", 67 | "\n", 68 | " def door_open(self):\n", 69 | " if self.locked:\n", 70 | " return\n", 71 | " self.door.door_open()\n", 72 | "\n", 73 | " def __getattr__(self, attr):\n", 74 | " return getattr(self.door, attr)" 75 | ] 76 | } 77 | ], 78 | "metadata": { 79 | "kernelspec": { 80 | "display_name": "Python 3", 81 | "language": "python", 82 | "name": "python3" 83 | }, 84 | "language_info": { 85 | "codemirror_mode": { 86 | "name": "ipython", 87 | "version": 3 88 | }, 89 | "file_extension": ".py", 90 | "mimetype": "text/x-python", 91 | "name": "python", 92 | "nbconvert_exporter": "python", 93 | "pygments_lexer": "ipython3", 94 | "version": "3.6.2" 95 | } 96 | }, 97 | "nbformat": 4, 98 | "nbformat_minor": 2 99 | } 100 | -------------------------------------------------------------------------------- /12-object-oriented-design/slides/ood_lecture.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/12-object-oriented-design/slides/ood_lecture.pptx -------------------------------------------------------------------------------- /13-design-patterns/hw/1-decorator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/1-decorator/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/hw/1-decorator/decorator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Используя паттерн "Декоратор" реализуйте возможность дополнительно добавлять к кофе 3 | маршмеллоу, взбитые сливки и сироп, а затем вычислить итоговую стоимость напитка. 4 | """ 5 | 6 | 7 | class Component: 8 | def get_cost(self): 9 | raise NotImplementedError("Override get_cost method") 10 | 11 | 12 | class BaseCoffe(Component): 13 | def get_cost(self): 14 | return 90 15 | 16 | 17 | if __name__ == "__main__": 18 | coffe = BaseCoffe() 19 | coffe = Whip(coffe) 20 | coffe = Marshmallow(coffe) 21 | coffe = Syrup(coffe) 22 | print("Итоговая стоимость за кофе: {}".format(str(coffe.get_cost()))) 23 | -------------------------------------------------------------------------------- /13-design-patterns/hw/2-adapter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/2-adapter/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/hw/2-adapter/adapter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Класс StoreService - очень упрощенный арихв XML-документов, который умеет сохранять документ и возвращать его. 3 | (и в котором могло бы быть реализовано больше функций, например, поиск документов по тексту, проверка на попытку 4 | загрузить один и тот же файл дважды и т.д.) 5 | Сервис предоставляет определенный интерфейс, который мы знаем, но не можем изменить. 6 | Класс StoreService нельзя изменять. 7 | 8 | Класс DocumentsHandler - выполняющая роль выгрузки и загрузки документов часть нашего гипотетически огромного 9 | приложения App. Другими словами, это клиент, который содержит существующую бизнес-логику программы. 10 | Класс DocumentsHandler нельзя изменять. 11 | 12 | Далее идет клиентский код, в котором реализована логика загрузки документов в сервис-архив и выгрузка этих же 13 | документов из сервиса. Клиентский код изменять нельзя. 14 | 15 | Ранее сервис работал с документами в формате XML, но недавно он изменился и стал работать с JSON-документами, 16 | что вызвало ряд неполадок в нашем приложении. Необходимо написать класс-адаптер, который перед отработкой 17 | текущей бизнес-логики (загрузка документов) сконвертирует XML-документы в JSON-документы. 18 | 19 | Примечание: Действительно конвертировать XML в JSON не нужно. Будет достаточно изменить расширение документа. 20 | """ 21 | 22 | import uuid 23 | import os 24 | 25 | 26 | class StoreService: 27 | stored_documents = {} 28 | 29 | def store_document(self, document): 30 | if document.endswith(".json"): 31 | document_id = uuid.uuid4() 32 | self.stored_documents[document_id] = document 33 | return {"status": "success", "document_id": document_id} 34 | else: 35 | return { 36 | "status": "error", 37 | "msg": "Now we store only JSON documents. Sorry :(", 38 | } 39 | 40 | def get_document(self, document_id): 41 | if document_id in self.stored_documents: 42 | return {"status": "success", "document": self.stored_documents[document_id]} 43 | return { 44 | "status": "error", 45 | "msg": f"Document with {document_id} ID is not found.", 46 | } 47 | 48 | 49 | class DocumentsHandler: 50 | _service = None 51 | _document_ids = [] 52 | 53 | def __init__(self, service): 54 | self._service = service 55 | 56 | def upload_documents(self, documents): 57 | """ 58 | Метод принимающий на вход 1 или более документов для отгрузки в сервис-архив. 59 | Возвращает список идентификаторов загруженных документов. 60 | """ 61 | if not isinstance(documents, list): 62 | documents = [documents] 63 | 64 | stored_documents = [] 65 | 66 | for document in documents: 67 | result = self._service.store_document(document) 68 | if result["status"] == "success": 69 | stored_documents.append(result["document_id"]) 70 | if result["status"] == "error": 71 | print("Couldn't upload document {}: {}".format(document, result["msg"])) 72 | 73 | self._document_ids.extend(stored_documents) 74 | 75 | return stored_documents 76 | 77 | def get_documents(self, document_ids): 78 | """ 79 | Метод принимающий на вход 1 или более идентификаторов документа для их загрузки из сервиса-архива. 80 | Возвращает список выгруженных документов. 81 | """ 82 | if not isinstance(document_ids, list): 83 | document_ids = [document_ids] 84 | 85 | loaded_documents = [] 86 | 87 | for doc_id in document_ids: 88 | result = self._service.get_document(doc_id) 89 | if result["status"] == "success": 90 | loaded_documents.append(result["document"]) 91 | if result["status"] == "error": 92 | print( 93 | "Couldn't load document with {} ID: {}".format( 94 | doc_id, result["msg"] 95 | ) 96 | ) 97 | 98 | return loaded_documents 99 | 100 | 101 | def client_code(documents_handler): 102 | xml_files_to_upload = os.listdir(os.path.dirname(__file__) + "/documents") 103 | 104 | document_ids = documents_handler.upload_documents(xml_files_to_upload) 105 | print(document_ids) 106 | print(documents_handler.get_documents(document_ids[1])) 107 | 108 | 109 | if __name__ == "__main__": 110 | 111 | class App: 112 | pass # Упрощенная реализация сложного приложения 113 | 114 | app = App() 115 | app.documents_handler = DocumentsHandler(StoreService()) 116 | # Реализуйте класс Adapter и раскомментируйте строку ниже 117 | # app.documents_handler = Adapter(app.documents_handler) 118 | client_code(app.documents_handler) 119 | -------------------------------------------------------------------------------- /13-design-patterns/hw/2-adapter/documents/1.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/2-adapter/documents/1.xml -------------------------------------------------------------------------------- /13-design-patterns/hw/2-adapter/documents/2.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/2-adapter/documents/2.xml -------------------------------------------------------------------------------- /13-design-patterns/hw/2-adapter/documents/3.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/2-adapter/documents/3.xml -------------------------------------------------------------------------------- /13-design-patterns/hw/3-observer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/3-observer/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/hw/3-observer/observer.py: -------------------------------------------------------------------------------- 1 | """ 2 | С помощью паттерна "Наблюдатель" реализуйте простую систему подписок и уведомлений видеохостинга MyTube. 3 | 4 | Для реализации можно использовать следующие определения классов: 5 | 6 | MyTubeChannel - канал, у которого есть владелец. 7 | Параметры: 8 | name: str - Название канала 9 | owner: MyTubeUser - Владелец канала 10 | playlists: Dict[str, List[str]] - Плейлисты на канале ({'Название плейлиста': ['видео 1', 'видео 2', 'видео 3']}) 11 | 12 | Методы: 13 | __init__(channel_name: str, chanel_owner: MyTubeUser) - При создании канала указывается название канала и его владелец 14 | subscribe(user: MyTubeUser) - Подписка пользователя user на канал 15 | publish_video(video: str) - Публикация нового видео и рассылка новости о публикации всем подписчикам 16 | publish_playlist(name: str, playlist: List[str]) - Публикация нового плейлиста и рассылка новости о публикации всем подписчикам 17 | 18 | MyTubeUser - Пользователь видеохостинга MyTube 19 | Параметры: 20 | _name: str - Имя пользователя MyTube 21 | Методы: 22 | __init__(user_name: str) - У нового пользователя есть имя 23 | update(message: str): - Метод для приёма уведомлений о публикации 24 | 25 | Пример кода, который должен работать: 26 | 27 | matt = MyTubeUser('Matt') 28 | john = MyTubeUser('John') 29 | erica = MyTubeUser('Erica') 30 | 31 | dogs_life = YoutubeChannel('All about dogs', matt) 32 | dogs_life.subscribe(john) 33 | dogs_life.subscribe(erica) 34 | 35 | dogs_nutrition_videos = ['What do dogs eat?', 'Which Pedigree pack to choose?'] 36 | dogs_nutrition_playlist = {'Dogs nutrition': dogs_nutrition_videos] 37 | 38 | for video in dogs_nutrition_videos: 39 | dogs_life.publish_video(video) 40 | 41 | dogs_life.publish_playlist(dogs_nutrition_playlist) 42 | 43 | Output: 44 | Dear John, there is new video on 'All about dogs' channel: 'What do dogs eat?' 45 | Dear Erica, there is new video on 'All about dogs' channel: 'What do dogs eat?' 46 | Dear John, there is new video on 'All about dogs' channel: 'Which Pedigree pack to choose?' 47 | Dear Erica, there is new video on 'All about dogs' channel: 'Which Pedigree pack to choose?' 48 | Dear John, there is new playlist on 'All about dogs' channel: 'Dogs nutrition' 49 | Dear Erica, there is new playlist on 'All about dogs' channel: 'Dogs nutrition' 50 | 51 | """ 52 | -------------------------------------------------------------------------------- /13-design-patterns/hw/4-chain_of_responsibility/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/4-chain_of_responsibility/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/hw/4-chain_of_responsibility/chain_of_responsibility.py: -------------------------------------------------------------------------------- 1 | """ 2 | С помощью паттерна "Цепочка обязанностей" составьте список покупок для выпечки блинов. 3 | Необходимо осмотреть холодильник и поочередно проверить, есть ли у нас необходимые ингридиенты: 4 | 2 яйца 5 | 300 грамм муки 6 | 0.5 л молока 7 | 100 грамм сахара 8 | 10 мл подсолнечного масла 9 | 120 грамм сливочного масла 10 | 11 | В итоге мы должны получить список недостающих ингридиентов. 12 | """ 13 | -------------------------------------------------------------------------------- /13-design-patterns/hw/5-abstract_factory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/hw/5-abstract_factory/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/hw/5-abstract_factory/abstract_factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Представьте, что вы пишите программу по формированию и выдачи комплексных обедов для сети столовых, которая стала 3 | расширяться и теперь предлагает комплексные обеды для вегетарианцев, детей и любителей китайской кухни. 4 | 5 | С помощью паттерна "Абстрактная фабрика" вам необходимо реализовать выдачу комплексного обеда, состоящего из трёх 6 | позиций (первое, второе и напиток). 7 | В файле menu.yml находится меню на каждый день, в котором указаны позиции и их принадлежность к 8 | определенному типу блюд. 9 | """ 10 | -------------------------------------------------------------------------------- /13-design-patterns/hw/5-abstract_factory/menu.yml: -------------------------------------------------------------------------------- 1 | Monday: 2 | first_courses: 3 | vegan: 'Салат с рукколой и редисом' 4 | child: 'Салат из белокочанной капусты с яблоком' 5 | chinese: 'Курица Гунбао' 6 | second_courses: 7 | vegan: 'Морковный суп-пюре с хрустящим нутом' 8 | child: 'Борщ-пюре' 9 | chinese: 'Китайский холодный суп из баклажанов' 10 | drinks: 11 | vegan: 'Фруктовый коктейль с кефиром и творогом' 12 | child: 'Компот из сухофруктов' 13 | chinese: 'Ореховый кисель' 14 | 15 | Tuesday: 16 | first_courses: 17 | vegan: 'Морковно-яблочный салат' 18 | child: 'Свекольный салат с черносливом и фетой' 19 | chinese: 'Жареные огурцы' 20 | second_courses: 21 | vegan: 'Гороховый суп с томатной пастой' 22 | child: 'Суп рататуй в мультиварке' 23 | chinese: 'Сырный суп с копчеными колбасками' 24 | drinks: 25 | vegan: 'Фруктовый коктейль с кефиром и творогом' 26 | child: 'Компот из замороженных ягод' 27 | chinese: 'Кисель из ржаной муки' 28 | 29 | Wednesday: 30 | first_courses: 31 | vegan: 'Салат из свежей капусты с чесночной заправкой' 32 | child: 'Салат из моркови и кураги' 33 | chinese: 'Тушеные грибы и морские ушки' 34 | second_courses: 35 | vegan: 'Гороховый суп с томатной пастой' 36 | child: 'Суп с рыбными фрикадельками' 37 | chinese: 'Суп с рисовой лапшой и бараниной' 38 | drinks: 39 | vegan: 'Фруктовый коктейль с кефиром и творогом' 40 | child: 'Растишка' 41 | chinese: 'Кисель из ананаса по-пекински' 42 | 43 | Thursday: 44 | first_courses: 45 | vegan: 'Салат из огурцов и мяты.' 46 | child: 'Салат "Витаминный"' 47 | chinese: 'Салат фунчоза с мясом' 48 | second_courses: 49 | vegan: 'Грибной суп из вешенок' 50 | child: 'Суп рататуй в мультиварке' 51 | chinese: 'Кисло-сладкий острый суп' 52 | drinks: 53 | vegan: 'Фруктовый коктейль с кефиром и творогом' 54 | child: 'Фрутоняня' 55 | chinese: 'Чай из древесных грибов' 56 | 57 | Friday: 58 | first_courses: 59 | vegan: 'Запеканка из брокколи и цветной капусты' 60 | child: 'Панкейки с морковью' 61 | chinese: 'Салат Пекинский с отварной курятиной' 62 | second_courses: 63 | vegan: 'Темный борщ с грибами и черносливом' 64 | child: 'Суп с рыбными фрикадельками' 65 | chinese: 'Суп с зимней тыквой и свиными рёбрышками' 66 | drinks: 67 | vegan: 'Фруктовый коктейль с кефиром и творогом' 68 | child: 'Агуша' 69 | chinese: 'Кисель из ржаной муки' 70 | 71 | Saturday: 72 | first_courses: 73 | vegan: 'Салат из моркови с чесноком' 74 | child: 'нутовые блины с печенью трески' 75 | chinese: 'Курица с ананасами в кисло-сладком соусе' 76 | second_courses: 77 | vegan: 'Светлый борщ с грибами и курагой' 78 | child: 'Гороховый суп' 79 | chinese: 'Яичный суп с помидорами' 80 | drinks: 81 | vegan: 'Фруктовый коктейль с кефиром и творогом' 82 | child: 'Яблочный сок из яблок' 83 | chinese: 'Ореховый кисель' 84 | 85 | Sunday: 86 | first_courses: 87 | vegan: 'Салат из свежей капусты с яблоком' 88 | child: 'цельнозерновая паста со стручковой фасолью' 89 | chinese: 'Жареный рис с овощами по китайски' 90 | second_courses: 91 | vegan: 'Запеканка с фасолью и картофельным пюре' 92 | child: 'Овощной суп с нутом' 93 | chinese: 'Яичный суп с чёрным древесным грибом' 94 | drinks: 95 | vegan: 'Фруктовый коктейль с кефиром и творогом' 96 | child: 'Банановый смузи с печеньем и орехами' 97 | chinese: 'Кисель из ананаса по-пекински' -------------------------------------------------------------------------------- /13-design-patterns/slides/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/slides/__init__.py -------------------------------------------------------------------------------- /13-design-patterns/slides/abstract_factory.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class AbstractProductA(ABC): # колесо 5 | """ 6 | Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс. 7 | Все вариации продукта должны реализовывать этот интерфейс. 8 | """ 9 | 10 | @abstractmethod 11 | def useful_function_a(self) -> str: # надуть колесо 12 | pass 13 | 14 | 15 | """ 16 | Конкретные продукты создаются соответствующими Конкретными Фабриками. 17 | """ 18 | 19 | 20 | class ConcreteProductA1(AbstractProductA): # колесо автомобиля 21 | def useful_function_a(self) -> str: # надуть колесо 22 | return "The result of the product A1." 23 | 24 | 25 | class ConcreteProductA2(AbstractProductA): # колесо велосипеда 26 | def useful_function_a(self) -> str: # надуть колесо 27 | return "The result of the product A2." 28 | 29 | 30 | class AbstractProductB(ABC): # средство передвижения 31 | """ 32 | Базовый интерфейс другого продукта. Все продукты могут взаимодействовать 33 | друг с другом, но правильное взаимодействие возможно только между продуктами 34 | одной и той же конкретной вариации. 35 | """ 36 | 37 | @abstractmethod 38 | def useful_function_b(self) -> None: # ехать 39 | """ 40 | Продукт Б способен работать самостоятельно... 41 | """ 42 | pass 43 | 44 | @abstractmethod 45 | def another_useful_function_b( 46 | self, collaborator: AbstractProductA 47 | ) -> None: # установить колесо 48 | """ 49 | ...а также взаимодействовать с Продуктами А той же вариации. 50 | 51 | Абстрактная Фабрика гарантирует, что все продукты, которые она создает, 52 | имеют одинаковую вариацию и, следовательно, совместимы. 53 | """ 54 | pass 55 | 56 | 57 | """ 58 | Конкретные Продукты создаются соответствующими Конкретными Фабриками. 59 | """ 60 | 61 | 62 | class ConcreteProductB1( 63 | AbstractProductB 64 | ): # конкретное средство передвижения: автомобиль 65 | def useful_function_b(self) -> str: # ехать 66 | # проверить колеса: если надо, установить и надуть колеса 67 | # завести двигатель 68 | # нажать на педаль газа 69 | return "The result of the product B1." 70 | 71 | """ 72 | Продукт Б1 может корректно работать только с Продуктом A1. Тем не менее, он 73 | принимает любой экземпляр Абстрактного Продукта А в качестве аргумента. 74 | """ 75 | 76 | def another_useful_function_b( 77 | self, collaborator: AbstractProductA 78 | ) -> str: # установить колесо(абстрактное колесо) 79 | result = collaborator.useful_function_a() # надули колесо 80 | return f"The result of the B1 collaborating with the ({result})" 81 | 82 | 83 | class ConcreteProductB2( 84 | AbstractProductB 85 | ): # конкретное средство передвижения: велосипед 86 | def useful_function_b(self) -> str: # ехать 87 | # проверить колеса: если надо, установить и надуть колеса 88 | # снять замок 89 | # крутить педали 90 | return "The result of the product B2." 91 | 92 | def another_useful_function_b( 93 | self, collaborator: AbstractProductA 94 | ): # установить колесо(абстрактное колесо) 95 | """ 96 | Продукт Б2 может корректно работать только с Продуктом A2. Тем не менее, 97 | он принимает любой экземпляр Абстрактного Продукта А в качестве 98 | аргумента. 99 | """ 100 | result = collaborator.useful_function_a() 101 | return f"The result of the B2 collaborating with the ({result})" 102 | 103 | 104 | class AbstractFactory(ABC): 105 | """ 106 | Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают 107 | различные абстрактные продукты. Эти продукты называются семейством и связаны 108 | темой или концепцией высокого уровня. Продукты одного семейства обычно могут 109 | взаимодействовать между собой. Семейство продуктов может иметь несколько 110 | вариаций, но продукты одной вариации несовместимы с продуктами другой. 111 | """ 112 | 113 | @abstractmethod 114 | def create_product_a(self) -> AbstractProductA: # создать колесо 115 | pass 116 | 117 | @abstractmethod 118 | def create_product_b(self) -> AbstractProductB: # создать средство передвижения 119 | pass 120 | 121 | 122 | class ConcreteFactory1(AbstractFactory): # будем работать с автомобилем 123 | """ 124 | Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика 125 | гарантирует совместимость полученных продуктов. Обратите внимание, что 126 | сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то 127 | время как внутри метода создается экземпляр конкретного продукта. 128 | """ 129 | 130 | def create_product_a(self) -> ConcreteProductA1: # создать колесо автомобиля 131 | return ConcreteProductA1() 132 | 133 | def create_product_b(self) -> ConcreteProductB1: # создать автомобиль 134 | return ConcreteProductB1() 135 | 136 | 137 | class ConcreteFactory2(AbstractFactory): # будем работать с велосипедом 138 | """ 139 | Каждая Конкретная Фабрика имеет соответствующую вариацию продукта. 140 | """ 141 | 142 | def create_product_a(self) -> ConcreteProductA2: # создать колесо велосипеда 143 | return ConcreteProductA2() 144 | 145 | def create_product_b(self) -> ConcreteProductB2: # создать велосипед 146 | return ConcreteProductB2() 147 | 148 | 149 | def client_code(factory: AbstractFactory) -> None: 150 | """ 151 | Клиентский код работает с фабриками и продуктами только через абстрактные 152 | типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать 153 | любой подкласс фабрики или продукта клиентскому коду, не нарушая его. 154 | """ 155 | product_a = factory.create_product_a() # создем какое-то колесо 156 | product_b = factory.create_product_b() # создаем какое-то средство передвижения 157 | 158 | # устанавливаем на какое-то средство передвижения какое-то колесо 159 | print(f"{product_b.another_useful_function_b(product_a)}") 160 | print(f"{product_b.useful_function_b()}") # едем 161 | 162 | 163 | if __name__ == "__main__": 164 | """ 165 | Клиентский код может работать с любым конкретным классом фабрики. 166 | """ 167 | print("Client: Testing client code with the first factory type:") 168 | client_code(ConcreteFactory1()) 169 | 170 | print("\n") 171 | 172 | print("Client: Testing the same client code with the second factory type:") 173 | client_code(ConcreteFactory2()) 174 | -------------------------------------------------------------------------------- /13-design-patterns/slides/abstract_factory_example.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class AbstractWheel(ABC): # колесо 5 | """ 6 | Каждый отдельный продукт семейства продуктов должен иметь базовый интерфейс. 7 | Все вариации продукта должны реализовывать этот интерфейс. 8 | """ 9 | 10 | @abstractmethod 11 | def inflate(self) -> str: # надуть колесо 12 | pass 13 | 14 | 15 | """ 16 | Конкретные Продукты создаются соответствующими Конкретными Фабриками. 17 | """ 18 | 19 | 20 | class ConcreteWheelCar(AbstractWheel): # колесо автомобиля 21 | def inflate(self) -> str: # надуть колесо 22 | return "Надутое колесо автомобиля" 23 | 24 | 25 | class ConcreteWheelBicycle(AbstractWheel): # колесо велосипеда 26 | def inflate(self) -> str: # надуть колесо 27 | return "Надутое колесо велосипеда" 28 | 29 | 30 | class AbstractTransport(ABC): # средство передвижения 31 | """ 32 | Базовый интерфейс другого продукта. Все продукты могут взаимодействовать 33 | друг с другом, но правильное взаимодействие возможно только между продуктами 34 | одной и той же конкретной вариации. 35 | """ 36 | 37 | @abstractmethod 38 | def ride(self) -> None: # ехать 39 | """ 40 | Продукт Б способен работать самостоятельно... 41 | """ 42 | pass 43 | 44 | @abstractmethod 45 | def set_wheel(self, collaborator: AbstractWheel) -> None: # установить колесо 46 | """ 47 | ...а также взаимодействовать с Продуктами А той же вариации. 48 | 49 | Абстрактная Фабрика гарантирует, что все продукты, которые она создает, 50 | имеют одинаковую вариацию и, следовательно, совместимы. 51 | """ 52 | pass 53 | 54 | 55 | """ 56 | Конкретные Продукты создаются соответствующими Конкретными Фабриками. 57 | """ 58 | 59 | 60 | class ConcreteTransportCar( 61 | AbstractTransport 62 | ): # конкретное средство передвижения: автомобиль 63 | def ride(self) -> str: # ехать 64 | # проверить колеса: если надо, установить и надуть колеса 65 | # завести двигатель 66 | # нажать на педаль газа 67 | return "Едем на автомобиле" 68 | 69 | """ 70 | Продукт Б1 может корректно работать только с Продуктом A1. Тем не менее, он 71 | принимает любой экземпляр Абстрактного Продукта А в качестве аргумента. 72 | """ 73 | 74 | def set_wheel( 75 | self, collaborator: AbstractWheel 76 | ) -> str: # установить колесо(абстрактное колесо) 77 | result = collaborator.inflate() # надули колесо 78 | return f"Результат установки на автомобиль компонента ({result})" 79 | 80 | 81 | class ConcreteTransportBicycle( 82 | AbstractTransport 83 | ): # конкретное средство передвижения: велосипед 84 | def ride(self) -> str: # ехать 85 | # проверить колеса: если надо, установить и надуть колеса 86 | # снять замок 87 | # крутить педали 88 | return "Едем на велосипеде" 89 | 90 | def set_wheel( 91 | self, collaborator: AbstractWheel 92 | ): # установить колесо(абстрактное колесо) 93 | """ 94 | Продукт Б2 может корректно работать только с Продуктом A2. Тем не менее, 95 | он принимает любой экземпляр Абстрактного Продукта А в качестве 96 | аргумента. 97 | """ 98 | result = collaborator.inflate() 99 | return f"Результат установки на велосипед компонента ({result})" 100 | 101 | 102 | class AbstractFactory(ABC): 103 | """ 104 | Интерфейс Абстрактной Фабрики объявляет набор методов, которые возвращают 105 | различные абстрактные продукты. Эти продукты называются семейством и связаны 106 | темой или концепцией высокого уровня. Продукты одного семейства обычно могут 107 | взаимодействовать между собой. Семейство продуктов может иметь несколько 108 | вариаций, но продукты одной вариации несовместимы с продуктами другой. 109 | """ 110 | 111 | @abstractmethod 112 | def create_wheel(self) -> AbstractWheel: # создать колесо 113 | pass 114 | 115 | @abstractmethod 116 | def create_transport(self) -> AbstractTransport: # создать средство передвижения 117 | pass 118 | 119 | 120 | class ConcreteFactoryCar(AbstractFactory): # будем работать с автомобилем 121 | """ 122 | Конкретная Фабрика производит семейство продуктов одной вариации. Фабрика 123 | гарантирует совместимость полученных продуктов. Обратите внимание, что 124 | сигнатуры методов Конкретной Фабрики возвращают абстрактный продукт, в то 125 | время как внутри метода создается экземпляр конкретного продукта. 126 | """ 127 | 128 | def create_wheel(self) -> ConcreteWheelCar: # создать колесо автомобиля 129 | return ConcreteWheelCar() 130 | 131 | def create_transport(self) -> ConcreteTransportCar: # создать автомобиль 132 | return ConcreteTransportCar() 133 | 134 | 135 | class ConcreteFactoryBicycle(AbstractFactory): # будем работать с велосипедом 136 | """ 137 | Каждая Конкретная Фабрика имеет соответствующую вариацию продукта. 138 | """ 139 | 140 | def create_wheel(self) -> ConcreteWheelBicycle: # создать колесо велосипеда 141 | return ConcreteWheelBicycle() 142 | 143 | def create_transport(self) -> ConcreteTransportBicycle: # создать велосипед 144 | return ConcreteTransportBicycle() 145 | 146 | 147 | def client_code(factory: AbstractFactory) -> None: 148 | """ 149 | Клиентский код работает с фабриками и продуктами только через абстрактные 150 | типы: Абстрактная Фабрика и Абстрактный Продукт. Это позволяет передавать 151 | любой подкласс фабрики или продукта клиентскому коду, не нарушая его. 152 | """ 153 | wheel = factory.create_wheel() # создем какое-то колесо 154 | transport = factory.create_transport() # создаем какое-то средство передвижения 155 | 156 | # устанавливаем на какое-то средство передвижения какое-то колесо 157 | print(f"{transport.set_wheel(wheel)}") 158 | print(f"{transport.ride()}") # едем 159 | 160 | 161 | if __name__ == "__main__": 162 | """ 163 | Клиентский код может работать с любым конкретным классом фабрики. 164 | """ 165 | print("Клиент: Тестируем код клиента с фабрикой автомобилей:") 166 | client_code(ConcreteFactoryCar()) 167 | 168 | print("\n") 169 | 170 | print("Клиент: Тестируем код клиента с фабрикой велосипедов:") 171 | client_code(ConcreteFactoryBicycle()) 172 | -------------------------------------------------------------------------------- /13-design-patterns/slides/adapter.py: -------------------------------------------------------------------------------- 1 | class Client: 2 | """ 3 | Целевой класс объявляет интерфейс, с которым может работать код пользователей. 4 | """ 5 | 6 | def request(self): 7 | return "Client: The default client's behavior." 8 | 9 | 10 | class Service: 11 | """ 12 | Адаптируемый класс содержит некоторое полезное поведение, но его интерфейс 13 | несовместим с существующим пользовательским кодом. Адаптируемый класс нуждается в 14 | некоторой доработке, прежде чем пользовательский код сможет его использовать. 15 | """ 16 | 17 | def specific_request(self): 18 | return ".ecivreS eht fo roivaheb laicepS" 19 | 20 | 21 | class Adapter(Client): 22 | """ 23 | Адаптер делает интерфейс Адаптируемого класса совместимым с целевым 24 | интерфейсом. 25 | """ 26 | 27 | def __init__(self, adaptee): 28 | self.adaptee = adaptee 29 | 30 | def request(self): 31 | return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}" 32 | 33 | 34 | def user_code(client): 35 | """ 36 | Пользовательский код поддерживает все классы, использующие интерфейс Client. 37 | """ 38 | 39 | print(client.request()) 40 | 41 | 42 | if __name__ == "__main__": 43 | print("User: I can work just fine with the Client objects:") 44 | client = Client() 45 | user_code(client) 46 | print("-" * 50) 47 | 48 | service = Service() 49 | print("User: The Service class has a weird interface. See, I don't understand it:") 50 | print(f"Service: {service.specific_request()}") 51 | print("-" * 50) 52 | 53 | print("User: But I can work with it via the Adapter:") 54 | adapter = Adapter(service) 55 | user_code(adapter) 56 | -------------------------------------------------------------------------------- /13-design-patterns/slides/chain_of_responsibility.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Any, Optional 3 | 4 | 5 | class Handler(ABC): 6 | """ 7 | Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он 8 | также объявляет метод для выполнения запроса. 9 | """ 10 | 11 | @abstractmethod 12 | def set_next(self, handler): 13 | pass 14 | 15 | @abstractmethod 16 | def handle(self, request) -> Optional[str]: 17 | pass 18 | 19 | 20 | class AbstractHandler(Handler): 21 | """ 22 | Поведение цепочки по умолчанию может быть реализовано внутри базового класса 23 | обработчика. 24 | """ 25 | 26 | _next_handler: Handler = None 27 | 28 | def set_next(self, handler: Handler) -> Handler: 29 | self._next_handler = handler 30 | # Возврат обработчика отсюда позволит связать обработчики простым 31 | # способом, вот так: 32 | # handler1.set_next(handler2).set_next(handler3) 33 | return handler 34 | 35 | @abstractmethod 36 | def handle(self, request: Any) -> Optional[str]: 37 | if self._next_handler: 38 | return self._next_handler.handle(request) 39 | return None 40 | 41 | 42 | """ 43 | Все Конкретные Обработчики либо обрабатывают запрос, либо передают его 44 | следующему обработчику в цепочке. 45 | """ 46 | 47 | MAX_FUEL_LEVEL = 100 48 | MAX_OIL_LEVEL = 100 49 | 50 | 51 | class Car: 52 | def __init__( 53 | self, fuel_level=100, wheels_broken=0, oil_level=100, brake_correct=True 54 | ): 55 | self._fuel_level = fuel_level # 0 - 100 % 56 | self._wheels_broken = wheels_broken # 0 - 4 57 | self._oil_level = oil_level # 0 - 100 % 58 | self._brake_correct = brake_correct # True or False 59 | 60 | def add_fuel(self, fuel_num): 61 | self._fuel_level += fuel_num 62 | 63 | def add_oil(self, oil_num): 64 | self._oil_level += oil_num 65 | 66 | 67 | class FuelHandler(AbstractHandler): 68 | def handle(self, car: Car): 69 | if car._fuel_level < MAX_FUEL_LEVEL: 70 | fuel_to_add = MAX_FUEL_LEVEL - car._fuel_level 71 | car.add_fuel(fuel_to_add) 72 | print(f"Долито {fuel_to_add} топлива в бак") 73 | if self._next_handler: 74 | return self._next_handler.handle(car) 75 | 76 | 77 | class WheelHandler(AbstractHandler): 78 | def handle(self, car: Car): 79 | if car._wheels_broken > 0: 80 | print(f"Смена колёс: {car._wheels_broken} шт.") 81 | car._wheels_broken = 0 82 | if self._next_handler: 83 | return self._next_handler.handle(car) 84 | 85 | 86 | class OilHandler(AbstractHandler): 87 | def handle(self, car: Car): 88 | if car._oil_level < MAX_OIL_LEVEL: 89 | oil_to_add = MAX_OIL_LEVEL - car._oil_level 90 | car.add_oil(oil_to_add) 91 | print(f"Долито {oil_to_add} масла") 92 | if self._next_handler: 93 | return self._next_handler.handle(car) 94 | 95 | 96 | class BrakeHandler(AbstractHandler): 97 | def handle(self, car: Car): 98 | if not car._brake_correct: 99 | print("Починка тормозов") 100 | car._brake_correct = True 101 | if self._next_handler: 102 | return self._next_handler.handle(car) 103 | 104 | 105 | def repair_car(car): 106 | fuel_handler = FuelHandler() 107 | wheel_handler = WheelHandler() 108 | oil_handler = OilHandler() 109 | brake_handler = BrakeHandler() 110 | 111 | fuel_handler.set_next(wheel_handler).set_next(oil_handler).set_next(brake_handler) 112 | 113 | fuel_handler.handle(car) 114 | 115 | 116 | car_to_service = Car(fuel_level=25, wheels_broken=2, oil_level=10, brake_correct=False) 117 | repair_car(car_to_service) 118 | -------------------------------------------------------------------------------- /13-design-patterns/slides/chain_of_responsibility_properties.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Any, Optional 3 | 4 | 5 | class Handler(ABC): 6 | """ 7 | Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он 8 | также объявляет метод для выполнения запроса. 9 | """ 10 | 11 | @abstractmethod 12 | def set_next(self, handler): 13 | pass 14 | 15 | @abstractmethod 16 | def handle(self, request) -> Optional[str]: 17 | pass 18 | 19 | 20 | class AbstractHandler(Handler): 21 | """ 22 | Поведение цепочки по умолчанию может быть реализовано внутри базового класса 23 | обработчика. 24 | """ 25 | 26 | _next_handler: Handler = None 27 | 28 | def set_next(self, handler: Handler) -> Handler: 29 | self._next_handler = handler 30 | # Возврат обработчика отсюда позволит связать обработчики простым 31 | # способом, вот так: 32 | # handler1.set_next(handler2).set_next(handler3) 33 | return handler 34 | 35 | @abstractmethod 36 | def handle(self, request: Any) -> Optional[str]: 37 | if self._next_handler: 38 | return self._next_handler.handle(request) 39 | return None 40 | 41 | 42 | """ 43 | Все Конкретные Обработчики либо обрабатывают запрос, либо передают его 44 | следующему обработчику в цепочке. 45 | """ 46 | 47 | MAX_FUEL_LEVEL = 100 48 | MAX_OIL_LEVEL = 100 49 | 50 | 51 | class Car: 52 | def __init__( 53 | self, fuel_level=100, wheels_broken=0, oil_level=100, brake_correct=True 54 | ): 55 | self.fuel_level = fuel_level # 0 - 100 % 56 | self.wheels_broken = wheels_broken # 0 - 4 57 | self.oil_level = oil_level # 0 - 100 % 58 | self.brake_correct = brake_correct # True or False 59 | 60 | def add_fuel(self, fuel_num): 61 | self._fuel_level += fuel_num 62 | 63 | def add_oil(self, oil_num): 64 | self._oil_level += oil_num 65 | 66 | # Проверки можно организовать с помощью свойств 67 | @property 68 | def fuel_level(self): 69 | return self._fuel_level 70 | 71 | @fuel_level.setter 72 | def fuel_level(self, num): 73 | if 0 <= num <= 100: 74 | self._fuel_level = num 75 | else: 76 | raise ValueError("Уровень топлива должен быть в диапазоне от 0 до 100 %") 77 | 78 | @property 79 | def wheels_broken(self): 80 | return self._wheels_broken 81 | 82 | @wheels_broken.setter 83 | def wheels_broken(self, num): 84 | if 0 <= num <= 4: 85 | self._wheels_broken = num 86 | else: 87 | raise ValueError("У автомобиля от 0 до 4 колес") 88 | 89 | @property 90 | def oil_level(self): 91 | return self._oil_level 92 | 93 | @oil_level.setter 94 | def oil_level(self, level): 95 | if 0 <= level <= 100: 96 | self._oil_level = level 97 | else: 98 | raise ValueError("Уровень масла должен быть в диапазоне от 0 до 100 %") 99 | 100 | @property 101 | def brake_correct(self): 102 | return self._brake_correct 103 | 104 | @brake_correct.setter 105 | def brake_correct(self, state): 106 | self._brake_correct = state 107 | 108 | 109 | class FuelHandler(AbstractHandler): 110 | def handle(self, car: Car): 111 | if car.fuel_level < MAX_FUEL_LEVEL: 112 | fuel_to_add = MAX_FUEL_LEVEL - car.fuel_level 113 | car.add_fuel(fuel_to_add) 114 | print(f"Долито {fuel_to_add} топлива в бак") 115 | if self._next_handler: 116 | return self._next_handler.handle(car) 117 | 118 | 119 | class WheelHandler(AbstractHandler): 120 | def handle(self, car: Car): 121 | if car.wheels_broken > 0: 122 | print(f"Смена колёс: {car.wheels_broken} шт.") 123 | car._wheels_broken = 0 124 | if self._next_handler: 125 | return self._next_handler.handle(car) 126 | 127 | 128 | class OilHandler(AbstractHandler): 129 | def handle(self, car: Car): 130 | if car.oil_level < MAX_OIL_LEVEL: 131 | oil_to_add = MAX_OIL_LEVEL - car.oil_level 132 | car.add_oil(oil_to_add) 133 | print(f"Долито {oil_to_add} масла") 134 | if self._next_handler: 135 | return self._next_handler.handle(car) 136 | 137 | 138 | class BrakeHandler(AbstractHandler): 139 | def handle(self, car: Car): 140 | if not car.brake_correct: 141 | print("Починка тормозов") 142 | car._brake_correct = True 143 | if self._next_handler: 144 | return self._next_handler.handle(car) 145 | 146 | 147 | def repair_car(car): 148 | fuel_handler = FuelHandler() 149 | wheel_handler = WheelHandler() 150 | oil_handler = OilHandler() 151 | brake_handler = BrakeHandler() 152 | 153 | fuel_handler.set_next(wheel_handler).set_next(oil_handler).set_next(brake_handler) 154 | 155 | fuel_handler.handle(car) 156 | 157 | 158 | car_to_check = Car(fuel_level=25, wheels_broken=2, oil_level=10, brake_correct=False) 159 | # wrong_car_to_check = Car(fuel_level=125, wheels_broken=2, oil_level=10, brake_correct=False) 160 | 161 | repair_car(car_to_check) 162 | -------------------------------------------------------------------------------- /13-design-patterns/slides/decorator_coffee_shop.py: -------------------------------------------------------------------------------- 1 | # Decorator design pattern (OOP) 2 | # https://www.tutorialspoint.com/python_design_patterns/python_design_patterns_decorator.htm 3 | # https://refactoring.guru/design-patterns/decorator/python/example#lang-features 4 | 5 | # Decoration of a function (functional programming) 6 | # https://realpython.com/primer-on-python-decorators/ 7 | # https://hackernoon.com/decorators-in-python-8fd0dce93c08 8 | 9 | from abc import ABC, abstractmethod 10 | 11 | 12 | class AbstractCoffee(ABC): 13 | """ 14 | Базовый интерфейс Компонента определяет поведение, которое изменяется декораторами. 15 | """ 16 | 17 | def __str__(self): 18 | return ( 19 | f"Coffee {hex(id(self))}; cost: {self.get_cost()}; " 20 | f"ingredients: {self.get_ingredients()}; tax: {self.get_tax()}" 21 | ) 22 | 23 | @abstractmethod 24 | def get_cost(self): 25 | pass 26 | 27 | @abstractmethod 28 | def get_ingredients(self): 29 | pass 30 | 31 | def get_tax(self): 32 | return 0.1 * self.get_cost() 33 | 34 | 35 | class ConcreteCoffee(AbstractCoffee): 36 | """ 37 | Конкретный Компонент предоставляет реализацию поведения по умолчанию. 38 | Может быть несколько вариаций этих классов. 39 | """ 40 | 41 | def get_cost(self): 42 | return 1 43 | 44 | def get_ingredients(self): 45 | return "coffee" 46 | 47 | 48 | class AbstractCoffeeDecorator(AbstractCoffee): 49 | """ 50 | Базовый класс Декоратора следует тому же интерфейсу, что и другие 51 | компоненты. Основная цель этого класса - определить интерфейс обёртки для 52 | всех конкретных декораторов. Реализация кода обёртки по умолчанию может 53 | включать в себя поле для хранения завёрнутого компонента и средства его 54 | инициализации. 55 | """ 56 | 57 | def __init__(self, decorated_coffee): 58 | self.decorated_coffee = decorated_coffee 59 | 60 | def get_cost(self): 61 | return self.decorated_coffee.get_cost() 62 | 63 | def get_ingredients(self): 64 | return self.decorated_coffee.get_ingredients() 65 | 66 | 67 | class Sugar(AbstractCoffeeDecorator): 68 | """ 69 | Конкретные Декораторы вызывают обёрнутый объект и изменяют его результат 70 | некоторым образом. 71 | """ 72 | 73 | def __init__(self, decorated_coffee): 74 | super(Sugar, self).__init__(decorated_coffee) 75 | 76 | def get_ingredients(self): 77 | return self.decorated_coffee.get_ingredients() + ", sugar" 78 | 79 | 80 | class Milk(AbstractCoffeeDecorator): 81 | def __init__(self, decorated_coffee): 82 | super(Milk, self).__init__(decorated_coffee) 83 | 84 | def get_cost(self): 85 | return self.decorated_coffee.get_cost() + 0.25 86 | 87 | def get_ingredients(self): 88 | return self.decorated_coffee.get_ingredients() + ", milk" 89 | 90 | 91 | class Vanilla(AbstractCoffeeDecorator): 92 | def __init__(self, decorated_coffee): 93 | super(Vanilla, self).__init__(decorated_coffee) 94 | 95 | def get_cost(self): 96 | return self.decorated_coffee.get_cost() + 0.75 97 | 98 | def get_ingredients(self): 99 | return self.decorated_coffee.get_ingredients() + ", vanilla" 100 | 101 | 102 | if __name__ == "__main__": 103 | print("It's a coffee shop") 104 | coffee = ConcreteCoffee() 105 | print(coffee) 106 | 107 | coffee = Milk(coffee) 108 | print(coffee) 109 | 110 | coffee = Vanilla(coffee) 111 | print(coffee) 112 | 113 | coffee = Sugar(coffee) 114 | print(coffee) 115 | 116 | print("-" * 50) 117 | 118 | # ----------------------------------------------------------------------- 119 | # decorator of a function 120 | def welcome_message(func): 121 | def print_wrapper(*args): 122 | print("Thank you for visiting our Coffee Shop!") 123 | func(*args) 124 | print("See you next time!") 125 | 126 | return print_wrapper 127 | 128 | # long form 129 | def print_coffee(_coffee): 130 | print( 131 | f"You ordered: {_coffee.get_ingredients()}; \n" 132 | f"The total price is: {_coffee.get_cost() + _coffee.get_tax()}$" 133 | ) 134 | 135 | decorated_print = welcome_message(print_coffee) 136 | decorated_print(coffee) 137 | 138 | print("-" * 50) 139 | 140 | # short form 141 | @welcome_message 142 | def decorated_print_coffee(_coffee): 143 | print( 144 | f"You ordered: {_coffee.get_ingredients()}; \n" 145 | f"The total price is: {_coffee.get_cost() + _coffee.get_tax()}$" 146 | ) 147 | 148 | decorated_print_coffee(coffee) 149 | -------------------------------------------------------------------------------- /13-design-patterns/slides/decorator_components.py: -------------------------------------------------------------------------------- 1 | class Component: 2 | """ 3 | Базовый интерфейс Компонента определяет поведение, которое изменяется 4 | декораторами. 5 | """ 6 | 7 | def operation(self) -> str: 8 | pass 9 | 10 | 11 | class ConcreteComponent(Component): 12 | """ 13 | Конкретные Компоненты предоставляют реализации поведения по умолчанию. Может 14 | быть несколько вариаций этих классов. 15 | """ 16 | 17 | def operation(self) -> str: 18 | return "Base operation" 19 | 20 | 21 | class Decorator(Component): 22 | """ 23 | Базовый класс Декоратора следует тому же интерфейсу, что и другие 24 | компоненты. Основная цель этого класса - определить интерфейс обёртки для 25 | всех конкретных декораторов. Реализация кода обёртки по умолчанию может 26 | включать в себя поле для хранения завёрнутого компонента и средства его 27 | инициализации. 28 | """ 29 | 30 | _component: Component = None 31 | 32 | def __init__(self, component: Component) -> None: 33 | self._component = component 34 | 35 | def operation(self) -> str: 36 | return self._component.operation() 37 | 38 | 39 | class ConcreteDecoratorA(Decorator): 40 | """ 41 | Конкретные Декораторы вызывают обёрнутый объект и изменяют его результат 42 | некоторым образом. 43 | """ 44 | 45 | def operation(self) -> str: 46 | """ 47 | Декораторы могут вызывать родительскую реализацию операции, вместо того, 48 | чтобы вызвать обёрнутый объект напрямую. Такой подход упрощает 49 | расширение классов декораторов. 50 | """ 51 | return "Perform Operation A. {}".format(self._component.operation()) 52 | 53 | 54 | class ConcreteDecoratorB(Decorator): 55 | """ 56 | Декораторы могут выполнять своё поведение до или после вызова обёрнутого 57 | объекта. 58 | """ 59 | 60 | def operation(self) -> str: 61 | return "{}. Perform Operation B".format(self._component.operation()) 62 | 63 | 64 | if __name__ == "__main__": 65 | operator = ConcreteComponent() 66 | before = ConcreteDecoratorA(operator) 67 | after = ConcreteDecoratorB(operator) 68 | print(after.operation()) 69 | -------------------------------------------------------------------------------- /13-design-patterns/slides/design_patterns_lections.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/13-design-patterns/slides/design_patterns_lections.pdf -------------------------------------------------------------------------------- /13-design-patterns/slides/factory_method.py: -------------------------------------------------------------------------------- 1 | """ 2 | Factory Method (Фабричный метод) 3 | Также известен как Виртуальный конструктор (Virtual Constructor) 4 | 5 | Порождающий шаблон проектирования, предоставляющий подклассам (дочерним классам) интерфейс для создания экземпляров 6 | некоторого класса. В момент создания наследники могут определить, какой класс создавать. 7 | Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. 8 | Это позволяет использовать в коде программы не специфические классы, 9 | а манипулировать абстрактными объектами на более высоком уровне. 10 | 11 | Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать. 12 | Фабричный метод позволяет классу делегировать создание подклассов. Используется, когда: 13 | * Классу заранее неизвестно, объекты каких подклассов ему нужно создавать. 14 | * Класс спроектирован так, чтобы объекты, которые он создаёт, специфицировались подклассами. 15 | * Класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и планируется локализовать 16 | знание о том, какой класс принимает эти обязанности на себя 17 | 18 | Структура 19 | * Product — продукт. Определяет интерфейс объектов, создаваемых абстрактным методом; 20 | * ConcreteProduct — конкретный продукт, реализует интерфейс Product; 21 | * Creator — создатель. Объявляет фабричный метод, который возвращает объект типа Product. Может также содержать 22 | реализацию этого метода «по умолчанию»; может вызывать фабричный метод для создания объекта типа Product; 23 | * ConcreteCreator — конкретный создатель. Переопределяет фабричный метод таким образом, чтобы он создавал и 24 | возвращал объект класса ConcreteProduct. 25 | 26 | 27 | Достоинства: 28 | * Позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам (ConcreteProduct), 29 | а оперируя лишь общим интерфейсом (Product) 30 | * Позволяет установить связь между параллельными иерархиями классов. 31 | 32 | Недостатки: 33 | * Необходимость создавать наследника Creator для каждого нового типа продукта (ConcreteProduct). 34 | 35 | Смысл шаблона Фабрика – инкапсулировать логику создания новых объектов. 36 | """ 37 | 38 | 39 | from abc import ABC, abstractmethod 40 | 41 | 42 | class Product(ABC): # абстрактное колесо 43 | """ 44 | Интерфейс Продукта объявляет операции, которые должны выполнять все 45 | конкретные продукты. 46 | """ 47 | 48 | @abstractmethod 49 | def operation(self): # надуть абстрактное колесо 50 | pass 51 | 52 | 53 | """ 54 | Конкретные Продукты предоставляют различные реализации интерфейса Продукта. 55 | """ 56 | 57 | 58 | class ConcreteProduct1(Product): # колесо автомобиля 59 | def operation(self): # надуть колесо 60 | return "{Результат операции ConcreteProduct1}" 61 | 62 | 63 | class ConcreteProduct2(Product): # колесо велосипеда 64 | def operation(self): # надуть колесо 65 | return "{Результат операции ConcreteProduct2}" 66 | 67 | 68 | class Creator(ABC): # создатель колес 69 | """ 70 | Класс Создатель объявляет фабричный метод, который должен возвращать объект 71 | класса Продукт. Подклассы Создателя обычно предоставляют реализацию этого 72 | метода. 73 | """ 74 | 75 | @abstractmethod 76 | def factory_method(self): # создадим какое-то колесо 77 | """ 78 | Обратите внимание, что Создатель может также обеспечить реализацию 79 | фабричного метода по умолчанию. 80 | """ 81 | pass 82 | 83 | def some_operation(self): # сделаем что-то полезное 84 | """ 85 | Также заметьте, что, несмотря на название, основная обязанность 86 | Создателя не заключается в создании продуктов. Обычно он содержит 87 | некоторую базовую бизнес-логику, которая основана на объектах Продуктов, 88 | возвращаемых фабричным методом. Подклассы могут косвенно изменять эту 89 | бизнес-логику, переопределяя фабричный метод и возвращая из него другой 90 | тип продукта. 91 | """ 92 | 93 | # Вызываем фабричный метод, чтобы получить объект-продукт. 94 | product = self.factory_method() # сделаем какое-то колесо 95 | 96 | # Далее, работаем с этим продуктом. 97 | # надуем какае-то колесо 98 | result = f"Создатель: Код создателя только что выполнен, и вернул {product.operation()}" 99 | 100 | return result 101 | 102 | 103 | """ 104 | Конкретные Создатели переопределяют фабричный метод для того, чтобы изменить тип 105 | результирующего продукта. 106 | """ 107 | 108 | 109 | class ConcreteCreator1(Creator): # создатель колес автомобиля 110 | """ 111 | Обратите внимание, что сигнатура метода по-прежнему использует тип 112 | абстрактного продукта, хотя фактически из метода возвращается конкретный 113 | продукт. Таким образом, Создатель может оставаться независимым от конкретных 114 | классов продуктов. 115 | """ 116 | 117 | def factory_method(self): 118 | return ConcreteProduct1() # конкертное колесо автомобиля 119 | 120 | 121 | class ConcreteCreator2(Creator): # создатель колес велосипеда 122 | def factory_method(self): 123 | return ConcreteProduct2() # конкретное колесо велосипеда 124 | 125 | 126 | def client_code(creator): 127 | """ 128 | Клиентский код работает с экземпляром конкретного создателя, хотя и через 129 | его базовый интерфейс. Пока клиент продолжает работать с создателем через 130 | базовый интерфейс, вы можете передать ему любой подкласс создателя. 131 | """ 132 | 133 | print( 134 | f"Клиент: Я не знаю, какой конкретный класс у создателя, но операция все равно работает\n" 135 | f"{creator.some_operation()}", 136 | end="", 137 | ) 138 | 139 | 140 | if __name__ == "__main__": 141 | print("Приложение: Запущено с ConcreteCreator1") 142 | client_code(ConcreteCreator1()) 143 | print("\n") 144 | 145 | print("Приложение: Запущено с ConcreteCreator2") 146 | client_code(ConcreteCreator2()) 147 | -------------------------------------------------------------------------------- /13-design-patterns/slides/observer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Observer (Наблюдатель) 3 | 4 | Определяет зависимость типа один ко многим между объектами таким образом, что при изменении состояния одного объекта 5 | все зависящие от него оповещаются об этом событии. 6 | 7 | При реализации шаблона «наблюдатель» обычно используются следующие классы: 8 | Observable — интерфейс, определяющий методы для добавления, удаления и оповещения наблюдателей; 9 | Observer — интерфейс, с помощью которого наблюдатель получает оповещение; 10 | ConcreteObservable — конкретный класс, который реализует интерфейс Observable; 11 | ConcreteObserver — конкретный класс, который реализует интерфейс Observer. 12 | 13 | Шаблон «наблюдатель» применяется в тех случаях, когда система обладает следующими свойствами: 14 | * существует, как минимум, один объект, рассылающий сообщения; 15 | * имеется не менее одного получателя сообщений, причём их количество и состав могут изменяться 16 | во время работы приложения; 17 | * нет надобности очень сильно связывать взаимодействующие объекты, что полезно для повторного использования. 18 | 19 | Данный шаблон часто применяют в ситуациях, в которых отправителя сообщений не интересует, 20 | что делают получатели с предоставленной им информацией. 21 | """ 22 | 23 | from abc import ABC, abstractmethod 24 | 25 | 26 | class Observer(ABC): 27 | """ 28 | Абстрактный наблюдатель 29 | """ 30 | 31 | @abstractmethod 32 | def update(self, message: str) -> None: 33 | """ 34 | Получение нового сообщения 35 | """ 36 | pass 37 | 38 | 39 | class Observable(ABC): 40 | """ 41 | Абстрактный наблюдаемый 42 | """ 43 | 44 | def __init__(self) -> None: 45 | self.observers = list() # инициализация списка наблюдателей 46 | 47 | def subscribe(self, observer: Observer) -> None: 48 | """ 49 | Регистрация нового наблюдателя на подписку 50 | """ 51 | self.observers.append(observer) 52 | 53 | def unsubscribe(self, observer: Observer) -> None: 54 | """ 55 | Регистрация нового наблюдателя на подписку 56 | """ 57 | if observer in self.observers: 58 | self.observers.remove(observer) 59 | 60 | def notify_observers(self, message: str) -> None: 61 | """ 62 | Передача сообщения всем наблюдателям, подписанным на события 63 | данного объекта наблюдаемого класса 64 | """ 65 | for observer in self.observers: 66 | observer.update(message) 67 | 68 | 69 | class Newspaper(Observable): 70 | """ 71 | Газета, за новостями которой следят тысячи людей 72 | """ 73 | 74 | def add_news(self, news: str) -> None: 75 | """ 76 | Выпуск очередной новости 77 | """ 78 | self.notify_observers(news) 79 | 80 | 81 | class Citizen(Observer): 82 | """ 83 | Гражданин, который любит читать газету 84 | """ 85 | 86 | def __init__(self, name: str) -> None: 87 | """ 88 | Constructor. 89 | 90 | :param name: имя гражданина, чтобы не спутать его с кем-то другим 91 | """ 92 | self.name = name 93 | 94 | def update(self, message: str) -> None: 95 | """ 96 | Получение очередной новости 97 | """ 98 | print(f"{self.name} узнал следующее: {message}") 99 | 100 | 101 | if __name__ == "__main__": 102 | newspaper = Newspaper() # Создаем газету 103 | 104 | # Добавляем двух человек 105 | ivan = Citizen("Иван") 106 | maria = Citizen("Мария") 107 | 108 | # Оформим подписку на газету 109 | newspaper.subscribe(ivan) 110 | newspaper.subscribe(maria) 111 | 112 | # Вбрасываем газетную утку 113 | newspaper.add_news("Пришельцы прилетели на Землю и посеяли пшеницу") 114 | 115 | # Один гражданин решил отписаться от таких новостей 116 | newspaper.unsubscribe(maria) 117 | 118 | # Выпустим опровержение 119 | newspaper.add_news("Опровержение! Пришельцев не было, пшеница сама выросла") 120 | -------------------------------------------------------------------------------- /13-design-patterns/slides/singleton.py: -------------------------------------------------------------------------------- 1 | class Singleton: 2 | __instance = None 3 | 4 | def __init__(self): 5 | if Singleton.__instance is None: 6 | Singleton.__instance = self 7 | else: 8 | raise Exception("The class is a singleton") 9 | 10 | @staticmethod 11 | def get_instance(): 12 | if Singleton.__instance is None: 13 | return Singleton() 14 | return Singleton.__instance 15 | 16 | @staticmethod 17 | def useful_function(): 18 | return 42 19 | 20 | 21 | if __name__ == "__main__": 22 | print(Singleton()) 23 | # print(Singleton()) # Второй вызов констуктора выдаст ошибку 24 | print(Singleton.get_instance()) 25 | print(Singleton.get_instance()) 26 | print(Singleton.get_instance().useful_function()) 27 | -------------------------------------------------------------------------------- /13-design-patterns/slides/singleton_decorator.py: -------------------------------------------------------------------------------- 1 | def singleton(class_): 2 | instances = {} 3 | 4 | def get_instance(*args, **kwargs): 5 | if class_ not in instances: 6 | instances[class_] = class_(*args, **kwargs) 7 | return instances[class_] 8 | 9 | return get_instance 10 | 11 | 12 | @singleton 13 | class UsefulClass: 14 | pass 15 | 16 | 17 | instance_1 = UsefulClass() 18 | instance_2 = UsefulClass() 19 | instance_3 = UsefulClass() 20 | 21 | print(instance_1 is instance_2 is instance_3) # True 22 | -------------------------------------------------------------------------------- /14-data-persistence/20190517.PythonDataPersistence.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smirnoffmg/EpamPython2019/724857adb941d94573c56f57d04dd8e440b3c128/14-data-persistence/20190517.PythonDataPersistence.pptx -------------------------------------------------------------------------------- /99-template-dir/README.md: -------------------------------------------------------------------------------- 1 | # Структура папки с материалами 2 | 3 | - В ```hw``` содержатся файлы задания 4 | - В ```slides``` содержатся презентация и дополнительные материалы -------------------------------------------------------------------------------- /99-template-dir/hw/README.md: -------------------------------------------------------------------------------- 1 | # Задачи к лекции 2 | 3 | Одна папка - одна задача. -------------------------------------------------------------------------------- /99-template-dir/hw/task1/example.py: -------------------------------------------------------------------------------- 1 | """ 2 | В начале файла содержится текст задания 3 | """ 4 | 5 | 6 | def test_some_function(*args, **kwargs): 7 | """ 8 | Некоторые дополнительные пояснения, если они нужны и касаются именно тела функции 9 | """ 10 | return 0 11 | 12 | 13 | # Тесты для проверки 14 | assert test_some_function() == 0 15 | assert test_some_function() == -1 16 | -------------------------------------------------------------------------------- /99-template-dir/slides/README.md: -------------------------------------------------------------------------------- 1 | # Презентации и наглядные материалы 2 | 3 | Если возможно, лучше оставлять презентации во всех доступных форматах. 4 | 5 | Джентельменский набор выглядит так: 6 | 7 | - ```pdf``` 8 | - ```ppt``` 9 | - ```pptx``` 10 | - ```ipynb``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EPAM Python Course 2019 2 | 3 | > Лекционные и методические материалы для слушателей курса Python в СПб 4 | 5 | ## Как работать с репозиторием 6 | 7 | - Склонировать 8 | - Выполнить задание 9 | - Прислать ответственному преподавателю ссылку на свой репозиторий с решениями 10 | 11 | ## Как компоновать материалы 12 | 13 | В папке ```99-template-dir``` лежит пример. 14 | 15 | ## Контакты 16 | 17 | Уважаемые лекторы, добавьте себя в этот список. 18 | 19 | - Максим Смирнов (maksim_smirnov@epam.com) 20 | - Даниил Шадрин (daniil_shadrin@epam.com) 21 | - Виктория Латынина (victoria_latynina@epam.com) 22 | - Андрей Шулаев (andrei_shulaev@epam.com) 23 | - Алексей Романов (aleksei_romanov@epam.com) 24 | --------------------------------------------------------------------------------