├── .gitignore ├── C00-Course-Introduction ├── 01-C00V01 │ └── README.md ├── 02-C00V02 │ └── README.md ├── 03-C00V03 │ └── README.md ├── 04-C00V04 │ └── README.md └── README.md ├── C01-Python-Basics ├── 01-C01V01 │ └── README.md ├── 02-C01V02 │ └── README.md ├── 03-C01V03 │ ├── README.md │ └── examples.py ├── 04-C01V04 │ ├── README.md │ └── examples.py ├── 05-C01V05 │ ├── README.md │ └── examples.py ├── 06-C01V06 │ └── README.md ├── 07-C01V07 │ └── README.md ├── 08-C01V08 │ ├── README.md │ └── examples.py ├── 09-C01V09 │ ├── README.md │ └── examples.py ├── 10-C01V10 │ ├── README.md │ └── examples.py ├── 11-C01V11 │ └── README.md ├── 12-C01P01 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 13-C01P02 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 14-C01P03 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 15-C01P04 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 16-C01P05 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 17-C01P06 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 18-C01P07 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 19-C01P08 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 20-C01P09 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 21-C01P10 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 22-C01P11 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 23-C01P12 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 24-C01P13 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 25-C01P14 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 26-C01P15 │ ├── README.md │ ├── Solution01 │ │ └── README.md │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 27-C01P16 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 28-C01P17 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ └── Solution02 │ │ ├── README.md │ │ └── solution.py ├── 29-C01P18 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 30-C01P19 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py └── README.md ├── C02-Linux-and-Python-Setup ├── 01-C02V01 │ └── README.md ├── 02-C02V02 │ └── README.md ├── 03-C02V03 │ └── README.md ├── 04-C02V04 │ └── README.md ├── 05-C02P01 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 06-C02V05 │ ├── README.md │ ├── example.py │ └── ls.py ├── 07-C02V06 │ ├── README.md │ └── example.py ├── 08-C02V07 │ ├── README.md │ ├── combined.py │ ├── poem.txt │ ├── stderr.py │ ├── stdin.py │ ├── stdout.py │ ├── stdout_print.py │ └── stdout_stderr.py ├── 09-C02V08 │ └── README.md ├── 10-C02V09 │ ├── README.md │ ├── false.py │ └── true.py ├── 11-C02V10 │ └── README.md ├── 12-C02P02 │ ├── README.md │ ├── Solution01 │ │ ├── README.md │ │ └── solution.py │ ├── Solution02 │ │ ├── README.md │ │ └── solution.py │ ├── example.json │ ├── gson.py │ └── weird.json ├── 13-C02V11 │ └── README.md ├── 14-C02V12 │ └── README.md ├── 15-C02V13 │ └── README.md └── README.md ├── C03-Unit-Testing ├── 01-C03V01 │ ├── README.md │ └── interval.py ├── 02-C03P01 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ └── solution.py ├── 03-C03V02 │ └── README.md ├── 04-C03V03 │ ├── README.md │ ├── interval.py │ └── interval_tests.py ├── 05-C03P02 │ ├── README.md │ └── Solution01 │ │ ├── README.md │ │ ├── interval.py │ │ └── interval_tests.py ├── 06-C03V04 │ ├── README.md │ ├── context_manager.py │ ├── context_manager_exception.py │ └── resources.py ├── 07-C03P03 │ ├── README.md │ └── Solution01 │ │ └── README.md ├── 08-C03V05 │ ├── README.md │ ├── call_stack.py │ ├── exceptions.py │ ├── flow.py │ └── try_catch.py ├── 09-C03V06 │ ├── README.md │ ├── hierarchy.py │ └── multiple.py ├── 10-C03V07 │ ├── README.md │ ├── finally.py │ └── reraising.py ├── 11-C03P04 │ ├── README.md │ ├── Solution01 │ │ └── README.md │ └── fraction.py ├── 12-C03V08 │ └── README.md ├── 13-C03V09 │ └── README.md ├── 14-C03P05 │ ├── README.md │ └── template.py ├── 15-C03V10 │ └── README.md ├── 16-C03V11 │ └── README.md ├── 17-C03V12 │ └── README.md ├── 18-C03V13 │ └── README.md ├── 19-C03V14 │ └── README.md ├── 20-C03P06 │ └── README.md ├── 21-C03V15 │ ├── README.md │ └── utils.py ├── 22-C03V16 │ ├── README.md │ ├── a_folder │ │ └── main.py │ └── utils.py ├── 23-C03V17 │ ├── README.md │ └── some_module.py ├── 24-C03V18 │ └── README.md ├── 25-C03V19 │ └── README.md └── README.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /C00-Course-Introduction/01-C00V01/README.md: -------------------------------------------------------------------------------- 1 | # C00V01 - What is Python 101 Forever? 2 | 3 | | Video | Slides | Next | 4 | |-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|-------------------------| 5 | | [Click here](https://www.youtube.com/watch?v=HE4M_0ZskqA) | [Click here](https://docs.google.com/presentation/d/116-UNxA3BzkeyCuS9K9oB01PgoTYkJ4ivpwjokx7YfI/edit?usp=sharing) | [C00V02](../02-C00V02/) | 6 | -------------------------------------------------------------------------------- /C00-Course-Introduction/02-C00V02/README.md: -------------------------------------------------------------------------------- 1 | # C00V02 - How is the course organized? 2 | 3 | | Video | Slides | Next | 4 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 5 | | [Click here](https://youtu.be/nLdDtkYX8cw) | [Click here](https://docs.google.com/presentation/d/1cp32LAzCgjbGB4fe6RsLSErlBm_D-i-urKjtZ7LapTM/edit?usp=sharing) | [C00V03](../03-C00V03/) | 6 | -------------------------------------------------------------------------------- /C00-Course-Introduction/03-C00V03/README.md: -------------------------------------------------------------------------------- 1 | # C00V03 - Who is behind the course? 2 | 3 | | Video | Slides | Next | 4 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 5 | | [Click here](https://youtu.be/AukAFZqz2Kk) | [Click here](https://docs.google.com/presentation/d/12_yKeCXd0W-ulSUWISI1yO7clOImHjBgNppVVBC7jtY/edit?usp=sharing) | [C00V04](../04-C00V04/) | 6 | -------------------------------------------------------------------------------- /C00-Course-Introduction/04-C00V04/README.md: -------------------------------------------------------------------------------- 1 | # C00V04 - How to take this course? 2 | 3 | | Video | Slides | Next | 4 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 5 | | [Click here](https://youtu.be/twbTRv9WBZA) | [Click here](https://docs.google.com/presentation/d/1ku12e7O4iQfqTQgYHvvPjiJr7byf6NN3mSwf-nKpxvo/edit?usp=sharing) | *Coming soon* | 6 | -------------------------------------------------------------------------------- /C00-Course-Introduction/README.md: -------------------------------------------------------------------------------- 1 | # C00 - Course Introduction 2 | 3 | Follow this order: 4 | 5 | | Order | Name | Link | 6 | |-------|---------------------------------------|--------------------------| 7 | | 1 | C00V01 - What is Python 101 Forever? | [Click here](01-C00V01/) | 8 | | 2 | C00V02 - How is the course organized? | [Click here](02-C00V02/) | 9 | | 3 | C00V03 - Who is behind the course? | [Click here](03-C00V03/) | 10 | | 4 | C00V04 - How to take this course? | [Click here](04-C00V04/) | 11 | -------------------------------------------------------------------------------- /C01-Python-Basics/01-C01V01/README.md: -------------------------------------------------------------------------------- 1 | # C01V01 - Basic Language Constructs 2 | 3 | We recommend checking this out - 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/NCQIXCstvqI) | [Click here](https://docs.google.com/presentation/d/1ue99wJqCWw0enGEw7k6a4sydiwKDbLXpbwN8_8Z4wCg/edit?usp=sharing) | [C01V02 - REPL](../02-C01V02/) | 8 | -------------------------------------------------------------------------------- /C01-Python-Basics/02-C01V02/README.md: -------------------------------------------------------------------------------- 1 | # C01V02 - REPL 2 | 3 | 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/Hz5Dkb8F1Rc) | [Click here](https://docs.google.com/presentation/d/1kV1F_bkpIo8vTkn6qR1LSQGvSvNID-dctONw0pWeo-I/edit?usp=sharing) | [C01V03 - If statements](../03-C01V03/) | 8 | -------------------------------------------------------------------------------- /C01-Python-Basics/03-C01V03/README.md: -------------------------------------------------------------------------------- 1 | # C01V03 - If statements 2 | 3 | Check the [examples](./examples.py). 4 | 5 | 6 | | Video | Slides | Next | 7 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 8 | | [Click here](https://youtu.be/3w7r33eagKc) | [Click here](https://docs.google.com/presentation/d/17wObxImPrXhWvaVmp-FeFDXBAylpCuhiJErpVB-y7Io/edit?usp=sharing) | [C01V04 - Lists](../04-C01V04/) | 9 | 10 | If you want to watch more on logic, check this video - 11 | -------------------------------------------------------------------------------- /C01-Python-Basics/03-C01V03/examples.py: -------------------------------------------------------------------------------- 1 | if True: 2 | pass 3 | 4 | 5 | if False: 6 | pass 7 | 8 | 9 | expression = (2 + 1) * 5 10 | 11 | 12 | # Implicit conversion 13 | # if bool(expression): 14 | if expression: 15 | pass 16 | 17 | 18 | if expression == 123: 19 | pass 20 | 21 | 22 | if expression is True: 23 | pass 24 | 25 | 26 | a_boolean = True 27 | 28 | 29 | if a_boolean: 30 | pass 31 | 32 | 33 | # More explicit way 34 | if a_boolean is True: 35 | pass 36 | 37 | 38 | # null value 39 | a_null = None 40 | 41 | 42 | if a_null: 43 | pass 44 | 45 | 46 | if not a_null: 47 | pass 48 | 49 | 50 | # More explicit way 51 | if a_null is None: 52 | pass 53 | 54 | 55 | # More explicit way 56 | if a_null is not None: 57 | pass 58 | 59 | 60 | # Falsy values 61 | 62 | if []: 63 | pass 64 | 65 | 66 | if {}: 67 | pass 68 | 69 | 70 | if 0: 71 | pass 72 | else: 73 | pass 74 | 75 | 76 | if '': 77 | pass 78 | 79 | 80 | today = 'Monday' 81 | hour = '15:00' 82 | 83 | 84 | if today == 'Monday': 85 | pass 86 | elif today == 'Tuesday': 87 | pass 88 | elif today == 'Wednesday': 89 | pass 90 | else: 91 | pass 92 | 93 | 94 | if today == 'Monday': 95 | if hour == '15:00': 96 | pass 97 | else: 98 | pass 99 | else: 100 | pass 101 | -------------------------------------------------------------------------------- /C01-Python-Basics/04-C01V04/README.md: -------------------------------------------------------------------------------- 1 | # C01V04 - Lists 2 | 3 | Check the [examples](./examples.py). 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/qpO5LuBFK8U) | [Click here](https://docs.google.com/presentation/d/1JogZsMCza1-9QpNasbQ3gH3ZWkq85KPShDOKRJWlQiM/edit?usp=sharing) | [C01V05 - Tuples](../05-C01V05/) | 8 | -------------------------------------------------------------------------------- /C01-Python-Basics/04-C01V04/examples.py: -------------------------------------------------------------------------------- 1 | languages = [] 2 | 3 | languages.append("Java") 4 | languages.append("Python") 5 | languages.append("C#") 6 | languages.append("Ruby") 7 | 8 | print(languages) 9 | 10 | numbers = [1, 2, 3] 11 | 12 | imdb_top_3 = [ 13 | "The Shawshank Redemption", 14 | "The Godfather", 15 | "The Godfather: Part II" 16 | ] 17 | 18 | print(imdb_top_3) 19 | 20 | empty = [] 21 | mixed = [1, True, "Three", [], None] 22 | 23 | print(mixed) 24 | 25 | # len is a "built-in" function 26 | # more on them, here - https://docs.python.org/3/library/functions.html 27 | if len(empty) == 0: 28 | print("Empty list check with len") 29 | 30 | # bool([]) == False 31 | # bool([1]) == True 32 | if numbers: 33 | print("Numbers is non-empty") 34 | 35 | 36 | if not empty: 37 | print("empty is empty") 38 | 39 | 40 | # How to check if a given value exists in a list 41 | if 1 in numbers: 42 | print("1 is in numbers") 43 | 44 | 45 | if 10 not in numbers: 46 | print("10 is not in numbers") 47 | 48 | 49 | for n in numbers: 50 | print(n) 51 | 52 | 53 | for movie in imdb_top_3: 54 | print(movie) 55 | 56 | 57 | for nothing in empty: 58 | print(nothing) 59 | 60 | 61 | for item in mixed: 62 | print(item) 63 | 64 | 65 | # Lists have standard index access 66 | print(numbers[0]) 67 | 68 | # Lists can be mutated 69 | print(numbers) 70 | numbers[0] = 111 71 | print(numbers) 72 | 73 | print(empty) 74 | empty.append("Something") 75 | print(empty) 76 | 77 | 78 | # We can easily compare lists 79 | # Two lists xs & ns are equal, if 80 | # xs[i] == ns[i] for every index i of that lists 81 | # or if both xs & ns are empty 82 | print([] == []) 83 | print([1] == []) 84 | print([1, 2] == [2, 1]) 85 | -------------------------------------------------------------------------------- /C01-Python-Basics/05-C01V05/README.md: -------------------------------------------------------------------------------- 1 | # C01V05 - Tuples 2 | 3 | Check the [examples](./examples.py). 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/LQ13LFGJ7mY) | [Click here](https://docs.google.com/presentation/d/1d_2AXV3uRTtMwmdUZfH2hLVfj1ve93DZk2hS8LU8PJA/edit?usp=sharing) | [C01V06 - Dictionaries](../06-C01V06/) | 8 | -------------------------------------------------------------------------------- /C01-Python-Basics/05-C01V05/examples.py: -------------------------------------------------------------------------------- 1 | # Tuples - immutable lists with fixed size 2 | 3 | a_list = [1, 2, 3] 4 | a_tuple = (1, 2, 3) 5 | a_tuple_with_one_element = (1,) 6 | 7 | # We cannot mutate a tuple: 8 | # a_tuple[0] = 111 9 | 10 | # We cannot add elements to a tuple 11 | # a_tuple.append(111) 12 | 13 | # We can omit the () 14 | another_tuple = 1, 2, 3 15 | 16 | print(a_tuple) 17 | print(another_tuple) 18 | 19 | print(a_tuple == another_tuple) 20 | 21 | 22 | # We can loop in a standard manner over a tuple 23 | for x in a_tuple: 24 | print(x) 25 | 26 | 27 | print(1 in (1, 2, 3)) # True 28 | print(1 in (2, 3)) # False 29 | print(1 in ()) # False 30 | print(1 not in ()) # True 31 | 32 | 33 | print((1) == (1,)) # False 34 | -------------------------------------------------------------------------------- /C01-Python-Basics/06-C01V06/README.md: -------------------------------------------------------------------------------- 1 | # C01V06 - Dictionaries 2 | 3 | | Video | Slides | Next | 4 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 5 | | [Click here](https://youtu.be/ReT4wSYBezA) | [Click here](https://docs.google.com/presentation/d/1k3UQsTbjOlXzYgliGTWwV63aY9GKAbk2UL1xW1o0hoM/edit?usp=sharing) | [C01V07 - Sets](../07-C01V07/) | 6 | 7 | Also watch this - [Raymond Hettinger Modern Python Dictionaries A confluence of a dozen great ideas PyCon 2017 8 | ](https://www.youtube.com/watch?v=npw4s1QTmPg) 9 | -------------------------------------------------------------------------------- /C01-Python-Basics/07-C01V07/README.md: -------------------------------------------------------------------------------- 1 | # C01V07 - Sets 2 | 3 | | Video | Slides | Next | 4 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 5 | | [Click here](https://youtu.be/Y_tbtTBwUUQ) | [Click here](https://docs.google.com/presentation/d/1n-m4fEzR6htP12oE9t7Vy8GYRJ4xdNeSjaSZxEpuscY/edit?usp=sharing) | [C01V08 - Common Patterns](../08-C01V08/) | 6 | -------------------------------------------------------------------------------- /C01-Python-Basics/08-C01V08/README.md: -------------------------------------------------------------------------------- 1 | # C01V08 - Common patterns 2 | 3 | Check the [examples](./examples.py). 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/g3_weEKkdh8) | [Click here](https://docs.google.com/presentation/d/1mZm6uwqVdaMldnKtveOownR1w_JnUKq3jsy86MJISdo/edit?usp=sharing) | [C01V09 - Functions](../09-C01V09/) | 8 | 9 | -------------------------------------------------------------------------------- /C01-Python-Basics/08-C01V08/examples.py: -------------------------------------------------------------------------------- 1 | # 1. Multiple assignments 2 | 3 | x, y = (1, 2) 4 | print(x, y) 5 | 6 | x, y = [1, 2] 7 | print(x, y) 8 | 9 | x, y = {'key1': 'value1', 'key2': 'value2'} 10 | print(x, y) 11 | 12 | x, y = {1, 2} 13 | print(x, y) 14 | 15 | 16 | d = { 17 | 'HackSoft': 'https://hacksoft.io', 18 | 'HackBulgaria': 'https://hackbulgaria.com', 19 | 'HackConf': 'https://hackconf.bg' 20 | } 21 | 22 | for name, url in d.items(): 23 | print(name, url) 24 | 25 | 26 | for a, b, c in [(1, 2, 3), (4, 5, 6)]: 27 | print(a, b, c) 28 | 29 | 30 | identifiers = [ 31 | "b37fe64cd6b046d699c6aab1cb989486", 32 | "0016468647b84e7db9187356517c14b0", 33 | "1074f7a611a1475e8530c0053cd94203" 34 | ] 35 | 36 | for index, identifier in enumerate(identifiers): 37 | print(index, identifier) 38 | 39 | 40 | # x, y, z = (1, 2) # ValueError 41 | # x, y = (1,) # ValueError 42 | # x, y = (1, 2, 3) # ValueError 43 | 44 | 45 | # 2. While loops 46 | 47 | index = 0 48 | n = 4 49 | 50 | while index < n: 51 | print(index) 52 | index = index + 1 53 | 54 | if index == 3: 55 | break 56 | else: 57 | continue 58 | 59 | 60 | # 3. String formatting 61 | 62 | a = "is" 63 | b = "pretty well" 64 | 65 | f_string = f"this string {a} formatted {b}" 66 | format_method = "this string {a} formatted {b}".format(a=a, b=b) 67 | 68 | print(f_string) 69 | print(format_method) 70 | 71 | words = ["A", "list", "of", "strings"] 72 | print(" ".join(words)) 73 | -------------------------------------------------------------------------------- /C01-Python-Basics/09-C01V09/README.md: -------------------------------------------------------------------------------- 1 | # C01V08 - Functions 2 | 3 | Check the [examples](./examples.py). 4 | 5 | | Video | Slides | Next | 6 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 7 | | [Click here](https://youtu.be/BReeG8qsMRk) | [Click here](https://docs.google.com/presentation/d/1vUXduhZpVshS5N0zArJ37sKdCqrG5PzJgjmtjrxP_I4/edit?usp=sharing) | [C01V10 - Pass by assignment](../10-C01V10/) | 8 | 9 | -------------------------------------------------------------------------------- /C01-Python-Basics/09-C01V09/examples.py: -------------------------------------------------------------------------------- 1 | def function_name_using_snake_case(argument1, argument2): 2 | print(argument1, argument2) 3 | 4 | return 42 5 | 6 | 7 | result = function_name_using_snake_case(1, 2) 8 | 9 | 10 | def function_without_arguments_or_body(): 11 | pass 12 | 13 | 14 | result = function_without_arguments_or_body() # None 15 | 16 | 17 | a = 1 18 | b = 2 19 | 20 | 21 | def f(a, b): 22 | print(a, b) # 3, 4 23 | 24 | return a + b 25 | 26 | 27 | result = f(3, 4) # 7 28 | 29 | 30 | def g(): 31 | print(a, b) # 1, 2 32 | 33 | return a + b 34 | 35 | 36 | result = g() # 3 37 | 38 | 39 | def sum1(xs): 40 | result = 0 41 | 42 | for x in xs: 43 | result += x 44 | 45 | return result 46 | 47 | 48 | result = sum1([1, 2, 3]) 49 | 50 | 51 | def sum2(*xs): 52 | result = 0 53 | 54 | for x in xs: 55 | result += x 56 | 57 | return result 58 | 59 | 60 | result = sum2(1, 2, 3) 61 | also_result = sum2(*[1, 2, 3]) 62 | 63 | 64 | def keys1(d): 65 | result = [] 66 | 67 | for key in d: 68 | result.append(key) 69 | 70 | return result 71 | 72 | 73 | # ['a', 'b'] 74 | result = keys1( 75 | { 76 | 'a': 1, 77 | 'b': 2 78 | } 79 | ) 80 | 81 | 82 | def keys2(**d): 83 | result = [] 84 | 85 | for key in d: 86 | result.append(key) 87 | 88 | return result 89 | 90 | 91 | # ['a', 'b'] 92 | result = keys2(a=1, b=2) 93 | 94 | d = {'a': 1, 'b': 2} 95 | # ['a', 'b'] 96 | also_result = keys2(**d) 97 | -------------------------------------------------------------------------------- /C01-Python-Basics/10-C01V10/README.md: -------------------------------------------------------------------------------- 1 | # C01V10 - Pass by assignment 2 | 3 | 1. 4 | 1. 5 | 1. This video has a nice visual explanation - 6 | 1. Check the [examples](./examples.py). 7 | 8 | | Video | Slides | Next | 9 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 10 | | [Click here](https://youtu.be/A9gtsfGgjvI) | [Click here](https://docs.google.com/presentation/d/1dEQ7LCp9DSC3v9xuJ0e-OrjM2ZGFQygPTmTfqq4VCZ0/edit?usp=sharing) | [C01V11 - How to write the actual Python code](../11-C01V11/) | 11 | -------------------------------------------------------------------------------- /C01-Python-Basics/10-C01V10/examples.py: -------------------------------------------------------------------------------- 1 | a = 1 2 | b = a 3 | 4 | print( 5 | id(a) == id(b) # True 6 | ) 7 | 8 | a = 2 9 | print( 10 | id(a) == id(b) # False 11 | ) 12 | 13 | print(a, b) # 2, 1 14 | 15 | 16 | a = [1, 2, 3] 17 | b = a 18 | 19 | print( 20 | id(a) == id(b) # True 21 | ) 22 | 23 | a = [3, 4, 5] 24 | print( 25 | id(a) == id(b) # False 26 | ) 27 | 28 | print(a, b) # [3, 4, 5], [1, 2, 3] 29 | 30 | a = [1, 2, 3] 31 | b = a 32 | 33 | print( 34 | id(a) == id(b) # True 35 | ) 36 | 37 | b.append(4) 38 | 39 | print( 40 | id(a) == id(b) # True 41 | ) 42 | 43 | print(a) # [1, 2, 3, 4] 44 | print(b) # [1, 2, 3, 4] 45 | 46 | 47 | a = [1, 2, 3] 48 | b = [1, 2, 3] 49 | 50 | print( 51 | a == b # True 52 | ) 53 | 54 | print( 55 | a is b # False 56 | ) 57 | 58 | print( 59 | id(a) == id(b) # False 60 | ) 61 | 62 | a = True 63 | b = True 64 | 65 | print( 66 | a == b # True 67 | ) 68 | 69 | print( 70 | a is b # True 71 | ) 72 | 73 | print( 74 | id(a) == id(b) # True 75 | ) 76 | -------------------------------------------------------------------------------- /C01-Python-Basics/11-C01V11/README.md: -------------------------------------------------------------------------------- 1 | # C01V11 - How to write the actual Python code 2 | 3 | You can download Sublime from here - 4 | 5 | Sublime settings: 6 | 7 | ```json 8 | { 9 | "translate_tabs_to_spaces": true, 10 | "highlight_line": true, 11 | "draw_white_space": "all", 12 | "font_size": 24 13 | } 14 | ``` 15 | 16 | | Video | Slides | Next | 17 | |--------------------------------------------|--------------------------------------------------------------------------------------------------------------------|---------------| 18 | | [Click here](https://youtu.be/33Kc0-EXjOs) | No slides this time. | [C01P01 - IBAN formatter](../12-C01P01/)| 19 | 20 | -------------------------------------------------------------------------------- /C01-Python-Basics/12-C01P01/README.md: -------------------------------------------------------------------------------- 1 | # C01P01 - IBAN formatter 2 | 3 | Someone is emailing you an invoice. You grab the "windows machine" and open up the online banking. 4 | 5 | It's time to type the provided IBAN. And to be frank - IBANs are really hard to read. 6 | 7 | You get something like that: 8 | 9 | ``` 10 | BG80BNBG96611020345678 11 | ``` 12 | 13 | A bunch of numbers and letters that are hard to look at & type elsewhere. 14 | 15 | But if you somehow manage to get the IBAN to look like that: 16 | 17 | ``` 18 | BG80 BNBG 9661 1020 3456 78 19 | ``` 20 | 21 | It's quite easy to type that elsewhere, because everyting is split in groups of 4 numbers & there's nice whitespace. 22 | 23 | **Your task is to implement the following Python function:** 24 | 25 | ```python 26 | def iban_formatter(iban): 27 | pass 28 | ``` 29 | 30 | 1. The function takes a string (an IBAN) and returns the IBAN in the formatted way showed above. 31 | 1. Keep in mind that the IBAN can be already formatted & the function should take care of that too. 32 | 33 | **Examples:** 34 | 35 | ```python 36 | iban_formatter("BG80BNBG96611020345678") == "BG80 BNBG 9661 1020 3456 78" 37 | iban_formatter("BG80 BNBG 9661 1020 3456 78") == "BG80 BNBG 9661 1020 3456 78" 38 | iban_formatter("BG14TTBB94005362446381") == "BG14 TTBB 9400 5362 4463 81" 39 | iban_formatter("BG91UNCR70001864961754") == "BG91 UNCR 7000 1864 9617 54" 40 | ``` 41 | 42 | **Hints:** 43 | 44 | 1. Think of `''.join(xs)` - 45 | 1. Think of `string.replace(x, y)` - 46 | -------------------------------------------------------------------------------- /C01-Python-Basics/12-C01P01/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P01S01 - IBAN formatter Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check [the video for the solution](https://youtu.be/t7j64JYma6o) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/12-C01P01/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/t7j64JYma6o 2 | 3 | def iban_formatter(iban): 4 | result = [] 5 | counter = 1 6 | 7 | for character in iban: 8 | if character == ' ': 9 | continue 10 | 11 | result.append(character) 12 | 13 | if counter == 4: 14 | result.append(' ') 15 | 16 | counter = 0 17 | 18 | counter += 1 19 | 20 | return "".join(result) 21 | 22 | 23 | tests = [ 24 | ("BG80BNBG96611020345678", "BG80 BNBG 9661 1020 3456 78"), 25 | ("BG80 BNBG 9661 1020 3456 78", "BG80 BNBG 9661 1020 3456 78"), 26 | ("BG14TTBB94005362446381", "BG14 TTBB 9400 5362 4463 81"), 27 | ("BG14 TTBB 9400 5362 4463 81", "BG14 TTBB 9400 5362 4463 81"), 28 | ("BG91UNCR70001864961754", "BG91 UNCR 7000 1864 9617 54"), 29 | ("BG91 UNCR 7000 1864 9617 54", "BG91 UNCR 7000 1864 9617 54") 30 | ] 31 | 32 | for iban, expected in tests: 33 | result = iban_formatter(iban) 34 | print(f"'{expected}', '{result}'") 35 | print(result == expected) 36 | -------------------------------------------------------------------------------- /C01-Python-Basics/13-C01P02/README.md: -------------------------------------------------------------------------------- 1 | # C01P02 - Sum of digits 2 | 3 | Given an integer, implement a function, called `sum_of_digits(n)` that calculates the sum of n's digits. 4 | 5 | If a negative number is given, our function should work as if it was positive. 6 | 7 | **Here's the signature:** 8 | 9 | ```python 10 | def sum_of_digits(n): 11 | pass 12 | ``` 13 | 14 | **Examples:** 15 | 16 | ```python 17 | sum_of_digits(1325132435356) == 43 18 | sum_of_digits(123) == 6 19 | sum_of_digits(6) == 6 20 | sum_of_digits(-10) == 1 21 | ``` 22 | 23 | **Hints:** 24 | 25 | In Python, there is a special operator for integer division: 26 | 27 | ```python 28 | 5 / 2 == 2.5 29 | 5 // 2 == 2 30 | ``` 31 | 32 | There's also the standard modulo division: 33 | 34 | ```python 35 | 123 % 10 == 3 36 | ``` 37 | -------------------------------------------------------------------------------- /C01-Python-Basics/13-C01P02/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P02S01 - Sum of digits Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution.](https://youtu.be/PmW8NfctNpk) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/13-C01P02/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/PmW8NfctNpk 2 | def sum_of_digits(n): 3 | n = abs(n) 4 | digits = [] 5 | 6 | char_digits = list(str(n)) 7 | 8 | for char_digit in char_digits: 9 | digits.append(int(char_digit)) 10 | 11 | return sum(digits) 12 | 13 | 14 | tests = [ 15 | (1325132435356, 43), 16 | (123, 6), 17 | (6, 6), 18 | (-10, 1) 19 | ] 20 | 21 | for n, expected in tests: 22 | result = sum_of_digits(n) 23 | 24 | print(result == expected) 25 | -------------------------------------------------------------------------------- /C01-Python-Basics/13-C01P02/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P02S02 - Sum of digits Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/DB2noiynqfs) 5 | 1. [Check this article on module division (`%`)](https://www.freecodecamp.org/news/the-python-modulo-operator-what-does-the-symbol-mean-in-python-solved/) 6 | -------------------------------------------------------------------------------- /C01-Python-Basics/13-C01P02/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/DB2noiynqfs 2 | def sum_of_digits(n): 3 | n = abs(n) 4 | 5 | result = 0 6 | 7 | while n != 0: 8 | digit = n % 10 9 | result += digit 10 | 11 | n = n // 10 12 | 13 | return result 14 | 15 | 16 | tests = [ 17 | (1325132435356, 43), 18 | (123, 6), 19 | (6, 6), 20 | (-10, 1), 21 | (0, 0) 22 | ] 23 | 24 | for n, expected in tests: 25 | result = sum_of_digits(n) 26 | 27 | print(result == expected) 28 | -------------------------------------------------------------------------------- /C01-Python-Basics/14-C01P03/README.md: -------------------------------------------------------------------------------- 1 | # C01P03 - Factorial digits 2 | 3 | Implement a function `fact_digits(n)`, that takes an integer and returns the sum of the factorials of each digit of `n`. 4 | 5 | For example, if `n = 145`, we want `1! + 4! + 5!`. 6 | 7 | **Here's the signature:** 8 | 9 | ```python 10 | def fact_digits(n): 11 | pass 12 | ``` 13 | 14 | **Examples:** 15 | 16 | ```python 17 | fact_digits(101) == 3 18 | fact_digits(111) == 3 19 | fact_digits(145) == 145 20 | fact_digits(999) == 1088640 21 | ``` 22 | 23 | **Hints:** 24 | 25 | We've already solved a similiar problem. Think how you can use that to your advantage. 26 | -------------------------------------------------------------------------------- /C01-Python-Basics/14-C01P03/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P03S01 - Factorial digits Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/7gTOe3RbA1U) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/14-C01P03/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/7gTOe3RbA1U 2 | # C01P03 Solution - Factorial Digits 3 | 4 | # n! = n * (n - 1) * (n - 2) * ... * 1 5 | # 0! = 1 6 | # 1! = 1 7 | def fact(n): 8 | result = 1 9 | 10 | for x in range(1, n + 1): 11 | result *= x 12 | 13 | return result 14 | 15 | 16 | # xyz = x! + y! + z! (x + y + z) 17 | def fact_digits(n): 18 | n = abs(n) 19 | 20 | result = 0 21 | 22 | while n != 0: 23 | digit = n % 10 24 | result += fact(digit) 25 | 26 | n = n // 10 27 | 28 | return result 29 | 30 | 31 | tests = [ 32 | (5, 120), 33 | (101, 3), 34 | (111, 3), 35 | (145, 145), 36 | (999, 1088640) 37 | ] 38 | 39 | 40 | for n, expected in tests: 41 | result = fact_digits(n) 42 | 43 | print(result, result == expected) 44 | -------------------------------------------------------------------------------- /C01-Python-Basics/15-C01P04/README.md: -------------------------------------------------------------------------------- 1 | # C01P04 - Char histotgram 2 | 3 | Implement a funcion, called `char_histogram(string)`, which takes a string and returns a dictionary, where each key is a character from string and its value is the number of occurrences of that char in the string. 4 | 5 | **Here's the signature:** 6 | 7 | ```python 8 | def char_histogram(string): 9 | pass 10 | ``` 11 | 12 | **Examples:** 13 | 14 | ```python 15 | char_histogram("") == {} 16 | char_histogram(" ") = {' ': 4} 17 | char_histogram("Python!") == {'P': 1, 'y': 1, 't': 1, 'h': 1, 'o': 1, 'n': 1, '!': 1} 18 | char_histogram("AAAAaaa!!!") == {'A': 4, 'a': 3, '!': 3} 19 | char_histogram("Some very long string here with different casing") == 20 | { 21 | 'S': 1, 22 | 'o': 2, 23 | 'm': 1, 24 | 'e': 6, 25 | ' ': 7, 26 | 'v': 1, 27 | 'r': 4, 28 | 'y': 1, 29 | 'l': 1, 30 | 'n': 4, 31 | 'g': 3, 32 | 's': 2, 33 | 't': 3, 34 | 'i': 4, 35 | 'h': 2, 36 | 'w': 1, 37 | 'd': 1, 38 | 'f': 2, 39 | 'c': 1, 40 | 'a': 1 41 | } 42 | ``` 43 | 44 | **Hints:** 45 | 46 | 1. Membership access check: `key in dict` / `key not in dict` 47 | 1. Or you can use the `.get(key, default)` method: `{}.get(key, 0) == 0` 48 | -------------------------------------------------------------------------------- /C01-Python-Basics/15-C01P04/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P04S01 - Char histotgram Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/1ADoSQkGUW0) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/15-C01P04/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/1ADoSQkGUW0 2 | # C01P04 - Char histogram 3 | 4 | 5 | def char_histogram(string): 6 | result = {} 7 | 8 | for char in string: 9 | if char not in result: 10 | result[char] = 0 11 | 12 | result[char] = result[char] + 1 13 | 14 | return result 15 | 16 | 17 | # Alternative, using dict.get 18 | # def char_histogram(string): 19 | # result = {} 20 | 21 | # for char in string: 22 | # result[char] = result.get(char, 0) + 1 23 | 24 | # return result 25 | 26 | 27 | tests = [ 28 | ( 29 | "Python!", 30 | {'P': 1, 'y': 1, 't': 1, 'h': 1, 'o': 1, 'n': 1, '!': 1} 31 | ), 32 | ( 33 | "Some very long string here with different casing", 34 | { 35 | 'S': 1, 36 | 'o': 2, 37 | 'm': 1, 38 | 'e': 6, 39 | ' ': 7, 40 | 'v': 1, 41 | 'r': 4, 42 | 'y': 1, 43 | 'l': 1, 44 | 'n': 4, 45 | 'g': 3, 46 | 's': 2, 47 | 't': 3, 48 | 'i': 4, 49 | 'h': 2, 50 | 'w': 1, 51 | 'd': 1, 52 | 'f': 2, 53 | 'c': 1, 54 | 'a': 1 55 | } 56 | ), 57 | ( 58 | "AAAAaaa!!!", 59 | {'A': 4, 'a': 3, '!': 3} 60 | ), 61 | ( 62 | "", 63 | {} 64 | ), 65 | ( 66 | " ", 67 | {" ": 4} 68 | ) 69 | ] 70 | 71 | for s, expected in tests: 72 | result = char_histogram(s) 73 | 74 | print(result == expected) 75 | -------------------------------------------------------------------------------- /C01-Python-Basics/16-C01P05/README.md: -------------------------------------------------------------------------------- 1 | # C01P05 - Sum matrix 2 | 3 | You are given a NxM matrix of integer numbers. 4 | 5 | Implement a function, called `sum_matrix(m)` that returns the sum of all numbers in the matrix. 6 | 7 | The matrix will be represented as nested lists in Python. 8 | 9 | **Here's the signature:** 10 | 11 | ```python 12 | def sum_matrix(m): 13 | pass 14 | ``` 15 | 16 | **Examples:** 17 | 18 | ```python 19 | m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 20 | sum_matrix(m) == 45 21 | 22 | m = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 23 | sum_matrix(m) == 0 24 | 25 | m = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] 26 | sum_matrix(m) == 55 27 | ``` 28 | 29 | **Hints:** 30 | 31 | 1. You can easily nest for-loops. 32 | -------------------------------------------------------------------------------- /C01-Python-Basics/16-C01P05/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P05 - Sum matrix Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/0cgnxoqpZJY) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/16-C01P05/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/0cgnxoqpZJY 2 | # C01P05 - Sum matrix 3 | 4 | 5 | def sum_matrix(m): 6 | result = 0 7 | 8 | for row in m: 9 | for x in row: 10 | result += x 11 | 12 | return result 13 | 14 | 15 | tests = [ 16 | ( 17 | [], 18 | 0 19 | ), 20 | ( 21 | [[]], 22 | 0 23 | ), 24 | ( 25 | [ 26 | [1, 2, 3], 27 | [4, 5, 6], 28 | [7, 8, 9] 29 | ], 30 | 45 31 | ), 32 | ( 33 | [ 34 | [0, 0, 0], 35 | [0, 0, 0], 36 | [0, 0, 0] 37 | ], 38 | 0 39 | ), 40 | ( 41 | [ 42 | [1, 2], 43 | [3, 4], 44 | [5, 6], 45 | [7, 8], 46 | [9, 10] 47 | ], 48 | 55 49 | ) 50 | ] 51 | 52 | for m, expected in tests: 53 | result = sum_matrix(m) 54 | 55 | print(result == expected) 56 | -------------------------------------------------------------------------------- /C01-Python-Basics/17-C01P06/README.md: -------------------------------------------------------------------------------- 1 | # C01P06 - NaN expand 2 | 3 | In most programming languages, NaN stands for Not a Number. 4 | 5 | If we take a look at the following JavaScript code: 6 | 7 | ```javascript 8 | typeof NaN === 'number'; // true 9 | ``` 10 | 11 | We will see that in JavaScript, `NaN` stands for `Not a NaN`, which is recursive by nature. 12 | 13 | Implement a Python function, called `nan_expand(times)`, which returns the expansion of `NaN` (In JavaScript terms) that many `times`. 14 | 15 | **Here's the signature:** 16 | 17 | ```python 18 | def nan_expand(times): 19 | pass 20 | ``` 21 | 22 | **Examples:** 23 | 24 | 1. If we expand NaN once (`times=1`), we will have `"Not a NaN"` 25 | 1. If we expand NaN twice (`times=2`), we will have `"Not a Not a NaN"` 26 | 1. If `times=3`, we have `"Not a Not a Not a NaN"` 27 | 1. And so on ... 28 | 29 | ```python 30 | nan_expand(0) == "" 31 | nan_expand(1) == "Not a NaN" 32 | nan_expand(2) == "Not a Not a NaN" 33 | nan_expand(3) == "Not a Not a Not a NaN" 34 | ``` 35 | -------------------------------------------------------------------------------- /C01-Python-Basics/17-C01P06/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P06S01 - NaN expand Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/Powijm5n3sA) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/17-C01P06/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # C01P06 - NaN expand 2 | 3 | # NaN = Not a NaN, Not a Not a NaN 4 | def nan_expand(times): 5 | if times == 0: 6 | return "" 7 | 8 | parts = ["Not a"] * times 9 | parts.append("NaN") 10 | 11 | return ' '.join(parts) 12 | 13 | 14 | tests = [ 15 | (0, ""), 16 | (1, "Not a NaN"), 17 | (2, "Not a Not a NaN"), 18 | (3, "Not a Not a Not a NaN") 19 | ] 20 | 21 | for times, expected in tests: 22 | result = nan_expand(times) 23 | 24 | print(result == expected) 25 | -------------------------------------------------------------------------------- /C01-Python-Basics/17-C01P06/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P06S01 - NaN expand Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/g4Cyw889uyI) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/17-C01P06/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # C01P06 - NaN expand 2 | 3 | # NaN = Not a NaN, Not a Not a NaN 4 | def nan_expand(times): 5 | if times == 0: 6 | return "" 7 | 8 | result = "Not a NaN" 9 | 10 | if times == 1: 11 | return result 12 | 13 | while times > 1: 14 | result = result.replace("NaN", "Not a NaN") 15 | 16 | times -= 1 17 | 18 | return result 19 | 20 | 21 | tests = [ 22 | (0, ""), 23 | (1, "Not a NaN"), 24 | (2, "Not a Not a NaN"), 25 | (3, "Not a Not a Not a NaN") 26 | ] 27 | 28 | for times, expected in tests: 29 | result = nan_expand(times) 30 | 31 | print(result == expected) 32 | -------------------------------------------------------------------------------- /C01-Python-Basics/18-C01P07/README.md: -------------------------------------------------------------------------------- 1 | # C01P07 - Increasing and decreasing 2 | 3 | Implement a function, called `increasing_or_decreasing(ns)` where `ns` is a list of integers. 4 | 5 | The function should return a value from the following [enum](https://docs.python.org/3/library/enum.html): 6 | 7 | ```python 8 | from enum import Enum 9 | 10 | 11 | class Monotonicity(Enum): 12 | INCREASING = 1 13 | DECREASING = 2 14 | NONE = 3 15 | ``` 16 | 17 | 1. It should return `Monotonicity.INCREASING`, if for every two elements, `a` and `b`, that are next to each other, we have `a < b` 18 | 1. It should return `Monotonicity.DECREASING`, if for every two elements, `a` and `b`, that are next to each other, we have `a > b` 19 | 1. It should return `Monotonicity.NONE` if it's neither increasing nor decrasing. 20 | 21 | **Signature** 22 | 23 | ```python 24 | def increasing_or_decreasing(ns): 25 | pass 26 | ``` 27 | 28 | **Examples** 29 | 30 | ```python 31 | increasing_or_decreasing([1, 2, 3, 4, 5]) == Monotonicity.INCREASING 32 | increasing_or_decreasing([5, 6, -10]) == Monotonicity.NONE 33 | increasing_or_decreasing([1, 1, 1, 1]) == Monotonicity.NONE 34 | increasing_or_decreasing([9, 8, 7, 6]) == Monotonicity.DECREASING 35 | increasing_or_decreasing([]) == Monotonicity.NONE 36 | increasing_or_decreasing([1]) == Monotonicity.NONE 37 | increasing_or_decreasing([1, 100]) == Monotonicity.INCREASING 38 | increasing_or_decreasing([1, 100, 100]) == Monotonicity.NONE 39 | increasing_or_decreasing([100, 1]) == Monotonicity.DECREASING 40 | increasing_or_decreasing([100, 1, 1]) == Monotonicity.NONE 41 | increasing_or_decreasing([100, 1, 2]) == Monotonicity.NONE 42 | ``` 43 | -------------------------------------------------------------------------------- /C01-Python-Basics/18-C01P07/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P07S01 - Increasing and decreasing Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/-tFiDt3Mops) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/18-C01P07/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/-tFiDt3Mops 2 | from enum import Enum 3 | 4 | 5 | class Monotonicity(Enum): 6 | INCREASING = 1 7 | DECREASING = 2 8 | NONE = 3 9 | 10 | 11 | def increasing_or_decreasing(ns): 12 | # True + True, True + False, False + True, False + False 13 | # increasing = True and 1 < 2 and 2 < 3 and 3 < 4 and 4 < 5 14 | # decreasing = True and 1 > 2 and 2 > 3 and 3 > 4 and 4 > 5 15 | length = len(ns) 16 | 17 | if length <= 1: 18 | return Monotonicity.NONE 19 | 20 | increasing = True 21 | decreasing = True 22 | 23 | for index in range(length - 1): 24 | a = ns[index] 25 | b = ns[index + 1] 26 | 27 | increasing = increasing and a < b 28 | decreasing = decreasing and a > b 29 | 30 | if increasing: 31 | return Monotonicity.INCREASING 32 | 33 | if decreasing: 34 | return Monotonicity.DECREASING 35 | 36 | return Monotonicity.NONE 37 | 38 | 39 | tests = [ 40 | ([1, 2, 3, 4, 5], Monotonicity.INCREASING), 41 | ([5, 6, -10], Monotonicity.NONE), 42 | ([1, 1, 1, 1], Monotonicity.NONE), 43 | ([9, 8, 7, 6], Monotonicity.DECREASING), 44 | ([], Monotonicity.NONE), 45 | ([1], Monotonicity.NONE), 46 | ([1, 100], Monotonicity.INCREASING), 47 | ([1, 100, 100], Monotonicity.NONE), 48 | ([100, 1], Monotonicity.DECREASING), 49 | ([100, 1, 1], Monotonicity.NONE), 50 | ([100, 1, 2], Monotonicity.NONE), 51 | ] 52 | 53 | 54 | for ns, expected in tests: 55 | result = increasing_or_decreasing(ns) 56 | 57 | print(result == expected) 58 | -------------------------------------------------------------------------------- /C01-Python-Basics/19-C01P08/README.md: -------------------------------------------------------------------------------- 1 | # C01P08 - Group 2 | 3 | We are going to implement a very helpful function, called `group`. 4 | 5 | `group` takes a list of items and returns a list of groups, where each group is formed by **all equal consecutive elements in the list.** 6 | 7 | **Signature** 8 | 9 | ```python 10 | def group(items): 11 | pass 12 | ``` 13 | 14 | **Examples** 15 | 16 | ```python 17 | group([1, 1, 1, 2, 3, 1, 1]) == [[1, 1, 1], [2], [3], [1, 1]] 18 | group([1, 2, 1, 2, 3, 3]) == [[1], [2], [1], [2], [3, 3]] 19 | group([]) == [] 20 | group([1]) == [[1]] 21 | group([1, 1, 1, 1]) == [[1, 1, 1, 1]] 22 | ``` 23 | -------------------------------------------------------------------------------- /C01-Python-Basics/19-C01P08/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P08S01 - Group Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/EzP6vfASVo0) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/19-C01P08/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/EzP6vfASVo0 2 | 3 | def group(items): 4 | result = [] 5 | length = len(items) 6 | index = 0 7 | 8 | while index < length: 9 | item = items[index] 10 | current_group = [item] 11 | 12 | lookup_index = index + 1 13 | 14 | while lookup_index < length and items[lookup_index] == item: 15 | current_group.append(items[lookup_index]) 16 | lookup_index += 1 17 | 18 | result.append(current_group) 19 | index = lookup_index 20 | 21 | return result 22 | 23 | 24 | tests = [ 25 | ([1, 1, 1, 2, 3, 1, 1], [[1, 1, 1], [2], [3], [1, 1]]), 26 | ([1, 2, 1, 2, 3, 3], [[1], [2], [1], [2], [3, 3]]), 27 | ([], []), 28 | ([1], [[1]]), 29 | ([1, 1, 1, 1], [[1, 1, 1, 1]]), 30 | ([1, 2, 3, 4], [[1], [2], [3], [4]]) 31 | ] 32 | 33 | for items, expected in tests: 34 | result = group(items) 35 | 36 | print(result == expected) 37 | -------------------------------------------------------------------------------- /C01-Python-Basics/20-C01P09/README.md: -------------------------------------------------------------------------------- 1 | # C01P09 - Balanced numbers 2 | 3 | A number is called balanced, if we take the middle of it and the sums of the left and right parts are equal. 4 | 5 | For example, the number `1238033` is balanced, because it's left part is `123` and right part is `033`. 6 | 7 | We have: `1 + 2 + 3 = 0 + 3 + 3 = 6` 8 | 9 | **Note: A number with only one digit is always balanced!** 10 | 11 | **Signature** 12 | 13 | ```python 14 | def is_number_balanced(number): 15 | pass 16 | ``` 17 | 18 | **Examples** 19 | 20 | ```python 21 | is_number_balanced(9) is True 22 | is_number_balanced(4518) is True 23 | is_number_balanced(28471) is False 24 | is_number_balanced(1238033) is True 25 | ``` 26 | -------------------------------------------------------------------------------- /C01-Python-Basics/20-C01P09/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P09S01 - Balanced numbers Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/Y8F79VIoomc) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/20-C01P09/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/Y8F79VIoomc 2 | def number_to_digits(n): 3 | n = abs(n) 4 | digits = [] 5 | 6 | char_digits = list(str(n)) 7 | 8 | for char_digit in char_digits: 9 | digits.append(int(char_digit)) 10 | 11 | return digits 12 | 13 | 14 | def is_number_balanced(number): 15 | # 0 1 2 3 16 | # [4, 5, | 1, 8] -> even length 17 | # ----- 18 | # 0 1 2 3 4 5 6 19 | # [1, 2, 3, |8|, 0, 3, 3] -> odd length 20 | digits = number_to_digits(number) 21 | length = len(digits) 22 | middle = length // 2 23 | is_odd_length = length % 2 == 1 24 | 25 | left_sum = 0 26 | right_sum = 0 27 | 28 | for index, digit in enumerate(digits): 29 | if index < middle: 30 | left_sum += digit 31 | else: 32 | if index == middle and is_odd_length: 33 | continue 34 | 35 | right_sum += digit 36 | 37 | return left_sum == right_sum 38 | 39 | 40 | tests = [ 41 | (9, True), 42 | (4518, True), 43 | (1111, True), 44 | (11111, True), 45 | (28471, False), 46 | (1238033, True), 47 | (123, False), 48 | (121, True), 49 | ] 50 | 51 | 52 | for n, expected in tests: 53 | result = is_number_balanced(n) 54 | 55 | print(result == expected) 56 | -------------------------------------------------------------------------------- /C01-Python-Basics/21-C01P10/README.md: -------------------------------------------------------------------------------- 1 | # C01P10 - Palindromes count 2 | 3 | Implement a function called `palindromes_count`, which: 4 | 5 | 1. Takes a number `n`. 6 | 1. Returns the number of integer palindromes between `[10, n]`. 7 | 1. It's important to note that: `10 <= n <= 99999`. 8 | 9 | An integer palindrome is a number `x`, that written in reverse, remains the same. 10 | 11 | For example, `11` is a palindrome, but `10` is not. 12 | 13 | **Signature** 14 | 15 | ```python 16 | def palindromes_count(n): 17 | pass 18 | ``` 19 | 20 | **Examples** 21 | 22 | ```python 23 | palindromes_count(10) == 0 24 | palindromes_count(20) == 1 # only 11 25 | palindromes_count(101) == 10 # 11, 22, 33, 44, 55, 66, 77, 88, 99, 101 26 | palindromes_count(92009) == 1009 27 | palindromes_count(99999) == 1089 28 | ``` 29 | -------------------------------------------------------------------------------- /C01-Python-Basics/21-C01P10/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P10S01 - Palindromes count Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/XJQ1fxAqsME) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/21-C01P10/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/XJQ1fxAqsME 2 | 3 | def string_reverse(s): 4 | return ''.join(reversed(s)) 5 | 6 | 7 | def is_integer_palindrome(x): 8 | # TODO: integer reverse 9 | x = str(x) 10 | 11 | return x == string_reverse(x) 12 | 13 | 14 | def palindromes_count(n): 15 | # 11 (11), 10 (01), 101 (101) 16 | count = 0 17 | 18 | for x in range(10, n + 1): 19 | if is_integer_palindrome(x): 20 | count += 1 21 | 22 | return count 23 | 24 | 25 | tests = [ 26 | (10, 0), 27 | (20, 1), 28 | (30, 2), 29 | (101, 10), 30 | (200, 19), 31 | (43539, 525), 32 | (4247, 132), 33 | (48877, 577), 34 | (94012, 1029), 35 | (62560, 715), 36 | (92009, 1009), 37 | (63176, 721), 38 | (67409, 763), 39 | (62834, 718), 40 | (77420, 863), 41 | (99999, 1089), 42 | ] 43 | 44 | 45 | for n, expected in tests: 46 | result = palindromes_count(n) 47 | 48 | print(result == expected) 49 | -------------------------------------------------------------------------------- /C01-Python-Basics/21-C01P10/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P10S02 - Palindromes count Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/BpoFDMIp3Fg) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/21-C01P10/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # https://youtu.be/BpoFDMIp3Fg 2 | 3 | def string_reverse(s): 4 | return ''.join(reversed(s)) 5 | 6 | 7 | def is_integer_palindrome(x): 8 | # TODO: integer reverse 9 | x = str(x) 10 | 11 | return x == string_reverse(x) 12 | 13 | 14 | def build_cache(): 15 | result = {} 16 | 17 | n = 99999 18 | 19 | for x in range(10, n + 1): 20 | result[x] = is_integer_palindrome(x) 21 | 22 | return result 23 | 24 | 25 | CACHE = build_cache() 26 | 27 | 28 | def palindromes_count(n): 29 | # 11 (11), 10 (01), 101 (101) 30 | count = 0 31 | 32 | for x in range(10, n + 1): 33 | if CACHE[x]: 34 | count += 1 35 | 36 | return count 37 | 38 | 39 | tests = [ 40 | (10, 0), 41 | (20, 1), 42 | (30, 2), 43 | (101, 10), 44 | (200, 19), 45 | (43539, 525), 46 | (4247, 132), 47 | (48877, 577), 48 | (94012, 1029), 49 | (62560, 715), 50 | (92009, 1009), 51 | (63176, 721), 52 | (67409, 763), 53 | (62834, 718), 54 | (77420, 863), 55 | (99999, 1089), 56 | ] 57 | 58 | 59 | for n, expected in tests: 60 | result = palindromes_count(n) 61 | 62 | print(result == expected) 63 | -------------------------------------------------------------------------------- /C01-Python-Basics/22-C01P11/README.md: -------------------------------------------------------------------------------- 1 | # C01P11 - Anagrams 2 | 3 | According to [Wikipedia](https://en.wikipedia.org/wiki/Anagram), we have the following definition of an anagram: 4 | 5 | > An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once. 6 | 7 | For example, `listen` and `silent` are anagrams. 8 | 9 | Implement a function called `anagrams`, that takes `word1, word2` and returns `True`, if those words are anagrams and `False` otherwise. 10 | 11 | We should take the following into consideration: 12 | 13 | 1. Casing does not matter, meaning `LISTEN` and `silent` are anagrams. 14 | 1. Whitespace does not matter, meaning `New York Times` and `monkeys writting` are anagrams. 15 | 16 | **Signature** 17 | 18 | ```python 19 | def anagrams(word1, word2): 20 | pass 21 | ``` 22 | 23 | **Examples** 24 | 25 | ```python 26 | anagrams("listen", "silent") is True 27 | anagrams("LISTEN", "silent") is True 28 | anagrams("python", "ruby") is False 29 | anagrams("New York Times", "monkeys write") is True 30 | anagrams("snake", "sssnakee") is False 31 | ``` 32 | -------------------------------------------------------------------------------- /C01-Python-Basics/22-C01P11/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P11S01 - Anagrams Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/wAXaJOxS-g8) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/22-C01P11/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/wAXaJOxS-g8 2 | 3 | def anagrams(word1, word2): 4 | # Chaining 5 | word1 = word1.lower().replace(' ', '') 6 | word2 = word2.lower().replace(' ', '') 7 | 8 | return sorted(word1) == sorted(word2) 9 | 10 | 11 | tests = [ 12 | (("silent", "listen"), True), 13 | (("SILENT", "listen"), True), 14 | (("silent", "LISTEN"), True), 15 | (("python", "ruby"), False), 16 | (("a gentleman", "elegant man"), True), 17 | (("eleven plus two", "twelve plus one"), True), 18 | (("William Shakespeare", "I am a weakish speller"), True), 19 | (("", ""), True) 20 | ] 21 | 22 | 23 | for args, expected in tests: 24 | result = anagrams(*args) 25 | 26 | print(result == expected) 27 | -------------------------------------------------------------------------------- /C01-Python-Basics/22-C01P11/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P11S02 - Anagrams Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/uuCIerDdLgQ) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/22-C01P11/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/uuCIerDdLgQ 2 | 3 | from itertools import permutations 4 | 5 | 6 | def anagrams(word1, word2): 7 | # Chaining 8 | word1 = word1.lower().replace(' ', '') 9 | word2 = word2.lower().replace(' ', '') 10 | 11 | for permutation in permutations(word1): 12 | w = ''.join(permutation) 13 | 14 | if w == word2: 15 | return True 16 | 17 | return False 18 | 19 | 20 | tests = [ 21 | (("silent", "listen"), True), 22 | (("SILENT", "listen"), True), 23 | (("silent", "LISTEN"), True), 24 | (("python", "ruby"), False), 25 | (("a gentleman", "elegant man"), True), 26 | (("eleven plus two", "twelve plus one"), True), 27 | (("William Shakespeare", "I am a weakish speller"), True), 28 | (("", ""), True) 29 | ] 30 | 31 | 32 | for args, expected in tests: 33 | result = anagrams(*args) 34 | 35 | print(result == expected) 36 | -------------------------------------------------------------------------------- /C01-Python-Basics/23-C01P12/README.md: -------------------------------------------------------------------------------- 1 | # C01P12 - Credit card validation 2 | 3 | Implement a function, called `is_credit_card_valid(number)`, which returns `True` or `False` based on the following algorithm: 4 | 5 | 1. Starting from the right, double every second digit. 6 | 1. If after doubling the digit becomes `>= 10`, calculate the sum of the digits of the new number & use that. For example, if we have `9` and doubling it gets us `18`, the final result is going to be `1 + 8 = 9` 7 | 1. After the transformation, we find the sum of all digits in the transformed number. 8 | 1. The number is valid, if the sum is divisible by 10. 9 | 10 | This is also known as the [Luhn algorithm.](https://en.wikipedia.org/wiki/Luhn_algorithm) 11 | 12 | For example: `79927398713` is valid, bacause: 13 | 14 | 1. After doubling, we get the following number: 15 | 16 | ``` 17 | 7 + 18 | sum_digits(9 * 2) + 19 | 9 + 20 | sum_digits(2 * 2) + 21 | 7 + 22 | sum_digits(3 * 2) + 23 | 9 + 24 | sum_digits(8 * 2) + 25 | 7 + 26 | sum_digits(1 * 2) + 27 | 3 28 | ``` 29 | 30 | 2. And calculating the sum of the digits of the transformed number, we get `70` => the card number is valid. 31 | 32 | 33 | **Signature** 34 | 35 | ```python 36 | def is_credit_card_valid(number): 37 | pass 38 | ``` 39 | 40 | **Examples** 41 | 42 | ```python 43 | is_credit_card_valid(79927398713) is True 44 | is_credit_card_valid(79927398715) is False 45 | is_credit_card_valid(4417123456789113) is True 46 | ``` 47 | 48 | In case you need more cards, you can check out [Stripe testing cards](https://stripe.com/docs/testing) 49 | -------------------------------------------------------------------------------- /C01-Python-Basics/23-C01P12/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P12S01 - Credit card validation Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/p9A5ySU8nWU) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/23-C01P12/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # Video - https://youtu.be/p9A5ySU8nWU 2 | 3 | def to_digits(number): 4 | digits = [] 5 | 6 | for x in str(number): 7 | digits.append(int(x)) 8 | 9 | return digits 10 | 11 | 12 | # 79927398713 13 | # 7 + 9 + 9 + 4 + 7 + 6 + 9 + 7 + 7 + 2 + 3 14 | def is_credit_card_valid(number): 15 | result = [] 16 | 17 | digits = to_digits(number) 18 | digits_count = len(digits) 19 | 20 | double = False 21 | 22 | for index in range(digits_count - 1, -1, -1): 23 | multiplier = 1 24 | 25 | if double: 26 | multiplier = 2 27 | 28 | transformed = sum(to_digits(digits[index] * multiplier)) 29 | 30 | result.append(transformed) 31 | 32 | double = not double 33 | 34 | return sum(result) % 10 == 0 35 | 36 | 37 | tests = [ 38 | (79927398713, True), 39 | (4417123456789113, True), 40 | (4242424242424242, True), 41 | (79927398715, False), 42 | (79927398710, False), 43 | (79927398711, False), 44 | (79927398712, False), 45 | (79927398714, False), 46 | (79927398715, False), 47 | (79927398716, False), 48 | (79927398717, False), 49 | (79927398718, False), 50 | (79927398719, False) 51 | ] 52 | 53 | 54 | for number, expected in tests: 55 | result = is_credit_card_valid(number) 56 | 57 | print(result == expected) 58 | -------------------------------------------------------------------------------- /C01-Python-Basics/24-C01P13/README.md: -------------------------------------------------------------------------------- 1 | # C01P13 - Integer prime factorization 2 | 3 | Given an integer `n`, we can factor it in the following form: 4 | 5 | ``` 6 | n = p1^a1 * p2^a2 * ... * pn^an 7 | ``` 8 | 9 | Where each `p` is a prime number and each `a` is an integer and `p^a` means `p` to the power of `a`. 10 | 11 | This is called [prime factorization](https://mathworld.wolfram.com/PrimeFactorization.html) 12 | 13 | Lets see few examples: 14 | 15 | ``` 16 | 10 = 2^1 * 5^1 17 | 25 = 5^2 18 | 356 = 2^2 * 89 ^ 1 19 | ``` 20 | 21 | Implement a function, called `prime_factorization`, which takes an integer and returns a list of tuples `(pi, ai)` that is the result of the factorization. 22 | 23 | **The list should be sorted in increasing order of the prime numbers.** 24 | 25 | **Signature** 26 | 27 | ```python 28 | def prime_factorization(n): 29 | pass 30 | ``` 31 | 32 | **Examples** 33 | 34 | ```python 35 | prime_factorization(10) == [(2, 1), (5, 1)] # This is 2^1 * 5^1 36 | prime_factorization(14) == [(2, 1), (7, 1)] 37 | prime_factorization(356) == [(2, 2), (89, 1)] 38 | prime_factorization(89) == [(89, 1)] # 89 is a prime number 39 | prime_factorization(1000) == [(2, 3), (5, 3)] 40 | ``` 41 | -------------------------------------------------------------------------------- /C01-Python-Basics/24-C01P13/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P13S01 - Integer prime factorization Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/CR_19VUt0tI) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/24-C01P13/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # C01P13 - Integer prime factorization 2 | # Video - https://youtu.be/CR_19VUt0tI 3 | 4 | 5 | def is_prime(n): 6 | if n < 2: 7 | return False 8 | 9 | for d in range(2, n): 10 | if n % d == 0: 11 | return False 12 | 13 | return True 14 | 15 | 16 | def next_prime(n): 17 | n = n + 1 18 | 19 | while not is_prime(n): 20 | n = n + 1 21 | 22 | return n 23 | 24 | 25 | def prime_factorization(n): 26 | result = [] 27 | 28 | p = 2 29 | 30 | while n != 1: 31 | a = 0 32 | 33 | while n % p == 0: 34 | a = a + 1 35 | n = n // p 36 | 37 | if a > 0: 38 | result.append((p, a)) 39 | 40 | p = next_prime(p) 41 | 42 | return result 43 | 44 | 45 | tests = [ 46 | (2, [(2, 1)]), 47 | (4, [(2, 2)]), 48 | (10, [(2, 1), (5, 1)]), # This is 2^1 * 5^1 49 | (14, [(2, 1), (7, 1)]), 50 | (356, [(2, 2), (89, 1)]), 51 | (89, [(89, 1)]), # 89 is a prime number 52 | (1000, [(2, 3), (5, 3)]) 53 | ] 54 | 55 | 56 | for n, expected in tests: 57 | result = prime_factorization(n) 58 | 59 | print(result == expected, result) 60 | -------------------------------------------------------------------------------- /C01-Python-Basics/25-C01P14/README.md: -------------------------------------------------------------------------------- 1 | # C01P14 - Goldbach conjecture 2 | 3 | Implement a function, called `goldbach` which takes an integer `n`, and returns a list of tuples, that is the goldbach conjecture for the given number. 4 | 5 | The [Goldbach conjecture](https://en.wikipedia.org/wiki/Goldbach%27s_conjecture) states: 6 | 7 | > Every even integer greater than 2 can be expressed as the sum of two primes. 8 | 9 | Keep in mind that there can be more than one combination of two primes, that sums up to the given number. 10 | 11 | In case an odd integer is given, return `None`. 12 | 13 | **The result should be sorted by the first item in the tuple.** 14 | 15 | For example: 16 | 17 | - `4 = 2 + 2` 18 | - `6 = 3 + 3` 19 | - `8 = 3 + 5` 20 | - `10 = 3 + 7 = 5 + 5` 21 | - `100 = 3 + 97 = 11 + 89 = 17 + 83 = 29 + 71 = 41 + 59 = 47 + 53` 22 | 23 | **Signature** 24 | 25 | ```python 26 | def goldbach(n): 27 | pass 28 | ``` 29 | 30 | **Examples** 31 | 32 | ```python 33 | goldbach(4) == [(2,2)] 34 | goldbach(6) == [(3,3)] 35 | goldbach(8) == [(3,5)] 36 | goldbach(10) == [(3,7), (5,5)] 37 | goldbach(100) == [(3, 97), (11, 89), (17, 83), (29, 71), (41, 59), (47, 53)] 38 | goldbach(5) is None 39 | ``` 40 | -------------------------------------------------------------------------------- /C01-Python-Basics/25-C01P14/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P14S01 - Goldbach conjecture Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/f2PsNBHR73o) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/25-C01P14/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # C01P14 - Goldbach conjecture 2 | # Video - https://youtu.be/f2PsNBHR73o 3 | 4 | def is_prime(n): 5 | if n < 2: 6 | return False 7 | 8 | for d in range(2, n): 9 | if n % d == 0: 10 | return False 11 | 12 | return True 13 | 14 | 15 | # Every even integer greater than 2 16 | # can be expressed as the sum of two primes. 17 | def goldbach(n): 18 | if n <= 2 or n % 2 == 1: 19 | return 20 | 21 | result = [] 22 | 23 | # List comprehension 24 | primes = [] 25 | 26 | for x in range(2, n): 27 | if is_prime(x): 28 | primes.append(x) 29 | 30 | # a + b == b + a 31 | used = set() 32 | 33 | for p1 in primes: 34 | for p2 in primes: 35 | if p1 + p2 == n: 36 | pair = (p1, p2) 37 | reverse_pair = (p2, p1) 38 | 39 | if pair not in used and reverse_pair not in used: 40 | used.add(pair) 41 | result.append(pair) 42 | 43 | return result 44 | 45 | 46 | tests = [ 47 | (4, [(2, 2)]), 48 | (6, [(3, 3)]), 49 | (8, [(3, 5)]), 50 | (10, [(3, 7), (5, 5)]), 51 | (100, [(3, 97), (11, 89), (17, 83), (29, 71), (41, 59), (47, 53)]), 52 | (5, None) 53 | ] 54 | 55 | for n, expected in tests: 56 | result = goldbach(n) 57 | 58 | print(result == expected) 59 | -------------------------------------------------------------------------------- /C01-Python-Basics/25-C01P14/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P14S02 - Goldbach conjecture Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/PeUJzKFh2sQ) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/25-C01P14/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # C01P14 - Goldbach conjecture 2 | # Video - https://youtu.be/PeUJzKFh2sQ 3 | 4 | from itertools import combinations_with_replacement 5 | 6 | 7 | def is_prime(n): 8 | if n < 2: 9 | return False 10 | 11 | for d in range(2, n): 12 | if n % d == 0: 13 | return False 14 | 15 | return True 16 | 17 | 18 | def goldbach(n): 19 | if n <= 2 or n % 2 == 1: 20 | return 21 | 22 | result = [] 23 | 24 | # List comprehension 25 | primes = [x for x in range(2, n) if is_prime(x)] 26 | 27 | for p1, p2 in combinations_with_replacement(primes, 2): 28 | if p1 + p2 == n: 29 | result.append((p1, p2)) 30 | 31 | return result 32 | 33 | 34 | tests = [ 35 | (4, [(2, 2)]), 36 | (6, [(3, 3)]), 37 | (8, [(3, 5)]), 38 | (10, [(3, 7), (5, 5)]), 39 | (100, [(3, 97), (11, 89), (17, 83), (29, 71), (41, 59), (47, 53)]), 40 | (5, None) 41 | ] 42 | 43 | for n, expected in tests: 44 | result = goldbach(n) 45 | 46 | print(result == expected) 47 | -------------------------------------------------------------------------------- /C01-Python-Basics/26-C01P15/README.md: -------------------------------------------------------------------------------- 1 | # C01P15 - Gas stations 2 | 3 | We are implementing a smart GPS software. 4 | 5 | - We are taking a long trip from Sofia to Bourgas and we know the distance between the two cities. It is a positive integer and we mark it as `distance`. 6 | - We know how much our car can ride with a full tank of gas. It is a positive integer in kilometers. We mark it as `tank_size`. 7 | - We have a list of gas stations. We know the distance between Sofia and the current gas station. `stations = [50, 80, 110, 180, 220, 290]` Notice, the list is sorted! 8 | 9 | By using this information we will implement a function that returns the shortest `list` of gas stations that we have to visit in order to travel from Sofia to Bourgas. We allways start with a full `tank_size`! 10 | 11 | Something important: 12 | 13 | 1. If there's a gas station in `50km` and you have `50l`, you cannot reach that gas station. You'll need `51l` in the tank. 14 | 1. If you cannot reach the destination, return an empty list. 15 | 16 | **Signature** 17 | 18 | ```python 19 | def gas_stations(distance, tank_size, stations): 20 | pass 21 | ``` 22 | 23 | **Examples** 24 | 25 | ```python 26 | gas_stations(320, 90, [50, 80, 140, 180, 220, 290]) == [80, 140, 220, 290] 27 | gas_stations(390, 80, [70, 90, 140, 210, 240, 280, 350]) == [70, 140, 210, 280, 350] 28 | gas_stations(100, 50, [10, 20, 30, 40, 50, 60, 70, 80, 90, 150]) == [40, 80] 29 | gas_stations(100, 50, [10, 90]) == [] 30 | ``` 31 | -------------------------------------------------------------------------------- /C01-Python-Basics/26-C01P15/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P15S01 - Gas stations Solution 01 2 | 3 | 1. Check the [video for the solution](https://youtu.be/R-7L89EG2y8) 4 | 5 | We are talking about a potential approach for solving the problem, without actually solving the problem with code. 6 | 7 | Check then next solution for the code. 8 | -------------------------------------------------------------------------------- /C01-Python-Basics/26-C01P15/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P15S01 - Gas stations Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/mlPfHta9Lus) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/26-C01P15/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | # C01P15 - Gas stations 2 | # Video - https://youtu.be/mlPfHta9Lus 3 | 4 | # 90, [50, 80, 140, 180, 220, 290], 320 5 | # diffs [50, 30, 60, 40, 40, 70, 30] 6 | # gas_tank [40, 90, 30, 50, 90, 90, 60] 7 | def gas_stations(distance, tank_size, stations): 8 | result = [] 9 | 10 | stations_with_target = [ 11 | station for station in stations 12 | if station < distance 13 | ] + [distance] 14 | diffs = [stations_with_target[0]] 15 | 16 | for index in range(0, len(stations_with_target) - 1): 17 | diffs.append( 18 | stations_with_target[index + 1] - stations_with_target[index] 19 | ) 20 | 21 | current_tank_size = tank_size 22 | 23 | for index, diff in enumerate(diffs): 24 | current_tank_size -= diff 25 | 26 | # Cannot reach 50km with 50l 27 | if current_tank_size <= 0: 28 | current_tank_size = tank_size - diff 29 | 30 | if current_tank_size <= 0: 31 | return [] 32 | 33 | result.append(stations[index - 1]) 34 | 35 | return result 36 | 37 | 38 | tests = [ 39 | ((320, 90, [50, 80, 140, 180, 220, 290]), [80, 140, 220, 290]), 40 | ((390, 80, [70, 90, 140, 210, 240, 280, 350]), [70, 140, 210, 280, 350]), 41 | ((100, 50, [10, 20, 30, 40, 50, 60, 70, 80, 90, 150]), [40, 80]), 42 | ((100, 101, [200]), []), 43 | ((100, 50, [200]), []), 44 | ((100, 50, [10, 90]), []), 45 | ] 46 | 47 | for args, expected in tests: 48 | result = gas_stations(*args) 49 | 50 | passing = result == expected 51 | 52 | if passing: 53 | print(passing) 54 | else: 55 | print(passing, result) 56 | -------------------------------------------------------------------------------- /C01-Python-Basics/27-C01P16/README.md: -------------------------------------------------------------------------------- 1 | # C01P16 - Nokia keypad 2 | 3 | A long time ago, before the smartphones, when you had to write some messages, the keypads looked like that: 4 | 5 | ![Nokia Keypad](https://user-images.githubusercontent.com/387867/116038228-d92c3380-a671-11eb-8e31-ff902ada6771.jpg) 6 | 7 | For example, on such keypad, if you want to write **Ruby**, you had to press the following sequence of numbers: 8 | 9 | ``` 10 | 7778822999 11 | ``` 12 | 13 | Each key contains some letters from the alphabet. And by pressing that key, you rotate the letters until you get to your desired one. 14 | 15 | It's time to implement some encode / decode functions for the old keypads! 16 | 17 | First, implement a function that takes a list of integers - the sequence of numbers that have been pressed. The returned value should be the corresponding string of the message. 18 | 19 | **Signature** 20 | 21 | ```python 22 | def numbers_to_message(pressed_sequence): 23 | pass 24 | ``` 25 | 26 | There are some special rules: 27 | 28 | - If you press `1`, the next letter is going to be capitalized 29 | - If you press `0`, this will insert a single white-space 30 | - If you press a number and wait for a few seconds, the special breaking number `-1` enters the sequence. This is the way to write different letters from only one keypad! 31 | 32 | **Examples:** 33 | 34 | ```python 35 | >>> numbers_to_message([2, -1, 2, 2, -1, 2, 2, 2]) 36 | "abc" 37 | >>> numbers_to_message([2, 2, 2, 2]) 38 | "a" 39 | >>> numbers_to_message([1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 7, 7, 7, 7, 2, 6, 6, 3, 2]) 40 | "Ivo e Panda" 41 | ``` 42 | 43 | Now it is time to convert the message to a sequence of numbers. This function takes a string - the `message` and returns the **minimal** keystrokes that you need to write that `message` 44 | 45 | **Signature** 46 | 47 | ```python 48 | def message_to_numbers(message): 49 | pass 50 | ``` 51 | 52 | **Examples** 53 | 54 | ```python 55 | >>> message_to_numbers("abc") 56 | [2, -1, 2, 2, -1, 2, 2, 2] 57 | >>> message_to_numbers("a") 58 | [2] 59 | >>> message_to_numbers("Ivo e Panda") 60 | [1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 2, 6, 6, 3, 2] 61 | >>> message_to_numbers("aabbcc") 62 | [2, -1, 2, -1, 2, 2, -1, 2, 2, -1, 2, 2, 2, -1, 2, 2, 2] 63 | ``` 64 | -------------------------------------------------------------------------------- /C01-Python-Basics/27-C01P16/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P16S01 - Nokia keypad Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/DXV5Mv4syuw) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/27-C01P16/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # C01P16 - Nokia keypad 2 | 3 | 4 | def group(items): 5 | result = [] 6 | length = len(items) 7 | index = 0 8 | 9 | while index < length: 10 | item = items[index] 11 | current_group = [item] 12 | 13 | lookup_index = index + 1 14 | 15 | while lookup_index < length and items[lookup_index] == item: 16 | current_group.append(items[lookup_index]) 17 | lookup_index += 1 18 | 19 | result.append(current_group) 20 | index = lookup_index 21 | 22 | return result 23 | 24 | 25 | KEYPAD = { 26 | 2: "abc", 27 | 3: "def", 28 | 4: "ghi", 29 | 5: "jkl", 30 | 6: "mno", 31 | 7: "pqrs", 32 | 8: "tuv", 33 | 9: "wxyz" 34 | } 35 | 36 | 37 | def numbers_to_message(pressed_sequence): 38 | letters = [] 39 | grouped_pressed_sequence = group(pressed_sequence) 40 | 41 | upper = False 42 | 43 | for grouped in grouped_pressed_sequence: 44 | key = grouped[0] 45 | times_pressed = len(grouped) 46 | 47 | if key == -1: 48 | continue 49 | 50 | if key == 1: 51 | upper = True 52 | continue 53 | 54 | if key == 0: 55 | letters.append(" " * times_pressed) 56 | continue 57 | 58 | sequence = KEYPAD[key] 59 | letter = sequence[(times_pressed - 1) % len(sequence)] 60 | 61 | if upper: 62 | letter = letter.upper() 63 | upper = False 64 | 65 | letters.append(letter) 66 | 67 | return "".join(letters) 68 | 69 | 70 | tests = [ 71 | ( 72 | [0, 0, 0, 0], 73 | " " 74 | ), 75 | ( 76 | [2, -1, 2, 2, -1, 2, 2, 2], 77 | "abc" 78 | ), 79 | ( 80 | [2, 2, 2, 2], 81 | "a" 82 | ), 83 | ( 84 | [1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 7, 7, 7, 7, 2, 6, 6, 3, 2], # noqa 85 | "Ivo e Panda" 86 | ), 87 | ( 88 | [2, 3, 4, 5, 6, 7, 8, 9], 89 | "adgjmptw" 90 | ), 91 | ( 92 | [2, -1, 3,-1, 4, -1, 5, -1, 6, -1, 7, -1, 8, -1, 9], # noqa 93 | "adgjmptw" 94 | ), 95 | ( 96 | [0, -1, 0, -1, 0, -1, 0], 97 | " " 98 | ), 99 | ( 100 | [2, 2, 2, -1, 2], 101 | "ca" 102 | ), 103 | ] 104 | 105 | for sequence, expected in tests: 106 | result = numbers_to_message(sequence) 107 | 108 | print(result == expected, result) 109 | -------------------------------------------------------------------------------- /C01-Python-Basics/27-C01P16/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P16S02 - Nokia keypad Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/NcykjzHCv8M) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/27-C01P16/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | SEQUENCES = { 2 | "a": [2], 3 | "b": [2, 2], 4 | "c": [2, 2, 2], 5 | "d": [3], 6 | "e": [3, 3], 7 | "f": [3, 3, 3], 8 | "g": [4], 9 | "h": [4, 4], 10 | "i": [4, 4, 4], 11 | "j": [5], 12 | "k": [5, 5], 13 | "l": [5, 5, 5], 14 | "m": [6], 15 | "n": [6, 6], 16 | "o": [6, 6, 6], 17 | "p": [7], 18 | "q": [7, 7], 19 | "r": [7, 7, 7], 20 | "s": [7, 7, 7, 7], 21 | "t": [8], 22 | "u": [8, 8], 23 | "v": [8, 8, 8], 24 | "w": [9], 25 | "x": [9, 9], 26 | "y": [9, 9, 9], 27 | "z": [9, 9, 9, 9], 28 | " ": [0] 29 | } 30 | 31 | 32 | def message_to_numbers(message): 33 | sequence = [] 34 | 35 | for char in message: 36 | if char.isupper(): 37 | sequence.append(1) 38 | 39 | shortest_sequence = SEQUENCES[char.lower()] 40 | 41 | if len(sequence) > 0: 42 | previous_key = sequence[len(sequence) - 1] 43 | 44 | # In case we have "abc", we want to add -1 as a separator 45 | # That's why we are looking behind to check the previous key 46 | if previous_key == shortest_sequence[0]: 47 | sequence.append(-1) 48 | 49 | sequence.extend(shortest_sequence) 50 | 51 | return sequence 52 | 53 | 54 | tests = [ 55 | ("abc", [2, -1, 2, 2, -1, 2, 2, 2]), 56 | ("a", [2]), 57 | ("Ivo e Panda", [1, 4, 4, 4, 8, 8, 8, 6, 6, 6, 0, 3, 3, 0, 1, 7, 2, 6, 6, 3, 2]), # noqa 58 | ("aabbcc", [2, -1, 2, -1, 2, 2, -1, 2, 2, -1, 2, 2, 2, -1, 2, 2, 2]) 59 | ] 60 | 61 | 62 | for message, expected in tests: 63 | result = message_to_numbers(message) 64 | 65 | print(result == expected, result) 66 | -------------------------------------------------------------------------------- /C01-Python-Basics/28-C01P17/README.md: -------------------------------------------------------------------------------- 1 | # C01P17 - Word counter 2 | 3 | You are given a matrix (list of lists) with chars & an additional `word`. 4 | 5 | Your task is to count the occurences of that `word` in the table. 6 | 7 | The word can be found: 8 | 9 | - horizontaly 10 | - vertically 11 | - across both left to right and right to left. 12 | 13 | 14 | 15 | For example, lets count the word `ivan` in the table: 16 | 17 | | i | v | a | n | 18 | | --- | --- | --- | --- | 19 | | e | _v_ | _n_ | h | 20 | | i | n | _a_ | v | 21 | | m | v | _v_ | _n_ | 22 | | q | r | _i_ | t | 23 | 24 | 25 | We count exactly `3` occurences of the word. 26 | 27 | Implement a function `word_counter` that does exactly that. 28 | 29 | **Signature** 30 | 31 | ```python 32 | def word_counter(matrix, word): 33 | pass 34 | ``` 35 | 36 | **Examples** 37 | 38 | ```python 39 | word = "ivan" 40 | matrix = [ 41 | ["i", "v", "a", "n"], 42 | ["e", "v", "n", "h"], 43 | ["i", "n", "a", "v"], 44 | ["m", "v", "v", "n"], 45 | ["q", "r", "i", "t"] 46 | ] 47 | word_counter(matrix, word) == 3 48 | ``` 49 | 50 | ```python 51 | word = "actually" 52 | matrix = [ 53 | ["i", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z", "o", "y", "m"], 54 | ["e", "v", "n", "h", "t", "r", "x", "e", "k", "y", "d", "a", "i", "l", "c"], 55 | ["i", "a", "c", "t", "u", "a", "l", "l", "y", "m", "c", "x", "r", "l", "e"], 56 | ["m", "v", "c", "n", "p", "u", "a", "m", "n", "t", "l", "u", "e", "a", "a"], 57 | ["q", "r", "i", "t", "w", "e", "a", "q", "u", "p", "r", "x", "t", "u", "z"], 58 | ["p", "e", "a", "c", "t", "u", "a", "l", "l", "y", "w", "p", "y", "t", "m"], 59 | ["o", "y", "h", "t", "r", "e", "l", "u", "f", "p", "q", "n", "z", "c", "s"], 60 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e", "q", "a", "r"] 61 | ] 62 | word_counter(matrix, word) == 4 63 | ``` 64 | 65 | ```python 66 | word = "madam" 67 | matrix = [ 68 | ["z", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z"], 69 | ["e", "v", "m", "h", "t", "r", "x", "e", "k", "y", "m", "a"], 70 | ["i", "a", "c", "a", "u", "a", "l", "l", "y", "a", "c", "x"], 71 | ["m", "v", "c", "n", "d", "u", "a", "m", "d", "t", "l", "u"], 72 | ["q", "t", "i", "t", "w", "a", "a", "a", "u", "p", "r", "x"], 73 | ["p", "e", "m", "a", "d", "a", "m", "l", "l", "y", "w", "p"], 74 | ["o", "y", "h", "t", "e", "e", "l", "u", "f", "p", "q", "n"], 75 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e"] 76 | ] 77 | word_counter(matrix, word) == 3 78 | ``` 79 | 80 | ```python 81 | word = "python" 82 | matrix = [ 83 | ["r", "u", "b", "y"], 84 | ["r", "u", "b", "y"], 85 | ["r", "u", "b", "y"], 86 | ["r", "u", "b", "y"], 87 | ] 88 | 89 | word_counter(matrix, word) == 0 90 | ``` 91 | -------------------------------------------------------------------------------- /C01-Python-Basics/28-C01P17/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P17S01 - Word counter Solution 01 2 | 3 | Here, we are talking about a potential approach to the problem. 4 | 5 | 1. Check [solution.py](./solution.py) 6 | 1. Check the [video for the solution](https://youtu.be/8LP7fxvY2zg) 7 | -------------------------------------------------------------------------------- /C01-Python-Basics/28-C01P17/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class Direction(enum.Enum): 5 | HORIZONTAL_LEFT = "HORIZONTAL_LEFT" 6 | HORIZONTAL_RIGHT = "HORIZONTAL_RIGHT" 7 | 8 | VERTICAL_LEFT = "VERTICAL_LEFT" 9 | VERTICAL_RIGHT = "VERTICAL_RIGHT" 10 | 11 | DIAGONAL_UP_RIGHT = "DIAGONAL_UP_RIGHT" 12 | DIAGONAL_UP_LEFT = "DIAGONAL_UP_LEFT" 13 | 14 | DIAGONAL_DOWN_RIGHT = "DIAGONAL_DOWN_RIGHT" 15 | DIAGONAL_DOWN_LEFT = "DIAGONAL_DOWN_LEFT" 16 | 17 | 18 | def take_word(n, point, direction, matrix): 19 | # dx, dy 20 | if direction == Direction.DIAGONAL_UP_LEFT: 21 | pass 22 | 23 | 24 | def word_counter(matrix, word): 25 | result = 0 26 | 27 | for row_index in range(len(matrix)): 28 | for column_index in range(len(matrix[0])): 29 | for direction in Direction: 30 | found_word = take_word( 31 | len(word), 32 | (row_index, column_index), 33 | direction, 34 | matrix, 35 | ) 36 | 37 | if found_word == word: 38 | result += 1 39 | 40 | # TODO: Test this 41 | if word == word[::-1]: 42 | result = result // 2 43 | 44 | return result 45 | 46 | 47 | word = "ivan" 48 | matrix = [ 49 | ["i", "v", "a", "n"], 50 | ["e", "v", "n", "h"], 51 | ["i", "n", "a", "v"], 52 | ["m", "v", "v", "n"], 53 | ["q", "r", "i", "t"] 54 | ] 55 | 56 | matrix[2][3] 57 | word_counter(matrix, word) == 3 58 | 59 | word = "actually" 60 | matrix = [ 61 | ["i", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z", "o", "y", "m"], # noqa 62 | ["e", "v", "n", "h", "t", "r", "x", "e", "k", "y", "d", "a", "i", "l", "c"], # noqa 63 | ["i", "a", "c", "t", "u", "a", "l", "l", "y", "m", "c", "x", "r", "l", "e"], # noqa 64 | ["m", "v", "c", "n", "p", "u", "a", "m", "n", "t", "l", "u", "e", "a", "a"], # noqa 65 | ["q", "r", "i", "t", "w", "e", "a", "q", "u", "p", "r", "x", "t", "u", "z"], # noqa 66 | ["p", "e", "a", "c", "t", "u", "a", "l", "l", "y", "w", "p", "y", "t", "m"], # noqa 67 | ["o", "y", "h", "t", "r", "e", "l", "u", "f", "p", "q", "n", "z", "c", "s"], # noqa 68 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e", "q", "a", "r"] # noqa 69 | ] 70 | word_counter(matrix, word) == 4 71 | 72 | word = "madam" 73 | matrix = [ 74 | ["z", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z"], 75 | ["e", "v", "m", "h", "t", "r", "x", "e", "k", "y", "m", "a"], 76 | ["i", "a", "c", "a", "u", "a", "l", "l", "y", "a", "c", "x"], 77 | ["m", "v", "c", "n", "d", "u", "a", "m", "d", "t", "l", "u"], 78 | ["q", "t", "i", "t", "w", "a", "a", "a", "u", "p", "r", "x"], 79 | ["p", "e", "m", "a", "d", "a", "m", "l", "l", "y", "w", "p"], 80 | ["o", "y", "h", "t", "e", "e", "l", "u", "f", "p", "q", "n"], 81 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e"] 82 | ] 83 | word_counter(matrix, word) == 3 84 | -------------------------------------------------------------------------------- /C01-Python-Basics/28-C01P17/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C01P17S02 - Word counter Solution 02 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/atD5q13nInM) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/28-C01P17/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class Direction(enum.Enum): 5 | HORIZONTAL_RIGHT = "HORIZONTAL_RIGHT" 6 | HORIZONTAL_LEFT = "HORIZONTAL_LEFT" 7 | 8 | VERTICAL_DOWN = "VERTICAL_DOWN" 9 | VERTICAL_UP = "VERTICAL_UP" 10 | 11 | DIAGONAL_UP_RIGHT = "DIAGONAL_UP_RIGHT" 12 | DIAGONAL_UP_LEFT = "DIAGONAL_UP_LEFT" 13 | 14 | DIAGONAL_DOWN_RIGHT = "DIAGONAL_DOWN_RIGHT" 15 | DIAGONAL_DOWN_LEFT = "DIAGONAL_DOWN_LEFT" 16 | 17 | 18 | def outside_of_bounds(point, matrix): 19 | x, y = point 20 | 21 | MIN_X = 0 22 | MAX_X = len(matrix) - 1 23 | 24 | MIN_Y = 0 25 | MAX_Y = len(matrix[0]) - 1 26 | 27 | return x < MIN_X or x > MAX_X or y < MIN_Y or y > MAX_Y 28 | 29 | 30 | def take_word(n, point, direction, matrix): 31 | dx = 0 32 | dy = 0 33 | 34 | direction_mapper = { 35 | Direction.HORIZONTAL_RIGHT: (0, 1), 36 | Direction.HORIZONTAL_LEFT: (0, -1), 37 | 38 | Direction.VERTICAL_DOWN: (1, 0), 39 | Direction.VERTICAL_UP: (-1, 0), 40 | 41 | Direction.DIAGONAL_UP_RIGHT: (-1, 1), 42 | Direction.DIAGONAL_UP_LEFT: (-1, -1), 43 | 44 | Direction.DIAGONAL_DOWN_RIGHT: (1, 1), 45 | Direction.DIAGONAL_DOWN_LEFT: (1, -1), 46 | } 47 | 48 | dx, dy = direction_mapper.get(direction, (0, 0)) 49 | 50 | # grunt work 51 | # if direction == Direction.HORIZONTAL_RIGHT: 52 | # dx = 0 53 | # dy = 1 54 | 55 | # if direction == Direction.HORIZONTAL_LEFT: 56 | # dx = 0 57 | # dy = -1 58 | 59 | # if direction == Direction.VERTICAL_DOWN: 60 | # dx = 1 61 | # dy = 0 62 | 63 | # if direction == Direction.VERTICAL_UP: 64 | # dx = -1 65 | # dy = 0 66 | 67 | # if direction == Direction.DIAGONAL_UP_RIGHT: 68 | # dx = -1 69 | # dy = 1 70 | 71 | # if direction == Direction.DIAGONAL_UP_LEFT: 72 | # dx = -1 73 | # dy = -1 74 | 75 | # if direction == Direction.DIAGONAL_DOWN_RIGHT: 76 | # dx = 1 77 | # dy = 1 78 | 79 | # if direction == Direction.DIAGONAL_DOWN_LEFT: 80 | # dx = 1 81 | # dy = -1 82 | 83 | start_x, start_y = point 84 | n_counter = 0 85 | chars = [] 86 | 87 | while n_counter != n: 88 | if outside_of_bounds((start_x, start_y), matrix): 89 | # However we decide to define it 90 | # can be break 91 | return None 92 | 93 | chars.append(matrix[start_x][start_y]) 94 | 95 | start_x += dx 96 | start_y += dy 97 | 98 | n_counter += 1 99 | 100 | return "".join(chars) 101 | 102 | 103 | def word_counter(matrix, word): 104 | result = 0 105 | 106 | for row_index in range(len(matrix)): 107 | for column_index in range(len(matrix[0])): 108 | for direction in Direction: 109 | found_word = take_word( 110 | len(word), 111 | (row_index, column_index), 112 | direction, 113 | matrix, 114 | ) 115 | 116 | if found_word == word: 117 | result += 1 118 | 119 | # TODO: Test this 120 | if word == word[::-1]: 121 | result = result // 2 122 | 123 | return result 124 | 125 | 126 | word = "ivan" 127 | matrix = [ 128 | ["i", "v", "a", "n"], 129 | ["e", "v", "n", "h"], 130 | ["i", "n", "a", "v"], 131 | ["m", "v", "v", "n"], 132 | ["q", "r", "i", "t"] 133 | ] 134 | 135 | result = word_counter(matrix, word) 136 | print(result == 3, result) 137 | 138 | word = "actually" 139 | matrix = [ 140 | ["i", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z", "o", "y", "m"], # noqa 141 | ["e", "v", "n", "h", "t", "r", "x", "e", "k", "y", "d", "a", "i", "l", "c"], # noqa 142 | ["i", "a", "c", "t", "u", "a", "l", "l", "y", "m", "c", "x", "r", "l", "e"], # noqa 143 | ["m", "v", "c", "n", "p", "u", "a", "m", "n", "t", "l", "u", "e", "a", "a"], # noqa 144 | ["q", "r", "i", "t", "w", "e", "a", "q", "u", "p", "r", "x", "t", "u", "z"], # noqa 145 | ["p", "e", "a", "c", "t", "u", "a", "l", "l", "y", "w", "p", "y", "t", "m"], # noqa 146 | ["o", "y", "h", "t", "r", "e", "l", "u", "f", "p", "q", "n", "z", "c", "s"], # noqa 147 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e", "q", "a", "r"] # noqa 148 | ] 149 | result = word_counter(matrix, word) 150 | print(result == 4, result) 151 | 152 | word = "madam" 153 | matrix = [ 154 | ["z", "v", "a", "n", "q", "h", "r", "e", "z", "g", "t", "z"], 155 | ["e", "v", "m", "h", "t", "r", "x", "e", "k", "y", "m", "a"], 156 | ["i", "a", "c", "a", "u", "a", "l", "l", "y", "a", "c", "x"], 157 | ["m", "v", "c", "n", "d", "u", "a", "m", "d", "t", "l", "u"], 158 | ["q", "t", "i", "t", "w", "a", "a", "a", "u", "p", "r", "x"], 159 | ["p", "e", "m", "a", "d", "a", "m", "l", "l", "y", "w", "p"], 160 | ["o", "y", "h", "t", "e", "e", "l", "u", "f", "p", "q", "n"], 161 | ["p", "a", "c", "t", "u", "a", "l", "l", "y", "u", "r", "e"] 162 | ] 163 | result = word_counter(matrix, word) 164 | print(result == 3, result) 165 | -------------------------------------------------------------------------------- /C01-Python-Basics/29-C01P18/README.md: -------------------------------------------------------------------------------- 1 | # C01P18 - Matrix bombing 2 | 3 | You are given a `NxM` matrix of integer numbers. 4 | 5 | We can drop a bomb at any place in the matrix, which has the following effect: 6 | 7 | - All of the **3 to 8 neighbours** (depending on where you hit!) of the target are reduced by the value of the target. 8 | - Numbers can be reduced only to 0 - they cannot go to negative. 9 | 10 | For example, if we have the following matrix: 11 | 12 | ``` 13 | 10 10 10 14 | 10 9 10 15 | 10 10 10 16 | ``` 17 | 18 | and we drop bomb at `9`, this will result in the following matrix: 19 | 20 | ``` 21 | 1 1 1 22 | 1 9 1 23 | 1 1 1 24 | ``` 25 | 26 | Implement a function called `matrix_bombing_plan(m)`. 27 | 28 | The function should return a dictionary where keys are positions in the matrix, represented as tuples, and values are the total sum of the elements of the matrix, after the bombing at that position. 29 | 30 | The positions are the standard indexes, starting from `(0, 0)` 31 | 32 | For example if we have the following matrix: 33 | 34 | ``` 35 | 1 2 3 36 | 4 5 6 37 | 7 8 9 38 | ``` 39 | 40 | and run the function, we will have: 41 | 42 | ```python 43 | {(0, 0): 42, 44 | (0, 1): 36, 45 | (0, 2): 37, 46 | (1, 0): 30, 47 | (1, 1): 15, 48 | (1, 2): 23, 49 | (2, 0): 29, 50 | (2, 1): 15, 51 | (2, 2): 26} 52 | ``` 53 | -------------------------------------------------------------------------------- /C01-Python-Basics/29-C01P18/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P18S01 - Matrix bombing Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://www.youtube.com/watch?v=gyKhC7yApMs) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/29-C01P18/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | 3 | from copy import deepcopy 4 | 5 | 6 | def sum_matrix(matrix): 7 | return sum(sum(row) for row in matrix) 8 | 9 | 10 | def outside_of_bounds(point, matrix): 11 | x, y = point 12 | 13 | MIN_X = 0 14 | MAX_X = len(matrix) - 1 15 | 16 | MIN_Y = 0 17 | MAX_Y = len(matrix[0]) - 1 18 | 19 | return x < MIN_X or x > MAX_X or y < MIN_Y or y > MAX_Y 20 | 21 | 22 | def bomb_matrix(point, matrix): 23 | # -1, 0, 1 без (0, 0) 24 | moves = [ 25 | (0, 1), 26 | (1, 0), 27 | 28 | (1, 1), 29 | (-1, -1), 30 | 31 | (-1, 1), 32 | (1, -1), 33 | 34 | (-1, 0), 35 | (0, -1) 36 | ] 37 | 38 | point_x, point_y = point 39 | bomb_value = matrix[point_x][point_y] 40 | 41 | points = [ 42 | (point_x + move[0], point_y + move[1]) 43 | for move in moves 44 | ] 45 | 46 | for x, y in points: 47 | if outside_of_bounds((x, y), matrix): 48 | continue 49 | 50 | current_value = matrix[x][y] 51 | new_value = current_value - bomb_value 52 | 53 | matrix[x][y] = max(0, new_value) 54 | 55 | return sum_matrix(matrix) 56 | 57 | 58 | def home_grown_matrix_deepcopy(matrix): 59 | result = [] 60 | 61 | for row in matrix: 62 | new_row = [] 63 | 64 | for item in row: 65 | new_row.append(item) 66 | 67 | result.append(new_row) 68 | 69 | return result 70 | 71 | 72 | def matrix_bombing_plan(matrix): 73 | result = {} 74 | 75 | for x in range(len(matrix)): 76 | for y in range(len(matrix[0])): 77 | point = (x, y) 78 | pprint(matrix) 79 | result[point] = bomb_matrix(point, deepcopy(matrix)) 80 | 81 | return result 82 | 83 | 84 | matrix = [ 85 | [1, 2, 3], 86 | [4, 5, 6], 87 | [7, 8, 9] 88 | ] 89 | 90 | expected = { 91 | (0, 0): 42, 92 | (0, 1): 36, 93 | (0, 2): 37, 94 | (1, 0): 30, 95 | (1, 1): 15, 96 | (1, 2): 23, 97 | (2, 0): 29, 98 | (2, 1): 15, 99 | (2, 2): 26 100 | } 101 | 102 | result = matrix_bombing_plan(matrix) 103 | 104 | print(result == expected) 105 | # pretty print 106 | pprint(result) 107 | -------------------------------------------------------------------------------- /C01-Python-Basics/30-C01P19/README.md: -------------------------------------------------------------------------------- 1 | # C01P19 - Stranger forms 2 | 3 | Ivan and his friends love going to the cinema (that's pre-pandemic). 4 | 5 | But when it comes to picking seats, they don't like the usual & boring "next to each other in one row" placement. 6 | 7 | That's why they always pick their seats in a strange way: 8 | 9 | - Maria should be above Ivan. 10 | - Georgi should be to the right of Maria. 11 | - Veronika should be above Maria. 12 | 13 | And when they are done, they can take really strange forms (at least, strange in the eyes of the cinema owners). 14 | 15 | Here's a way they can be placed, using the description above: 16 | 17 | ``` 18 | ...*..V.*****.. 19 | ..*..*MG..*.*.. 20 | ....*.I...*.... 21 | ``` 22 | 23 | Where: 24 | 25 | * `.` represents an empty seat. 26 | * `*` represents a reserved seat. 27 | * `I`, `M`, `G` and `V` are the first letters of the respective people. 28 | 29 | ## The task 30 | 31 | Implement the following function: 32 | 33 | ```python 34 | def stranger_forms(cinema_layout, friends_configuration): 35 | pass 36 | ``` 37 | 38 | You'll be given 2 arguments: 39 | 40 | - The cinema layout 41 | - The configuration our friends want to take. 42 | 43 | The function should return a list of **all possible placements**, that satisfy the given form. This is basically a list of possible cinema layouts (structure the layouts the same way as the input) 44 | 45 | Possible placement is a configuration where: 46 | 47 | * Our friends can book seats in the way they want. 48 | * They are not going outside of the cinema. 49 | * They are not taking any already reserved seats. 50 | 51 | The cinema layout will be structured as list of strings: 52 | 53 | ```python 54 | ['..*...*.**', 55 | '.....**...', 56 | '*.*...*..*', 57 | '.**....*.*', 58 | '...*..*.*.', 59 | '.***...*..', 60 | '*......*.*', 61 | '.....**..*', 62 | '..*.*.*..*', 63 | '***.*.**..'] 64 | ``` 65 | 66 | Friends configuration will also be given as a list of strings: 67 | 68 | ```python 69 | ["A", "BAA", "FRA", "CAB", "DRC", "EAD", "GLE"] 70 | ``` 71 | 72 | Lets break it down: 73 | 74 | * `A` - that's the first letter of the name of someone, who is going to be "central" for the configuration. 75 | * `BAA` - means - person with name `B` will be `Above` the person with name `A`. 76 | * `FRA` - means - person with name `F` will be `Right` of the person with name `A`. 77 | * `CAB` - means - person with name `C` will be `Above` the person with name `B`. 78 | * `DRC` - means - person with name `D` will be `Right` of person with name `C`. 79 | * `EAD` - means - person with name `E` will be `Above` the person with name `D`. 80 | * `GLE` means - person with name `G` will be `Left` of the person with name `E`. 81 | 82 | Few things to consider: 83 | 84 | * The input will be correct - there won't be 2 people occupying the same place. 85 | * All names are going to be unique. 86 | * There won't be a configuration for someone not being previously introduced. 87 | 88 | ## Examples 89 | 90 | If we take the input from above, here're all the possible configurations: 91 | 92 | ``` 93 | ..*GE.*.** 94 | ...CD**... 95 | *.*B..*..* 96 | .**AF..*.* 97 | ...*..*.*. 98 | .***...*.. 99 | *......*.* 100 | .....**..* 101 | ..*.*.*..* 102 | ***.*.**.. 103 | ``` 104 | 105 | ``` 106 | ..*...*.** 107 | .....**... 108 | *.*.GE*..* 109 | .**.CD.*.* 110 | ...*B.*.*. 111 | .***AF.*.. 112 | *......*.* 113 | .....**..* 114 | ..*.*.*..* 115 | ***.*.**.. 116 | ``` 117 | 118 | ``` 119 | ..*...*.** 120 | .....**... 121 | *.*...*..* 122 | .**.GE.*.* 123 | ...*CD*.*. 124 | .***B..*.. 125 | *...AF.*.* 126 | .....**..* 127 | ..*.*.*..* 128 | ***.*.**.. 129 | ``` 130 | -------------------------------------------------------------------------------- /C01-Python-Basics/30-C01P19/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C01P19S01 - Stranger forms Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video](https://youtu.be/AFDltj6MLU8) 5 | -------------------------------------------------------------------------------- /C01-Python-Basics/30-C01P19/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | # SCREAMING_SNAKE_CASE 4 | MOVEMENTS = { 5 | "A": (-1, 0), 6 | "B": (1, 0), 7 | "L": (0, -1), 8 | "R": (0, 1) 9 | } 10 | 11 | 12 | def outside_of_bounds(point, matrix): 13 | x, y = point 14 | 15 | MIN_X = 0 16 | MAX_X = len(matrix) - 1 17 | 18 | MIN_Y = 0 19 | MAX_Y = len(matrix[0]) - 1 20 | 21 | return x < MIN_X or x > MAX_X or y < MIN_Y or y > MAX_Y 22 | 23 | 24 | def add_points(p, q): 25 | px, py = p 26 | qx, qy = q 27 | 28 | return (px + qx, py + qy) 29 | 30 | 31 | def print_layout(configuration, layout): 32 | layout = deepcopy([list(row) for row in layout]) 33 | 34 | for name, point in configuration.items(): 35 | x, y = point 36 | layout[x][y] = name 37 | 38 | for row in layout: 39 | print("".join(row)) 40 | 41 | 42 | def build_layout(configuration, layout): 43 | layout = deepcopy([list(row) for row in layout]) 44 | 45 | for name, point in configuration.items(): 46 | x, y = point 47 | layout[x][y] = name 48 | 49 | new_layout = [] 50 | 51 | for row in layout: 52 | new_layout.append("".join(row)) 53 | 54 | return new_layout 55 | 56 | 57 | def in_cinema(point, cinema_layout): 58 | x, y = point 59 | 60 | return ( 61 | # short circuit 62 | not outside_of_bounds(point, cinema_layout) 63 | and cinema_layout[x][y] == "." 64 | ) 65 | 66 | 67 | def build_friends_relative_position(friends_configuration): 68 | relative_position = {} 69 | 70 | for configuration in friends_configuration: 71 | relative_position[configuration[0]] = (0, 0) 72 | 73 | for configuration in friends_configuration: 74 | if len(configuration) == 1: 75 | continue 76 | 77 | name, position, relative_to = configuration 78 | 79 | relative_x, relative_y = relative_position[relative_to] 80 | dx, dy = MOVEMENTS[position] 81 | 82 | relative_position[name] = (relative_x + dx, relative_y + dy) 83 | 84 | return relative_position 85 | 86 | 87 | def stranger_forms(cinema_layout, friends_configuration): 88 | result = [] 89 | friends_relative_position = build_friends_relative_position( 90 | friends_configuration 91 | ) 92 | 93 | for x in range(len(cinema_layout)): 94 | for y in range(len(cinema_layout[0])): 95 | if cinema_layout[x][y] == "*": 96 | continue 97 | 98 | # Do we satisfy friends_configuration for (x, y) 99 | # If yes, build new layout with configuration 100 | # Push to result 101 | friends_current_position = { 102 | name: add_points((x, y), relative_point) 103 | for name, relative_point in friends_relative_position.items() 104 | } 105 | 106 | # Dict comprehension above is the same as: 107 | # friends_current_position = {} 108 | # for name, point in friends_relative_position.items(): 109 | # friends_current_position[name] = add_points((x, y), point) 110 | 111 | all_points_in = True 112 | 113 | for point in friends_current_position.values(): 114 | if not in_cinema(point, cinema_layout): 115 | all_points_in = False 116 | break 117 | 118 | if all_points_in: 119 | print_layout(friends_current_position, cinema_layout) 120 | print('---------------------------------') 121 | 122 | result.append( 123 | build_layout( 124 | friends_current_position, 125 | cinema_layout 126 | ) 127 | ) 128 | 129 | return result 130 | 131 | 132 | cinema_layout = [ 133 | '..*...*.**', 134 | '.....**...', 135 | '*.*...*..*', 136 | '.**....*.*', 137 | '...*..*.*.', 138 | '.***...*..', 139 | '*......*.*', 140 | '.....**..*', 141 | '..*.*.*..*', 142 | '***.*.**..' 143 | ] 144 | 145 | friends_configuration = ["A", "BAA", "FRA", "CAB", "DRC", "EAD", "GLE"] 146 | 147 | stranger_forms(cinema_layout, friends_configuration) 148 | -------------------------------------------------------------------------------- /C01-Python-Basics/README.md: -------------------------------------------------------------------------------- 1 | # C01 - Python Basics 2 | 3 | Follow this order: 4 | 5 | | Order | Name | Link | 6 | |-------|----------------------------------------------|--------------------------| 7 | | 1 | C01V01 - Basic Language Constructs | [Click here](01-C01V01/) | 8 | | 2 | C01V02 - REPL | [Click here](02-C01V02/) | 9 | | 3 | C01V03 - If statements | [Click here](03-C01V03/) | 10 | | 4 | C01V04 - Lists | [Click here](04-C01V04/) | 11 | | 5 | C01V05 - Tuples | [Click here](05-C01V05/) | 12 | | 6 | C01V06 - Dictionaries | [Click here](06-C01V06/) | 13 | | 7 | C01V07 - Sets | [Click here](07-C01V07/) | 14 | | 8 | C01V08 - Common patterns | [Click here](08-C01V08/) | 15 | | 9 | C01V09 - Functions | [Click here](09-C01V09/) | 16 | | 10 | C01V10 - Pass by assignment | [Click here](10-C01V10/) | 17 | | 11 | C01V11 - How to write the actual Python code | [Click here](11-C01V11/) | 18 | | 12 | C01P01 - IBAN formatter | [Click here](12-C01P01/) | 19 | | 13 | C01P02 - Sum of digits | [Click here](13-C01P02/) | 20 | | 14 | C01P03 - Factorial digits | [Click here](14-C01P03/) | 21 | | 15 | C01P04 - Char histogram | [Click here](15-C01P04/) | 22 | | 16 | C01P05 - Sum matrix | [Click here](16-C01P05/) | 23 | | 17 | C01P06 - NaN expand | [Click here](17-C01P06/) | 24 | | 18 | C01P07 - Increasing and decreasing | [Click here](18-C01P07/) | 25 | | 19 | C01P08 - Group | [Click here](19-C01P08/) | 26 | | 20 | C01P09 - Balanced numbers | [Click here](20-C01P09/) | 27 | | 21 | C01P10 - Palindromes count | [Click here](21-C01P10/) | 28 | | 22 | C01P11 - Anagrams | [Click here](22-C01P11/) | 29 | | 23 | C01P12 - Credit card validation | [Click here](23-C01P12/) | 30 | | 24 | C01P13 - Integer prime factorization | [Click here](24-C01P13/) | 31 | | 25 | C01P14 - Goldback conjecture | [Click here](25-C01P14/) | 32 | | 26 | C01P15 - Gas stations | [Click here](26-C01P15/) | 33 | | 27 | C01P16 - Nokia keypad | [Click here](27-C01P16/) | 34 | | 28 | C01P17 - Word counter | [Click here](28-C01P17/) | 35 | | 29 | C01P18 - Matrix bombing | [Click here](29-C01P18/) | 36 | | 30 | C01P19 - Stranger forms | [Click here](30-C01P19/) | 37 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/01-C02V01/README.md: -------------------------------------------------------------------------------- 1 | # C02V01 - Why Linux? 2 | 3 | **Materials:** 4 | 5 | * [Slides](https://docs.google.com/presentation/d/1W24BzqWhyWPWkoz_TI9eiHAhoj3nRbZ19OYJ_qMtwXw/edit?usp=sharing) 6 | * [Video](https://www.youtube.com/watch?v=l4j1_fai3ew) 7 | 8 | **Materials to follow up:** 9 | 10 | 1. 11 | 1. 12 | 1. 13 | 1. [Unix vs Linux](https://www.youtube.com/watch?v=jowCUo_UGts) 14 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/02-C02V02/README.md: -------------------------------------------------------------------------------- 1 | # C02V02 - Why Ubuntu? 2 | 3 | ## Materials 4 | 5 | * [Video](https://youtu.be/hl-IZ79d3U8) 6 | * [Slides](https://docs.google.com/presentation/d/1Vyfz7eHe32DOd3dAdF0lvlb4sRXWcEU7vtPWkq72tBg/edit?usp=sharing) 7 | 8 | ## Additional materials 9 | 10 | 1. 11 | 1. 12 | 1. 13 | 1. [Why so many distros? The Weird History of Linux](https://www.youtube.com/watch?v=ShcR4Zfc6Dw) 14 | 15 | ## How to install 16 | 17 | **Bootable USB:** 18 | 19 | 1. [Create a bootable Ubuntu USB on Windows](https://ubuntu.com/tutorials/create-a-usb-stick-on-windows#1-overview) 20 | 1. [Create a bootable Ubuntu USB on Ubuntu](https://ubuntu.com/tutorials/create-a-usb-stick-on-ubuntu#1-overview) 21 | 1. [Create a bootable Ubuntu USB on macOS](https://ubuntu.com/tutorials/create-a-usb-stick-on-macos#1-overview) 22 | 23 | **Dual boot installation:** 24 | 25 | 1. [Official Dual boot guide from Ubuntu](https://help.ubuntu.com/community/WindowsDualBoot) 26 | 1. [How to Install Ubuntu Alongside Windows 10](https://itsfoss.com/install-ubuntu-1404-dual-boot-mode-windows-8-81-uefi/) 27 | 1. [How to install Ubuntu 20.04 alongside Windows 10 (Dual Boot)](https://linuxconfig.org/how-to-install-ubuntu-20-04-alongside-windows-10-dual-boot) 28 | 29 | 30 | **Fresh install:** 31 | 32 | 1. [Official guide from Ubuntu](https://ubuntu.com/tutorials/install-ubuntu-desktop#1-overview) 33 | 34 | 35 | **Ubuntu on WSL:** 36 | 37 | 1. [Official guide from Ubuntu](https://ubuntu.com/wsl) 38 | 39 | 40 | **Virtual machine:** 41 | 42 | 1. [Install Linux Inside Windows Using VirtualBox](https://itsfoss.com/install-linux-in-virtualbox/) 43 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/03-C02V03/README.md: -------------------------------------------------------------------------------- 1 | # C02V03 - File system 2 | 3 | ## Materials 4 | 5 | * [Video](https://www.youtube.com/watch?v=YCr326uvLm4) 6 | * [Slides](https://docs.google.com/presentation/d/1WASN0xYQNL3zlRavGSejqrN9UxvC02Qdd2A6MWVAtNc/edit?usp=sharing) 7 | 8 | ## Additional materials 9 | 10 | * 11 | * [Linux File System/Structure Explained!](https://www.youtube.com/watch?v=HbgzrKJvDRw) 12 | * [Linux Directories Explained - including /etc /home /var /proc /usr](https://www.youtube.com/watch?v=PEaixsvzRUk) 13 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/04-C02V04/README.md: -------------------------------------------------------------------------------- 1 | # C02V04 - File system: Basic commands 2 | 3 | ## Materials 4 | 5 | * [Video](https://youtu.be/fE6o0fiG53E) 6 | * [Slides](https://docs.google.com/presentation/d/1WASN0xYQNL3zlRavGSejqrN9UxvC02Qdd2A6MWVAtNc/edit?usp=sharing) 7 | 8 | ## Additional materials 9 | 10 | * 11 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/05-C02P01/README.md: -------------------------------------------------------------------------------- 1 | # C02P01 - Reduce file path 2 | 3 | A file path in a Unix-like operating system looks like this: 4 | 5 | ``` 6 | /home/user/courses/Programming101-Python/week01 7 | ``` 8 | 9 | We start from the root - `/` and we navigate to the destination folder. 10 | 11 | But there is a problem - if we have `..` and `.` in our file path, it's not clear where we are going to end up. 12 | 13 | - `..` means to go back one directory. 14 | - `.` means to stay in the same directory. 15 | - we can have more then one `/` between the directories - `/home//code`. 16 | 17 | So for example: 18 | 19 | ``` 20 | /home//user/courses/./Programming101-Python/week01/../ 21 | ``` 22 | 23 | reduces to 24 | 25 | ``` 26 | /home/user/courses/Programming101-Python 27 | ``` 28 | 29 | Implement a function, called `reduce_file_path(path)` which takes a string and returns the reduced version of the path. 30 | 31 | - Every `..` means that we have to go one directory back. 32 | - Every `.` means that we are staying in the same directory. 33 | - Every extra `/` is unnecessary. 34 | - Always remove the last `/`. 35 | 36 | **Avoid using a standard library for dealing with Unix paths.** 37 | 38 | ## Signature 39 | 40 | ```python 41 | def reduce_file_path(path): 42 | pass 43 | ``` 44 | 45 | ## Examples 46 | 47 | ```python 48 | reduce_file_path("/") == "/" 49 | reduce_file_path("/srv/../") == "/" 50 | reduce_file_path("/srv/www/htdocs/wtf/") == "/srv/www/htdocs/wtf" 51 | reduce_file_path("/srv/www/htdocs/wtf") == "/srv/www/htdocs/wtf" 52 | reduce_file_path("/srv/./././././") == "/srv" 53 | reduce_file_path("/etc//wtf/") == "/etc/wtf" 54 | reduce_file_path("/etc/../etc/../etc/../") == "/" 55 | reduce_file_path("//////////////") == "/" 56 | reduce_file_path("/../") == "/" 57 | ``` 58 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/05-C02P01/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C02P01S01 - Reduce file path Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video for the solution](https://youtu.be/Ogm9lgW-nuw) 5 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/05-C02P01/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # https://youtu.be/Ogm9lgW-nuw 2 | from collections import deque 3 | 4 | 5 | def reduce_file_path(path): 6 | result = deque() 7 | 8 | for i in path.split('/'): 9 | if i == '..' and len(result) > 0: 10 | result.pop() 11 | if i not in ('.', '..', ''): 12 | result.append(i) 13 | return '/' + '/'.join(result) 14 | 15 | 16 | result = [ 17 | reduce_file_path( 18 | '/home//user/courses/./Programming101-Python/week01/../' 19 | ) == '/home/user/courses/Programming101-Python', 20 | reduce_file_path('/') == '/', 21 | reduce_file_path('/srv/../') == '/', 22 | reduce_file_path('/srv/www/htdocs/wtf/') == '/srv/www/htdocs/wtf', 23 | reduce_file_path('/srv/www/htdocs/wtf') == '/srv/www/htdocs/wtf', 24 | reduce_file_path('/srv/./././././') == '/srv', 25 | reduce_file_path('/etc//wtf/') == '/etc/wtf', 26 | reduce_file_path('/etc/../etc/../etc/../') == '/', 27 | reduce_file_path('//////////////') == '/', 28 | reduce_file_path('/../../../../../../../') == '/' 29 | ] 30 | print(result) 31 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/06-C02V05/README.md: -------------------------------------------------------------------------------- 1 | # C02V05 - Terminal commands: Part 1 2 | 3 | We talk about terminal, commands & command arguments. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/cW2yiPirm7k) 8 | * [Slides](https://docs.google.com/presentation/d/16WI4srns-OKK1zffAX5FYqo_EqrMzDh2HqfilOFZ140/edit?usp=sharing) 9 | 10 | ## Code examples 11 | 12 | * [example.py](example.py) 13 | * [ls.py](ls.py) 14 | 15 | ## Additional materials 16 | 17 | * 18 | * 19 | * 20 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/06-C02V05/example.py: -------------------------------------------------------------------------------- 1 | # example.py 2 | import sys 3 | 4 | print(sys.argv) 5 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/06-C02V05/ls.py: -------------------------------------------------------------------------------- 1 | # ls.py 2 | import sys 3 | import os 4 | 5 | show_hidden = len(sys.argv) > 1 and sys.argv[1] == "-a" 6 | 7 | for directory_item in os.listdir("."): 8 | if directory_item.startswith("."): 9 | if not show_hidden: 10 | continue 11 | 12 | print(directory_item) 13 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/07-C02V06/README.md: -------------------------------------------------------------------------------- 1 | # C02V06 - Terminal commands: Part 2 2 | 3 | We talk about environment variables. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/wVEqsIKkNQ8) 8 | * [Slides](https://docs.google.com/presentation/d/1SodKkwW_n9btscEWPFQ1T0ai_ogRins87DZtDzUgLg4/edit?usp=sharing) 9 | 10 | ## Code examples 11 | 12 | * [example.py](example.py) 13 | 14 | ## Additional materials 15 | 16 | * 17 | * 18 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/07-C02V06/example.py: -------------------------------------------------------------------------------- 1 | # example.py 2 | 3 | import os 4 | 5 | value = os.environ.get("PYTHON_101_FOREVER") 6 | 7 | print(value) 8 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/README.md: -------------------------------------------------------------------------------- 1 | # C02V07 - Terminal commands: Part 3 2 | 3 | We talk about sdtdin / stdout / stderr. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/yY5iYsufebI) 8 | * [Slides](https://docs.google.com/presentation/d/1GBXE3oFUQj-PH3q2YfwfYaMBuI9D7tZQtlA7XxVLPBU/edit?usp=sharing) 9 | 10 | ## Code examples 11 | 12 | * [stdin.py](stdin.py) 13 | * [stdout.py](stdout.py) 14 | * [stdout_print.py](stdout_print.py) 15 | * [stdout_stderr.py](stdout_stderr.py) 16 | * [combined.py](combined.py) 17 | 18 | ## Additional materials 19 | 20 | * 21 | * 22 | * 23 | * 24 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/combined.py: -------------------------------------------------------------------------------- 1 | # combined.py 2 | import sys 3 | 4 | for line in sys.stdin: 5 | if "And" in line: 6 | sys.stdout.write(line) 7 | else: 8 | sys.stderr.write(line) 9 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/poem.txt: -------------------------------------------------------------------------------- 1 | Deep into that darkness peering, 2 | Long I stood there, wondering, fearing, 3 | Doubting, dreaming dreams no mortals 4 | Ever dared to dream before; 5 | But the silence was unbroken, 6 | And the stillness gave no token, 7 | And the only word there spoken 8 | Was the whispered word, "Lenore!" 9 | This I whispered, and an echo 10 | Murmured back the word, "Lenore!" 11 | Merely this, and nothing more. 12 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/stderr.py: -------------------------------------------------------------------------------- 1 | # stderr.py 2 | 3 | import sys 4 | 5 | sys.stderr.write("Python speaking") 6 | sys.stderr.write("Do you hear me?") 7 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/stdin.py: -------------------------------------------------------------------------------- 1 | # stdin.py 2 | import sys 3 | 4 | 5 | for line in sys.stdin: 6 | continue 7 | 8 | 9 | print("EOF reached") 10 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/stdout.py: -------------------------------------------------------------------------------- 1 | # stdout.py 2 | 3 | import sys 4 | 5 | sys.stdout.write("Python speaking") 6 | sys.stdout.write("Do you hear me?") 7 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/stdout_print.py: -------------------------------------------------------------------------------- 1 | # stdout_print.py 2 | print("Python speaking") 3 | print("Do you hear me?") 4 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/08-C02V07/stdout_stderr.py: -------------------------------------------------------------------------------- 1 | # stdout_stderr.py 2 | import sys 3 | 4 | sys.stdout.write("stdout goes here") 5 | sys.stderr.write("stderr goes here") 6 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/09-C02V08/README.md: -------------------------------------------------------------------------------- 1 | # C02V08 - Terminal commands: Part 4 2 | 3 | We talk about Unix pipes. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/ieKVMFA3Ut0) 8 | * [Slides](https://docs.google.com/presentation/d/1V-tuFFfnsFn02wDFJGZAg1LZsxcRi6l83Y7oMuDU3fQ/edit?usp=sharing) 9 | 10 | ## Additional materials 11 | 12 | * 13 | * 14 | * 15 | * 16 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/10-C02V09/README.md: -------------------------------------------------------------------------------- 1 | # C02V09 - Terminal commands: Part 5 2 | 3 | We talk about exit codes & conditionally executing programs. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/UIg00a_R5ps) 8 | * [Slides](https://docs.google.com/presentation/d/1FOZSzCg9pV_5alfhJ-m_sTQ13Yp_1yNI1Pch0S3qyTA/edit?usp=sharing) 9 | 10 | ## Code examples 11 | 12 | * [false.py](false.py) 13 | * [true.py](true.py) 14 | 15 | ## Additional materials 16 | 17 | * 18 | * 19 | * 20 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/10-C02V09/false.py: -------------------------------------------------------------------------------- 1 | # false.py 2 | import sys 3 | 4 | sys.exit(1) 5 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/10-C02V09/true.py: -------------------------------------------------------------------------------- 1 | # true.py 2 | import sys 3 | 4 | sys.exit(0) 5 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/11-C02V10/README.md: -------------------------------------------------------------------------------- 1 | # C02V09 - Terminal commands: Part 6 2 | 3 | We talk about various different topics, including man pages, `sudo`, `PATH` and `.bashrc` 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/cV8E2BrCF9k) 8 | * [Slides](https://docs.google.com/presentation/d/11ESxwQ8p6zEES-LHSFcM1wDq8xeCl1Z5wIn4OpnNqt8/edit?usp=sharing) 9 | 10 | ## Other materials 11 | 12 | * 13 | * 14 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/README.md: -------------------------------------------------------------------------------- 1 | # C02P02 - gson 2 | 3 | Implement a Python script called `gson.py` that reads 2 arguments: 4 | 5 | - Path to a JSON file. 6 | - Path inside the JSON file, which can describe properties & array indices. 7 | 8 | The program should output: 9 | 10 | - To stdout, the value given from the path. In case the program fails, it should exit with a non-zero status. 11 | - To stderr, if the file is not a proper JSON or the path cannot be found in the JSON. 12 | 13 | ## Examples 14 | 15 | Lets say we have the following `example.json` file: 16 | 17 | ``` 18 | $ cat example.json 19 | ``` 20 | 21 | which outputs ([taken from here](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON)) 22 | 23 | ```json 24 | { 25 | "squadName": "Super hero squad", 26 | "homeTown": "Metro City", 27 | "formed": 2016, 28 | "secretBase": "Super tower", 29 | "active": true, 30 | "properties": { 31 | "foo": "bar" 32 | }, 33 | "members": [ 34 | { 35 | "name": "Molecule Man", 36 | "age": 29, 37 | "secretIdentity": "Dan Jukes", 38 | "powers": [ 39 | "Radiation resistance", 40 | "Turning tiny", 41 | "Radiation blast" 42 | ] 43 | }, 44 | { 45 | "name": "Madame Uppercut", 46 | "age": 39, 47 | "secretIdentity": "Jane Wilson", 48 | "powers": [ 49 | "Million tonne punch", 50 | "Damage resistance", 51 | "Superhuman reflexes" 52 | ] 53 | }, 54 | { 55 | "name": "Eternal Flame", 56 | "age": 1000000, 57 | "secretIdentity": "Unknown", 58 | "powers": [ 59 | "Immortality", 60 | "Heat Immunity", 61 | "Inferno", 62 | "Teleportation", 63 | "Interdimensional travel" 64 | ] 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | Now, if we have our `gson.py` in the same directory, we should be able to run the following: 71 | 72 | ``` 73 | $ python gson.py example.json "members[0].name" 74 | ``` 75 | 76 | which outputs: 77 | 78 | ``` 79 | Molecule Man 80 | ``` 81 | 82 | Basically, the second argument describes a path in the JSON file. 83 | 84 | Here are some more examples: 85 | 86 | ``` 87 | $ python gson.py example.json "members[0].powers[1]" 88 | Turning tiny 89 | ``` 90 | 91 | ``` 92 | $ python gson.py example.json "squadName" 93 | Super hero squad 94 | ``` 95 | 96 | ``` 97 | $ python gson.py example.json "properties" 98 | { 99 | "foo": "bar" 100 | } 101 | ``` 102 | 103 | ``` 104 | $ python gson.py example.json "properties.foo" 105 | bar 106 | ``` 107 | 108 | ``` 109 | $ python gson.py example.json "shano.property" 110 | Error: Property not found 111 | 112 | $ echo $? 113 | 1 114 | ``` 115 | 116 | We can even have more weird JSON formats: 117 | 118 | ```json 119 | { 120 | "items": [ 121 | [1], 122 | [2] 123 | ] 124 | } 125 | ``` 126 | 127 | ``` 128 | $ python gson.py weird.json "items[0]" 129 | [1] 130 | 131 | $ python gson.py weird.json "items[0][0]" 132 | 1 133 | ``` 134 | 135 | ## Hints 136 | 137 | - We are effectively trying to mimick or . 138 | - Use to read & parse the JSON file. 139 | - Use `sys.argv` to read the arguments. 140 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C02P02S01 - gson Solution 01 2 | 3 | This is the raw solution that came out from the live. 4 | 5 | As an exercise, try improving the code. 6 | 7 | 1. Check [solution.py](./solution.py) 8 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # This is a raw solution, recorded during a Discord Live 2 | # Try improving it! 3 | 4 | import json 5 | 6 | 7 | example = """{ 8 | "squadName": "Super hero squad", 9 | "homeTown": "Metro City", 10 | "formed": 2016, 11 | "secretBase": "Super tower", 12 | "active": true, 13 | "properties": { 14 | "foo": "bar" 15 | }, 16 | "members": [ 17 | { 18 | "name": "Molecule Man", 19 | "age": 29, 20 | "secretIdentity": "Dan Jukes", 21 | "powers": [ 22 | "Radiation resistance", 23 | "Turning tiny", 24 | "Radiation blast" 25 | ] 26 | }, 27 | { 28 | "name": "Madame Uppercut", 29 | "age": 39, 30 | "secretIdentity": "Jane Wilson", 31 | "powers": [ 32 | "Million tonne punch", 33 | "Damage resistance", 34 | "Superhuman reflexes" 35 | ] 36 | }, 37 | { 38 | "name": "Eternal Flame", 39 | "age": 1000000, 40 | "secretIdentity": "Unknown", 41 | "powers": [ 42 | "Immortality", 43 | "Heat Immunity", 44 | "Inferno", 45 | "Teleportation", 46 | "Interdimensional travel" 47 | ] 48 | } 49 | ] 50 | }""" 51 | 52 | example_2 = """{ 53 | "items": [ 54 | [[1], 3], 55 | [2] 56 | ] 57 | }""" 58 | 59 | 60 | def get_attribute(structure, attribute_name): 61 | if type(attribute_name) is int: 62 | return structure[attribute_name] 63 | 64 | if attribute_name.endswith(']'): 65 | attribute, index = get_attribute_and_index(attribute_name) 66 | return get_attribute(get_attribute(structure, attribute), index) 67 | 68 | return structure[attribute_name] 69 | 70 | 71 | def get_attribute_and_index(attribute_name): 72 | # we should handle indexes greater than 9 73 | attribute = attribute_name[:-3] 74 | index = int(attribute_name[-2:-1]) 75 | 76 | return attribute, index 77 | 78 | 79 | def deep_get(structure, path): 80 | attribute_chain = path.split('.') 81 | 82 | try: 83 | current = get_attribute(structure, attribute_chain[0]) 84 | for a in attribute_chain[1:]: 85 | current = get_attribute(current, a) 86 | except (IndexError, KeyError): 87 | print('Input not valid!') 88 | return -1 89 | 90 | return current 91 | 92 | 93 | def gson(json_file, path): 94 | return deep_get(json.loads(json_file), path) 95 | 96 | 97 | print(gson(example_2, 'items[0][1]')) 98 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/Solution02/README.md: -------------------------------------------------------------------------------- 1 | # C02P02S02 - gson Solution 02 2 | 3 | This is a non-live recorded solution. 4 | 5 | 1. Check [the video](https://youtu.be/R_RY5BzK4fI) 6 | 1. Check [solution.py](./solution.py) 7 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/Solution02/solution.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | attribute_path = "members[0].name" 5 | 6 | 7 | def deep_get(example_dict, attribute_path): 8 | attribute_chain = attribute_path.split('.') 9 | 10 | try: 11 | current = get_attribute(example_dict, attribute_chain[0]) 12 | for a in attribute_chain[1:]: 13 | current = get_attribute(current, a) 14 | except KeyError: 15 | print("Input not correct!") 16 | print(-1) 17 | return 18 | 19 | return current 20 | 21 | 22 | def get_attribute(structure, attribute): 23 | if type(attribute) is int: 24 | return structure[attribute] 25 | if attribute.endswith(']'): 26 | index, attribute_name = extract_index_and_attribute(attribute) 27 | return get_attribute(get_attribute(structure, attribute_name), index) 28 | 29 | return structure[attribute] 30 | 31 | 32 | def extract_index_and_attribute(attribute): 33 | index = attribute[-2:-1] 34 | attribute_name = attribute[:-3] 35 | return int(index), attribute_name 36 | 37 | 38 | example_dict = json.loads("""{ 39 | "squadName": "Super hero squad", 40 | "homeTown": "Metro City", 41 | "formed": 2016, 42 | "secretBase": "Super tower", 43 | "active": true, 44 | "properties": { 45 | "foo": "bar" 46 | }, 47 | "members": [ 48 | { 49 | "name": "Molecule Man", 50 | "age": 29, 51 | "secretIdentity": "Dan Jukes", 52 | "powers": [ 53 | "Radiation resistance", 54 | "Turning tiny", 55 | "Radiation blast" 56 | ] 57 | }, 58 | { 59 | "name": "Madame Uppercut", 60 | "age": 39, 61 | "secretIdentity": "Jane Wilson", 62 | "powers": [ 63 | "Million tonne punch", 64 | "Damage resistance", 65 | "Superhuman reflexes" 66 | ] 67 | }, 68 | { 69 | "name": "Eternal Flame", 70 | "age": 1000000, 71 | "secretIdentity": "Unknown", 72 | "powers": [ 73 | "Immortality", 74 | "Heat Immunity", 75 | "Inferno", 76 | "Teleportation", 77 | "Interdimensional travel" 78 | ] 79 | } 80 | ] 81 | }""") 82 | 83 | example_dict_2 = json.loads("""{ 84 | "items": [ 85 | [[1]], 86 | [2] 87 | ] 88 | }""") 89 | 90 | print(deep_get(example_dict_2, "items[1][0]")) 91 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "squadName": "Super hero squad", 3 | "homeTown": "Metro City", 4 | "formed": 2016, 5 | "secretBase": "Super tower", 6 | "active": true, 7 | "properties": { 8 | "foo": "bar" 9 | }, 10 | "members": [ 11 | { 12 | "name": "Molecule Man", 13 | "age": 29, 14 | "secretIdentity": "Dan Jukes", 15 | "powers": [ 16 | "Radiation resistance", 17 | "Turning tiny", 18 | "Radiation blast" 19 | ] 20 | }, 21 | { 22 | "name": "Madame Uppercut", 23 | "age": 39, 24 | "secretIdentity": "Jane Wilson", 25 | "powers": [ 26 | "Million tonne punch", 27 | "Damage resistance", 28 | "Superhuman reflexes" 29 | ] 30 | }, 31 | { 32 | "name": "Eternal Flame", 33 | "age": 1000000, 34 | "secretIdentity": "Unknown", 35 | "powers": [ 36 | "Immortality", 37 | "Heat Immunity", 38 | "Inferno", 39 | "Teleportation", 40 | "Interdimensional travel" 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/gson.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | print(sys.argv) 4 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/12-C02P02/weird.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | [1], 4 | [2] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/13-C02V11/README.md: -------------------------------------------------------------------------------- 1 | # C02V11 - Python versions & pyenv 2 | 3 | We talk about the different Python verions & defaults & how to control them with `pyenv` 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/5mW-qi4cYv0) 8 | * [Slides](https://docs.google.com/presentation/d/1vK4JG0ASzHsHrprsKHfPPqKjnhdWR96rfTUYMXBOW6A/edit?usp=sharing) 9 | 10 | ## Other materials 11 | 12 | To get the required OS dependencies (adapted from here - ): 13 | 14 | ``` 15 | sudo apt update --fix-missing 16 | 17 | sudo apt install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev 18 | ``` 19 | 20 | To install, read the following READMEs (make sure to check the proper settings for Ubuntu): 21 | 22 | * 23 | * 24 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/14-C02V12/README.md: -------------------------------------------------------------------------------- 1 | # C02V12 - Virtual environments 2 | 3 | We talk about the why, what & how of virtual environments. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/lJnFFxKSUn0) 8 | * [Slides](https://docs.google.com/presentation/d/1zSVbDd1Sc8lpMNP6jYaV1GL4754vH3Rr6U0Kl7Wq5vw/edit?usp=sharing) 9 | 10 | ## Other materials 11 | 12 | * An extensive guide - 13 | * Use this to install - 14 | * The classic - 15 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/15-C02V13/README.md: -------------------------------------------------------------------------------- 1 | # C02V13 - Python editors 2 | 3 | We talk about the difference between an editor and an IDE and what are the options for Python. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/VUILyNncTA8) 8 | * [Slides](https://docs.google.com/presentation/d/1sfpHthhrVit4cU_gsfDnChocuEzy0zjF7IAjYMNasOQ/edit?usp=sharing) 9 | 10 | ## Other materials 11 | 12 | * 13 | -------------------------------------------------------------------------------- /C02-Linux-and-Python-Setup/README.md: -------------------------------------------------------------------------------- 1 | # C02 - Linux & Python Setup 2 | 3 | Follow this order: 4 | 5 | 6 | | Order | Name | Link | 7 | |-------|---------------------------------------------------------|--------------------------| 8 | | 1 | C02V01 - Why Linux? | [Click here](01-C02V01/) | 9 | | 2 | C02V02 - Why Ubuntu? | [Click here](02-C02V02/) | 10 | | 3 | C02V03 - File system | [Click here](03-C02V03/) | 11 | | 4 | C02V04 - File system: Basic commands | [Click here](04-C02V04/) | 12 | | 5 | C02P01 - Reduce file path | [Click here](05-C02P01/) | 13 | | 6 | C02V05 - Terminal commands: Part 1 | [Click here](06-C02V05/) | 14 | | 7 | C02V06 - Terminal commands: Part 2 | [Click here](07-C02V06/) | 15 | | 8 | C02V07 - Terminal commands: Part 3 | [Click here](08-C02V07/) | 16 | | 9 | C02V08 - Terminal commands: Part 4 | [Click here](09-C02V08/) | 17 | | 10 | C02V09 - Terminal commands: Part 5 | [Click here](10-C02V09/) | 18 | | 11 | C02V10 - Terminal commands: Part 6 | [Click here](11-C02V10/) | 19 | | 12 | C02P02 - gson | [Click here](12-C02P02/) | 20 | | 13 | C02V11 - Python versions & pyenv | [Click here](13-C02V11/) | 21 | | 14 | C02V12 - Virtual environments | [Click here](14-C02V12/) | 22 | | 15 | C02V13 - Python editors | [Click here](15-C02V13/) | 23 | -------------------------------------------------------------------------------- /C03-Unit-Testing/01-C03V01/README.md: -------------------------------------------------------------------------------- 1 | # C03V01 - Basic OOP concepts in Python 2 | 3 | We talk about the general idea behind object oriented programming & the basic syntax for a class in Python. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/nG-bSOVTvIY) 8 | * [Slides](https://docs.google.com/presentation/d/1UmRDDHKO_imJeEkwiplRDokjIhJOytCK0zvyQJfZMb4/edit?usp=sharing) 9 | * [interval.py](./interval.py) 10 | 11 | ## Other materials 12 | 13 | * Boundaries by Gary Bernhardt - 14 | -------------------------------------------------------------------------------- /C03-Unit-Testing/01-C03V01/interval.py: -------------------------------------------------------------------------------- 1 | class Interval: 2 | def __init__(self, start, end): 3 | # This is the constructor 4 | self.start = start 5 | self.end = end 6 | 7 | def is_inside(self, value): 8 | return self.start <= value <= self.end 9 | 10 | def stringify(self): 11 | start_symbol = "[" 12 | end_symbol = "]" 13 | 14 | return f"{start_symbol}{self.start}, {self.end}{end_symbol}" 15 | 16 | def __str__(self): 17 | return self.stringify() 18 | 19 | def __repr__(self): 20 | return self.stringify() 21 | 22 | 23 | # There's no "new" keyword here. 24 | interval = Interval(1, 10) 25 | print(interval.stringify()) # [1, 10] 26 | print(interval.is_inside(1)) # True 27 | print(interval.is_inside(5)) # True 28 | print(interval.is_inside(10)) # True 29 | -------------------------------------------------------------------------------- /C03-Unit-Testing/02-C03P01/README.md: -------------------------------------------------------------------------------- 1 | # C03P01 - Interval extended 2 | 3 | In `C03V01`, we showed the `Interval` class. 4 | 5 | We want to extend the interval class, so it is aware of open & closed intervals. 6 | 7 | Finish up the implementation of this: 8 | 9 | ```python 10 | class Interval: 11 | def __init__(self, start, end, start_opened=False, end_opened=False): 12 | # This is the constructor 13 | pass 14 | 15 | def is_inside(self, value): 16 | pass 17 | 18 | def stringify(self): 19 | pass 20 | ``` 21 | 22 | So it satisfies the following examples: 23 | 24 | ```python 25 | closed_interval = Interval(1, 10) 26 | 27 | closed_interval.is_inside(1) is True 28 | closed_interval.is_inside(5) is True 29 | closed_interval.is_inside(10) is True 30 | 31 | closed_interval.stringify() == "[1, 10] 32 | ``` 33 | 34 | and 35 | 36 | ```python 37 | opened_interval = Interval(1, 10, start_opened=True, end_opened=True) 38 | 39 | opened_interval.is_inside(1) is False 40 | opened_interval.is_inside(5) is True 41 | opened_interval.is_inside(10) is False 42 | 43 | opened_interval.stringify() == "(1, 10)" 44 | ``` 45 | 46 | and 47 | 48 | ```python 49 | half_opened_interval = Interval(1, 10, start_opened=False, end_opened=True) 50 | 51 | half_opened_interval.is_inside(1) is True 52 | half_opened_interval.is_inside(5) is True 53 | half_opened_interval.is_inside(10) is False 54 | 55 | half_opened_interval.stringify() == "[1, 10)" 56 | ``` 57 | 58 | and 59 | 60 | ```python 61 | half_opened_interval = Interval(1, 10, start_opened=True, end_opened=False) 62 | 63 | half_opened_interval.is_inside(1) is False 64 | half_opened_interval.is_inside(5) is True 65 | half_opened_interval.is_inside(10) is True 66 | 67 | half_opened_interval.stringify() == "(1, 10]" 68 | ``` 69 | -------------------------------------------------------------------------------- /C03-Unit-Testing/02-C03P01/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C03P01S01 - Interval extended Solution 01 2 | 3 | 1. Check [solution.py](./solution.py) 4 | 1. Check the [video](https://www.youtube.com/watch?v=ZhZcQmGrYB4) 5 | -------------------------------------------------------------------------------- /C03-Unit-Testing/02-C03P01/Solution01/solution.py: -------------------------------------------------------------------------------- 1 | # https://github.com/HackBulgaria/Python-101-Forever/tree/master/C03-Unit-Testing/02-C03P01 2 | 3 | class Interval: 4 | def __init__(self, start, end, start_opened=False, end_opened=False): 5 | # This is the constructor 6 | self.start = start 7 | self.end = end 8 | self.start_opened = start_opened 9 | self.end_opened = end_opened 10 | 11 | def is_inside(self, value): 12 | after_start = False 13 | before_end = False 14 | 15 | if self.start_opened: 16 | after_start = value > self.start 17 | else: 18 | after_start = value >= self.start 19 | 20 | if self.end_opened: 21 | before_end = value < self.end 22 | else: 23 | before_end = value <= self.end 24 | 25 | return after_start and before_end 26 | 27 | def stringify(self): 28 | # [x, y] 29 | # (x, y) 30 | # (x, y] 31 | # [x, y) 32 | 33 | open_bracket = "[" 34 | close_bracket = "]" 35 | 36 | if self.start_opened: 37 | open_bracket = "(" 38 | 39 | if self.end_opened: 40 | close_bracket = ")" 41 | 42 | return f"{open_bracket}{self.start}, {self.end}{close_bracket}" 43 | 44 | 45 | closed_interval = Interval(1, 10) 46 | 47 | print(closed_interval.is_inside(1) is True) 48 | print(closed_interval.is_inside(5) is True) 49 | print(closed_interval.is_inside(10) is True) 50 | 51 | print(closed_interval.stringify() == "[1, 10]") 52 | 53 | opened_interval = Interval(1, 10, start_opened=True, end_opened=True) 54 | 55 | print(opened_interval.is_inside(1) is False) 56 | print(opened_interval.is_inside(5) is True) 57 | print(opened_interval.is_inside(10) is False) 58 | 59 | print(opened_interval.stringify() == "(1, 10)") 60 | 61 | half_opened_interval = Interval(1, 10, start_opened=False, end_opened=True) 62 | 63 | print(half_opened_interval.is_inside(1) is True) 64 | print(half_opened_interval.is_inside(5) is True) 65 | print(half_opened_interval.is_inside(10) is False) 66 | 67 | print(half_opened_interval.stringify() == "[1, 10)") 68 | 69 | half_opened_interval = Interval(1, 10, start_opened=True, end_opened=False) 70 | 71 | print(half_opened_interval.is_inside(1) is False) 72 | print(half_opened_interval.is_inside(5) is True) 73 | print(half_opened_interval.is_inside(10) is True) 74 | 75 | print(half_opened_interval.stringify() == "(1, 10]") 76 | -------------------------------------------------------------------------------- /C03-Unit-Testing/03-C03V02/README.md: -------------------------------------------------------------------------------- 1 | # C03V02 - OOP principles 2 | 3 | In this video we discuss the 4 basic OOP principles on a higher level. 4 | 5 | ## Materials 6 | 7 | * [Video](https://www.youtube.com/watch?v=5-mCKphqUCI) 8 | * [Slides](https://docs.google.com/presentation/d/1tNzVcsIuuM0zECkJglvZ6PAqrgUcH03rUCfQ3bPsG5g/edit?usp=sharing) 9 | -------------------------------------------------------------------------------- /C03-Unit-Testing/04-C03V03/README.md: -------------------------------------------------------------------------------- 1 | # C03V03 - Unit tests 2 | 3 | In this video we introduce the concept for testing & show how we can do it with Python. 4 | 5 | ## Materials 6 | 7 | * [Video](https://www.youtube.com/watch?v=jNDsbbBxeUM) 8 | * [Slides](https://docs.google.com/presentation/d/19LTnoBp6qTgvtfVmFtpR2RHixRSQRLKFsUuSR9-5dBA/edit?usp=sharing) 9 | 10 | ## Additional materials 11 | 12 | * [Python unittest module](https://docs.python.org/3/library/unittest.html) 13 | -------------------------------------------------------------------------------- /C03-Unit-Testing/04-C03V03/interval.py: -------------------------------------------------------------------------------- 1 | class Interval: 2 | def __init__(self, start, end): 3 | # This is the constructor 4 | self.start = start 5 | self.end = end 6 | 7 | def is_inside(self, value): 8 | return self.start <= value <= self.end 9 | 10 | def stringify(self): 11 | start_symbol = "[" 12 | end_symbol = "]" 13 | 14 | return f"{start_symbol}{self.start}, {self.end}{end_symbol}" 15 | 16 | def __str__(self): 17 | return self.stringify() 18 | 19 | def __repr__(self): 20 | return self.stringify() 21 | -------------------------------------------------------------------------------- /C03-Unit-Testing/04-C03V03/interval_tests.py: -------------------------------------------------------------------------------- 1 | # interval_tests.py 2 | from unittest import TestCase 3 | 4 | from interval import Interval 5 | 6 | 7 | class IntervalTests(TestCase): 8 | def test_stringify_produces_correct_result(self): 9 | interval = Interval(1, 10) 10 | expected = "[1, 10]" 11 | 12 | self.assertEqual(expected, interval.stringify()) 13 | 14 | def test_is_inside_produces_correct_results(self): 15 | interval = Interval(1, 10) 16 | 17 | self.assertTrue(interval.is_inside(1)) 18 | self.assertTrue(interval.is_inside(5)) 19 | self.assertTrue(interval.is_inside(10)) 20 | -------------------------------------------------------------------------------- /C03-Unit-Testing/05-C03P02/README.md: -------------------------------------------------------------------------------- 1 | # C03P02 - Interval extended, tests 2 | 3 | Take your solution to `C03P01` and cover with with tests: 4 | 5 | 1. That exists in a separate file (for example, `interval_tests.py`). 6 | 1. That are using the `unittest` module from Python. 7 | 8 | The solution should come in 2 forms: 9 | 10 | 1. The source of the tests. 11 | 1. A screenshot where you've ran all the tests & they are passing. 12 | -------------------------------------------------------------------------------- /C03-Unit-Testing/05-C03P02/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C03P02S01 - Interval extended, tests Solution 01 2 | 3 | 1. Check [solution](./interval_tests.py) 4 | 1. Check the [video](https://www.youtube.com/watch?v=ZhZcQmGrYB4) 5 | -------------------------------------------------------------------------------- /C03-Unit-Testing/05-C03P02/Solution01/interval.py: -------------------------------------------------------------------------------- 1 | # https://github.com/HackBulgaria/Python-101-Forever/tree/master/C03-Unit-Testing/02-C03P01 2 | 3 | class Interval: 4 | def __init__(self, start, end, start_opened=False, end_opened=False): 5 | # This is the constructor 6 | self.start = start 7 | self.end = end 8 | self.start_opened = start_opened 9 | self.end_opened = end_opened 10 | 11 | def is_inside(self, value): 12 | after_start = False 13 | before_end = False 14 | 15 | if self.start_opened: 16 | after_start = value > self.start 17 | else: 18 | after_start = value >= self.start 19 | 20 | if self.end_opened: 21 | before_end = value < self.end 22 | else: 23 | before_end = value <= self.end 24 | 25 | return after_start and before_end 26 | 27 | def stringify(self): 28 | # [x, y] 29 | # (x, y) 30 | # (x, y] 31 | # [x, y) 32 | 33 | open_bracket = "[" 34 | close_bracket = "]" 35 | 36 | if self.start_opened: 37 | open_bracket = "(" 38 | 39 | if self.end_opened: 40 | close_bracket = ")" 41 | 42 | return f"{open_bracket}{self.start}, {self.end}{close_bracket}" 43 | 44 | 45 | closed_interval = Interval(1, 10) 46 | 47 | print(closed_interval.is_inside(1) is True) 48 | print(closed_interval.is_inside(5) is True) 49 | print(closed_interval.is_inside(10) is True) 50 | 51 | print(closed_interval.stringify() == "[1, 10]") 52 | 53 | opened_interval = Interval(1, 10, start_opened=True, end_opened=True) 54 | 55 | print(opened_interval.is_inside(1) is False) 56 | print(opened_interval.is_inside(5) is True) 57 | print(opened_interval.is_inside(10) is False) 58 | 59 | print(opened_interval.stringify() == "(1, 10)") 60 | 61 | half_opened_interval = Interval(1, 10, start_opened=False, end_opened=True) 62 | 63 | print(half_opened_interval.is_inside(1) is True) 64 | print(half_opened_interval.is_inside(5) is True) 65 | print(half_opened_interval.is_inside(10) is False) 66 | 67 | print(half_opened_interval.stringify() == "[1, 10)") 68 | 69 | half_opened_interval = Interval(1, 10, start_opened=True, end_opened=False) 70 | 71 | print(half_opened_interval.is_inside(1) is False) 72 | print(half_opened_interval.is_inside(5) is True) 73 | print(half_opened_interval.is_inside(10) is True) 74 | 75 | print(half_opened_interval.stringify() == "(1, 10]") 76 | -------------------------------------------------------------------------------- /C03-Unit-Testing/05-C03P02/Solution01/interval_tests.py: -------------------------------------------------------------------------------- 1 | # https://github.com/HackBulgaria/Python-101-Forever/tree/master/C03-Unit-Testing/05-C03P02 2 | 3 | from unittest import TestCase 4 | 5 | from interval import Interval 6 | 7 | 8 | class IntervalTests(TestCase): 9 | def test_closed_interval_is_inside(self): 10 | closed_interval = Interval(1, 10) 11 | 12 | self.assertTrue(closed_interval.is_inside(1)) 13 | self.assertTrue(closed_interval.is_inside(5)) 14 | self.assertTrue(closed_interval.is_inside(10)) 15 | 16 | def test_closed_interval_stringify(self): 17 | closed_interval = Interval(1, 10) 18 | self.assertEqual("[1, 10]", closed_interval.stringify()) 19 | 20 | def test_opened_interval_is_inside(self): 21 | opened_interval = Interval(1, 10, start_opened=True, end_opened=True) 22 | 23 | self.assertFalse(opened_interval.is_inside(1)) 24 | self.assertTrue(opened_interval.is_inside(5)) 25 | self.assertFalse(opened_interval.is_inside(10)) 26 | 27 | def test_opened_interval_stringify(self): 28 | opened_interval = Interval(1, 10, start_opened=True, end_opened=True) 29 | self.assertEqual("(1, 10)", opened_interval.stringify()) 30 | 31 | def test_half_opened_interval_is_inside(self): 32 | with self.subTest("End opened"): 33 | half_opened_interval = Interval(1, 10, start_opened=False, end_opened=True) 34 | 35 | self.assertTrue(half_opened_interval.is_inside(1)) 36 | self.assertTrue(half_opened_interval.is_inside(5)) 37 | self.assertFalse(half_opened_interval.is_inside(10)) 38 | 39 | with self.subTest("Start opened"): 40 | half_opened_interval = Interval(1, 10, start_opened=True, end_opened=False) 41 | 42 | self.assertFalse(half_opened_interval.is_inside(1)) 43 | self.assertTrue(half_opened_interval.is_inside(5)) 44 | self.assertTrue(half_opened_interval.is_inside(10)) 45 | 46 | def test_half_opened_interval_strigify(self): 47 | with self.subTest("End opened"): 48 | half_opened_interval = Interval(1, 10, start_opened=False, end_opened=True) 49 | self.assertEqual("[1, 10)", half_opened_interval.stringify()) 50 | 51 | with self.subTest("Start opened"): 52 | half_opened_interval = Interval(1, 10, start_opened=True, end_opened=False) 53 | self.assertEqual("(1, 10]", half_opened_interval.stringify()) 54 | -------------------------------------------------------------------------------- /C03-Unit-Testing/06-C03V04/README.md: -------------------------------------------------------------------------------- 1 | # C03V04 - Context managers 2 | 3 | In this video we introduce the concept of a context manager in Python. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/OF9KaDeIX_U) 8 | * [Slides](https://docs.google.com/presentation/d/1uolVU1joDc631u6sXogoTSa7143qFYJwIQb60wcBiVs/edit?usp=sharing) 9 | * [`resources.py`](resources.py) 10 | * [`context_manager.py`](context_manager.py) 11 | * [`context_manager_exception.py`](context_manager_exception.py) 12 | 13 | ## Additional materials 14 | 15 | * 16 | * 17 | -------------------------------------------------------------------------------- /C03-Unit-Testing/06-C03V04/context_manager.py: -------------------------------------------------------------------------------- 1 | # context_manager.py 2 | class EchoManager: 3 | def __enter__(self): 4 | print("enter") 5 | 6 | context_manager_value = 42 7 | 8 | return context_manager_value 9 | 10 | def __exit__(self, exception_type, exception_value, exception_traceback): 11 | print("exit") 12 | 13 | # If no exception, None, None, None 14 | print(exception_type, exception_value, exception_traceback) 15 | 16 | 17 | with EchoManager() as value: 18 | print(value) # 42 19 | -------------------------------------------------------------------------------- /C03-Unit-Testing/06-C03V04/context_manager_exception.py: -------------------------------------------------------------------------------- 1 | # context_manager_exception.py 2 | 3 | class EchoManager: 4 | def __enter__(self): 5 | pass 6 | 7 | def __exit__(self, exception_type, exception_value, exception_traceback): 8 | # ValueError , str(exception) , traceback_object 9 | # OOOPS 10 | print(exception_type, exception_value, exception_traceback) 11 | 12 | return True 13 | 14 | 15 | with EchoManager(): 16 | raise ValueError("OOOPS") 17 | -------------------------------------------------------------------------------- /C03-Unit-Testing/06-C03V04/resources.py: -------------------------------------------------------------------------------- 1 | # resources.py 2 | 3 | fd = open("README.md", "r") 4 | 5 | # Nothing stops us from getting an exception here 6 | contents = fd.read() 7 | print(len(contents)) 8 | 9 | # Nothing forces us to close the file, 10 | # So we can effectively forget to do this 11 | # And leak the file descriptor 12 | fd.close() 13 | 14 | # "with" statement, also known as context manager 15 | with open("README.md", "r") as fd: 16 | print(len(contents)) 17 | -------------------------------------------------------------------------------- /C03-Unit-Testing/07-C03P03/README.md: -------------------------------------------------------------------------------- 1 | # C03P03 - Exception silencing 2 | 3 | Implement a context manager called `ExceptionSilencer`, which can be used as follows: 4 | 5 | ```python 6 | with ExceptionSilencer(ValueError): 7 | int("aa") 8 | 9 | print("We can reach this point, because the exception was not propagated") 10 | ``` 11 | 12 | We want our exception silencer to only silence the specific exception class that we are providing. 13 | 14 | For example: 15 | 16 | ```python 17 | with ExceptionSilencer(Exception): 18 | int("aa") 19 | 20 | print("We cannot reach this point, because the exception was propagated") 21 | ``` 22 | 23 | Alongside the solution, add tests to show that the solution is behaving as expected. 24 | 25 | ## Hints 26 | 27 | 1. Have 2 Python files - `exception_silencer.py` and `exception_silencer_tests.py` 28 | 2. Consider using the `is` operator. 29 | -------------------------------------------------------------------------------- /C03-Unit-Testing/07-C03P03/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C03P03S01 - Exception silencing Solution 01 2 | 3 | 1. Check the [video](https://youtu.be/ph31b-n9pQM) 4 | -------------------------------------------------------------------------------- /C03-Unit-Testing/08-C03V05/README.md: -------------------------------------------------------------------------------- 1 | # C03V05 - Exceptions, part 1 2 | 3 | In this video we introduce the basic concepts around exceptions & program control flow. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/1iWrBhaUIns) 8 | * [Slides](https://docs.google.com/presentation/d/10wQkKSlOILSs_h5MvTKTm9u-vvlpQnyIS_Csz96KE5w/edit?usp=sharing) 9 | * [`flow.py`](flow.py) 10 | * [`call_stack.py`](call_stack.py) 11 | * [`exceptions.py`](exceptions.py) 12 | 13 | ## Additional materials 14 | 15 | * 16 | -------------------------------------------------------------------------------- /C03-Unit-Testing/08-C03V05/call_stack.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | 4 | def a(): 5 | b() 6 | 7 | 8 | def b(): 9 | c() 10 | 11 | 12 | def c(): 13 | print_callstack() 14 | 15 | 16 | def print_callstack(): 17 | for line in traceback.format_stack(): 18 | print(line.strip()) 19 | 20 | 21 | def main(): 22 | a() 23 | print("----------------") 24 | print_callstack() 25 | 26 | 27 | main() 28 | -------------------------------------------------------------------------------- /C03-Unit-Testing/08-C03V05/exceptions.py: -------------------------------------------------------------------------------- 1 | def a(): 2 | b() 3 | 4 | 5 | def b(): 6 | fail() 7 | 8 | 9 | def fail(): 10 | raise Exception("Ooops") 11 | c() 12 | 13 | 14 | def c(): 15 | print("We will never reach here") 16 | 17 | 18 | def main(): 19 | a() 20 | print("We will never reach here") 21 | 22 | 23 | main() 24 | -------------------------------------------------------------------------------- /C03-Unit-Testing/08-C03V05/flow.py: -------------------------------------------------------------------------------- 1 | def get_some_data(): 2 | return {} 3 | 4 | 5 | def parse_some_data(data): 6 | return data 7 | 8 | 9 | def use_some_data(parsed_data): 10 | pass 11 | 12 | 13 | def main(): 14 | data = get_some_data() 15 | parsed_data = parse_some_data(data) 16 | 17 | use_some_data(parsed_data) 18 | 19 | 20 | main() 21 | -------------------------------------------------------------------------------- /C03-Unit-Testing/08-C03V05/try_catch.py: -------------------------------------------------------------------------------- 1 | def fail(): 2 | raise Exception("Ooops") 3 | 4 | 5 | def main(): 6 | fail() 7 | print("We will never reach here") 8 | 9 | 10 | try: 11 | main() 12 | except Exception as exc: 13 | print("Some kind of exception happened") 14 | print(exc) 15 | -------------------------------------------------------------------------------- /C03-Unit-Testing/09-C03V06/README.md: -------------------------------------------------------------------------------- 1 | # C03V06 - Exceptions, part 2 2 | 3 | In this video, we expand on exception handling. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/ClFzITA9_OM) 8 | * [Slides](https://docs.google.com/presentation/d/1cN5hZ-fPAQu1_wnVBmOT0cd-Qo27FBLi2VeKXPLuO6c/edit?usp=sharing) 9 | * [`hierarchy.py`](hierarchy.py) 10 | * [`multiple.py`](multiple.py) 11 | 12 | ## Additional materials 13 | 14 | * 15 | * 16 | -------------------------------------------------------------------------------- /C03-Unit-Testing/09-C03V06/hierarchy.py: -------------------------------------------------------------------------------- 1 | # hierarchy.py 2 | 3 | def fail(exception_cls): 4 | raise exception_cls("Ooops") 5 | 6 | 7 | try: 8 | fail(ValueError) 9 | except Exception as exc: 10 | print("Can catch ValueError as Exception.") 11 | print(exc.__class__) 12 | 13 | 14 | try: 15 | fail(Exception) 16 | except ValueError as exc: 17 | print("Cannot catch Exception as ValueError.") 18 | print(exc.__class__) 19 | -------------------------------------------------------------------------------- /C03-Unit-Testing/09-C03V06/multiple.py: -------------------------------------------------------------------------------- 1 | # multiple.py 2 | 3 | def fail(exception_cls): 4 | raise exception_cls("Ooops") 5 | 6 | 7 | try: 8 | fail(ValueError) 9 | except Exception as exc: 10 | print("Can catch ValueError as Exception.") 11 | print(exc.__class__) 12 | except ValueError as exc: 13 | print("Can catch ValueError as ValueError.") 14 | print(exc.__class__) 15 | 16 | 17 | try: 18 | fail(ValueError) 19 | except ValueError as exc: 20 | print("Can catch ValueError as ValueError.") 21 | print(exc.__class__) 22 | except Exception as exc: 23 | print("Can catch ValueError as Exception.") 24 | print(exc.__class__) 25 | 26 | 27 | try: 28 | fail(ValueError) 29 | except (ValueError, Exception) as exc: 30 | print("Can catch multiple exceptions in a single except block") 31 | print(exc.__class__) 32 | -------------------------------------------------------------------------------- /C03-Unit-Testing/10-C03V07/README.md: -------------------------------------------------------------------------------- 1 | # C03V06 - Exceptions, part 3 2 | 3 | In this video, we expand even further on exception handling, introducing the `finally` clause. 4 | 5 | ## Materials 6 | 7 | * [Video](https://youtu.be/TF_V4UjbmG8) 8 | * [Slides](https://docs.google.com/presentation/d/10rOvuUZMwNGhpgJkSofSWnS1LzBaE6bpS-l5c4xaPtE/edit?usp=sharing) 9 | * [`finally.py`](finally.py) 10 | * [`reraising.py`](reraising.py) 11 | 12 | 13 | ## Additional materials 14 | 15 | * 16 | -------------------------------------------------------------------------------- /C03-Unit-Testing/10-C03V07/finally.py: -------------------------------------------------------------------------------- 1 | # finally.py 2 | 3 | def fail(exception_cls): 4 | raise exception_cls("Ooops") 5 | 6 | 7 | try: 8 | fail(ValueError) 9 | except ValueError as exc: 10 | print(f"Got value error: {exc}") 11 | finally: 12 | print("All is good") 13 | 14 | 15 | try: 16 | print("No exception here") 17 | except ValueError as exc: 18 | print(f"Got value error: {exc}") 19 | finally: 20 | print("All is good") 21 | 22 | 23 | try: 24 | fail(ValueError) 25 | finally: 26 | print("All is good") 27 | -------------------------------------------------------------------------------- /C03-Unit-Testing/10-C03V07/reraising.py: -------------------------------------------------------------------------------- 1 | # reraising.py 2 | 3 | def fail(exception_cls): 4 | raise exception_cls("Ooops") 5 | 6 | 7 | try: 8 | fail(ValueError) 9 | except Exception as exc: 10 | print(f"Got exception: {exc}") 11 | raise 12 | -------------------------------------------------------------------------------- /C03-Unit-Testing/11-C03P04/README.md: -------------------------------------------------------------------------------- 1 | # C03P04 - Fractions 2 | 3 | You are given the following empty class: 4 | 5 | ```python 6 | class Fraction: 7 | def __init__(self, numerator, denominator): 8 | """ 9 | Construct a new Fraction. 10 | 11 | If denominator = 0, raise ValueError. 12 | """ 13 | pass 14 | 15 | def __str__(self): 16 | """ 17 | Returns the string representation of self. 18 | """ 19 | 20 | def __repr__(self): 21 | """ 22 | Returns the REPL representation of self. 23 | """ 24 | 25 | def __eq__(self, other): 26 | """ 27 | Returns True/False, if self is equal to other. 28 | """ 29 | 30 | def __add__(self, other): 31 | """ 32 | Returns new Fraction, that's the sum of self and other. 33 | """ 34 | 35 | def __sub__(self, other): 36 | """ 37 | Returns new Fraction, that's the substraction of self and other. 38 | """ 39 | 40 | def __mul__(self, other): 41 | """ 42 | Returns new Fraction, that's the product of self and other. 43 | """ 44 | 45 | def simplify(self): 46 | """ 47 | Returns new Fraction, that's the simplification of self 48 | """ 49 | 50 | def is_simplified(self): 51 | """ 52 | Returns True/False, if self cannot be simplified further 53 | """ 54 | ``` 55 | 56 | This class defines a basic interfaces for a fraction. 57 | 58 | **Your task is to implement all methods & add unit tests accordingly.** 59 | 60 | 1. The implementation of `Fraction` should live in `fraction.py`. 61 | 1. The tests should live in `fraction_tests.py`. 62 | 63 | ## Examples of behavior 64 | 65 | ```python 66 | a = Fraction(1, 2) 67 | b = Fraction(1, 2) 68 | 69 | print(a == b) # True 70 | 71 | print(a) # 1/2 72 | print(b) # 1/2 73 | 74 | c = a + b 75 | 76 | print(a) # 1/2 77 | print(b) # 1/2 78 | print(c) # 2/2 79 | 80 | print(c.simplify()) # 1/1 81 | print(c) # 2/2 82 | 83 | print(c.is_simplified()) # False 84 | print(c.simplify().is_simplified()) # True 85 | 86 | d = a - b 87 | print(d) # 0 88 | print(d.simplify()) # 0 89 | print(d.is_simplified()) # True 90 | 91 | e = a * b 92 | print(e) # 1/4 93 | print(e.simplify()) # 1/4 94 | print(e.is_simplified()) # True 95 | ``` 96 | 97 | ## Extra credit 98 | 99 | Add additional methods to `Fraction`, so a list of fractions can be sorted: 100 | 101 | ```python 102 | sorted([Fraction(3, 4), Fraction(1, 2)]) == [Fraction(1, 2), Fraction(3, 4)] 103 | ``` 104 | -------------------------------------------------------------------------------- /C03-Unit-Testing/11-C03P04/Solution01/README.md: -------------------------------------------------------------------------------- 1 | # C03P04S01 - Fractions Solution 01 2 | 3 | 1. Check the videos: 4 | - Part 1 Sub tests [video](https://youtu.be/Qt4HCZ76fiI) 5 | - Part 2 Initialization [video](https://youtu.be/7I0tFGyQWKk) 6 | - Part 3 String representation [video](https://youtu.be/aRCsJTb5Oe4) 7 | - Part 4 Equality [video](https://youtu.be/w_1k4xRyLp8) 8 | - Part 5 Add [video](https://youtu.be/gSEz3UOUJq0) 9 | - Part 6 Subtraction [video](https://youtu.be/irDMTY3rLWs) 10 | - Part 7 Multiply [video](https://youtu.be/lNZiKpYGBrc) 11 | - Part 8 Simplification [video](https://youtu.be/M4NGUkFjwNI) 12 | - Part 9 Ordering [video](https://youtu.be/xyqewD-KhB8) 13 | - Part 10 Summary [video](https://youtu.be/sMd18tXjuW8) 14 | -------------------------------------------------------------------------------- /C03-Unit-Testing/11-C03P04/fraction.py: -------------------------------------------------------------------------------- 1 | class Fraction: 2 | def __init__(self, numerator, denominator): 3 | """ 4 | Construct a new Fraction. 5 | 6 | If denominator = 0, raise ValueError. 7 | """ 8 | pass 9 | 10 | def __str__(self): 11 | """ 12 | Returns the string representation of self. 13 | """ 14 | 15 | def __repr__(self): 16 | """ 17 | Returns the REPL representation of self. 18 | """ 19 | 20 | def __eq__(self, other): 21 | """ 22 | Returns True/False, if self is equal to other. 23 | """ 24 | 25 | def __add__(self, other): 26 | """ 27 | Returns new Fraction, that's the sum of self and other. 28 | """ 29 | 30 | def __sub__(self, other): 31 | """ 32 | Returns new Fraction, that's the substraction of self and other. 33 | """ 34 | 35 | def __mul__(self, other): 36 | """ 37 | Returns new Fraction, that's the product of self and other. 38 | """ 39 | 40 | def simplify(self): 41 | """ 42 | Returns new Fraction, that's the simplification of self 43 | """ 44 | 45 | def is_simplified(self): 46 | """ 47 | Returns True/False, if self cannot be simplified further 48 | """ 49 | -------------------------------------------------------------------------------- /C03-Unit-Testing/12-C03V08/README.md: -------------------------------------------------------------------------------- 1 | # C03V08 - Exceptions, part 4 2 | -------------------------------------------------------------------------------- /C03-Unit-Testing/13-C03V09/README.md: -------------------------------------------------------------------------------- 1 | # C03V09 - Testing exceptions 2 | 3 | *Coming soon* 4 | -------------------------------------------------------------------------------- /C03-Unit-Testing/14-C03P05/README.md: -------------------------------------------------------------------------------- 1 | # C03P05 - Simple template engine 2 | 3 | We are working on a piece of software that needs a simple templating engine, so we can send simple templated emails. 4 | 5 | In a module called `template.py` implement the following class: 6 | 7 | ```python 8 | class TemplateEngine: 9 | def __init__(self, template): 10 | pass 11 | 12 | def render(self, **context): 13 | """ 14 | Renders `self.template` by interpolating the variables with data from `context`. 15 | 16 | Raise `TemplatEngineError` if not all variables, present in `self.template`, have values in `context`. 17 | """ 18 | 19 | def extract_variables(self): 20 | """ 21 | Returns a list of all variables names, that are present in `self.template` 22 | """ 23 | ``` 24 | 25 | The class should behave as follows: 26 | 27 | ```python 28 | template = "Hello there, {{ x }}" 29 | engine = TemplateEngine(template) 30 | 31 | rendered = engine.render(x="general Kenobi.") 32 | print(rendered) # "Hello there, general Kenobi." 33 | 34 | variables = engine.extract_variables() 35 | print(variables) # ["x"] 36 | ``` 37 | 38 | **Few important things:** 39 | 40 | 1. The engine should support multiline strings. 41 | 1. All of those are valid: `{{x}}`, `{{ x}}`, `{{ x }}`, `{{ x}}`, etc. You can have arbitrary whitespace between `{{`, the variable name and `}}`. 42 | 1. One variable can occur in multiple places in the template. 43 | 1. Behavior of the `render` method is described in the docstring. 44 | 1. In order to show that your implementation is working as expected, **cover the `TemplateEngine` implementation with tests**, placed in `template_tests.py` 45 | 46 | **Few more examples** 47 | 48 | ```python 49 | template = """ 50 | Hello {{ first_name }} {{ last_name }}, 51 | 52 | I hope this email finds you well. 53 | 54 | We are currently running a promotion for {{ product }}. 55 | 56 | You can get your discount {{ here }} 57 | """ 58 | 59 | engine = TemplateEngine(template) 60 | 61 | variables = engine.extract_variables() 62 | print(variables) # ["first_name", "last_name", "product", "here"] 63 | 64 | rendered = engine.render( 65 | first_name="Ivan", 66 | last_name="Ivanov", 67 | product="Python course", 68 | here="https://hackbulgaria.com/python-101-forever" 69 | ) 70 | print(rendered) 71 | ``` 72 | 73 | The rendered version should look like this: 74 | 75 | ``` 76 | Hello Ivan Ivanov, 77 | 78 | I hope this email finds you well. 79 | 80 | We are currently running a promotion for Python course. 81 | 82 | You can get your discount https://hackbulgaria.com/python-101-forever 83 | ``` 84 | -------------------------------------------------------------------------------- /C03-Unit-Testing/14-C03P05/template.py: -------------------------------------------------------------------------------- 1 | class TemplateEngine: 2 | def __init__(self, template): 3 | pass 4 | 5 | def render(self, **context): 6 | """ 7 | Renders `self.template` by interpolating the variables with data from `context`. 8 | 9 | Raise `TemplatEngineError` if not all variables, present in `self.template`, have values in `context`. 10 | """ 11 | 12 | def extract_variables(self): 13 | """ 14 | Returns a list of all variables names, that are present in `self.template` 15 | """ 16 | -------------------------------------------------------------------------------- /C03-Unit-Testing/15-C03V10/README.md: -------------------------------------------------------------------------------- 1 | # C03V10 - Debugging in Python 2 | 3 | In this video, we introduce the concept of debugging & why it's a crucial skill that we need to learn. 4 | 5 | ## Materials 6 | 7 | * [Video](https://www.youtube.com/watch?v=G_clWez-W68) 8 | * [Slides](https://docs.google.com/presentation/d/1-ypm1HuovmZyxmSiyB46QOP6sHNFo3-5kl81f4XCf-s/edit?usp=sharing) 9 | 10 | ## Additional materials 11 | 12 | * [Radoslav Georgiev - Practical Debugging - Tips, Tricks and Ways to think](https://www.youtube.com/watch?v=9Ys4gCUtTh8) 13 | -------------------------------------------------------------------------------- /C03-Unit-Testing/16-C03V11/README.md: -------------------------------------------------------------------------------- 1 | # C03V11 - Debugging with print 2 | 3 | In this video, we explore the most basic & yet very powerful debugging technique - using `print`. 4 | 5 | ## Materials 6 | 7 | * [Video](https://www.youtube.com/watch?v=WxDQ98DtUag) 8 | -------------------------------------------------------------------------------- /C03-Unit-Testing/17-C03V12/README.md: -------------------------------------------------------------------------------- 1 | # C03V12 - Debugging with pdb 2 | 3 | In this video, we explore Python's built-in debugger. 4 | 5 | A quick cheathseet: 6 | 7 | - `l` / `ll` - show where we are 8 | - `w` - print the stack trace 9 | - `n` - next 10 | - `c` - continue until next breakpoint or program end 11 | 12 | ## Materials 13 | 14 | * [Video](https://www.youtube.com/watch?v=_oK00K3mSSg) 15 | 16 | ## Additional materials 17 | 18 | * Read the official documentation - 19 | -------------------------------------------------------------------------------- /C03-Unit-Testing/18-C03V13/README.md: -------------------------------------------------------------------------------- 1 | # C03V12 - Debugging with ipdb 2 | 3 | In this video, we build on top of `pdb` by showing `ipython` and `ipdb`. 4 | 5 | You need to install those via pip: 6 | 7 | ``` 8 | pip install ipython 9 | pip install ipdb 10 | ``` 11 | 12 | ## Materials 13 | 14 | * [Video](https://www.youtube.com/watch?v=yHSB_qzZhGs) 15 | 16 | ## Additional materials 17 | 18 | * `ipython` - 19 | * `ipdb` - 20 | -------------------------------------------------------------------------------- /C03-Unit-Testing/19-C03V14/README.md: -------------------------------------------------------------------------------- 1 | # C03V12 - Debugging with breakpoint 2 | 3 | In this video we show the built-in `breakpoint`, which can be configured via env variables. 4 | 5 | Quick configuration cheatsheet: 6 | 7 | ``` 8 | # Disables breakpoint 9 | export PYTHONBREAKPOINT=0 10 | 11 | # Sets ipdb as the default debugger 12 | export PYTHONBREAKPOINT=ipdb.set_trace 13 | 14 | # Return the default behavior 15 | export PYTHONBREAKPOINT= 16 | ``` 17 | 18 | ## Materials 19 | 20 | * [Video](https://www.youtube.com/watch?v=LsFUpRYLuu8) 21 | 22 | ## Additional materials 23 | 24 | * PEP 553 - 25 | * 26 | -------------------------------------------------------------------------------- /C03-Unit-Testing/20-C03P06/README.md: -------------------------------------------------------------------------------- 1 | # C03P06 - Conway's Game of Life 2 | 3 | It's time. We are going to implement a classic - [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) 4 | 5 | And this time, we won't have very strict requirements. Actually, we'll have only 3 requirements: 6 | 7 | 1. Have a Python module, called `game_of_life.py`, which we can run & observe a simulation. 8 | 1. Have a test suite, in a separate module, that prooves the core of the game is functional & works as intended. 9 | 1. Have a `.gif` or `.mp4` showing the game in action - with a simulation in place. 10 | 11 | You can spice things up, but try sticking to those 3 requirements, for the initial implementation. 12 | -------------------------------------------------------------------------------- /C03-Unit-Testing/21-C03V15/README.md: -------------------------------------------------------------------------------- 1 | # C03V15 - Python modules, part 1 2 | 3 | In this video, we explore the concept of a `module` in Python. 4 | 5 | 6 | ## Materials 7 | 8 | * [Slides](https://docs.google.com/presentation/d/1AAf2RoYqDKwTZZq9dqLEkN2PXuR8JckvYXVwt2eDqsg/edit?usp=sharing) 9 | * [Video](https://www.youtube.com/watch?v=DV7MQXKSeTo) 10 | 11 | ## Additional materials 12 | 13 | * 14 | -------------------------------------------------------------------------------- /C03-Unit-Testing/21-C03V15/utils.py: -------------------------------------------------------------------------------- 1 | BIG_CONSTANT = "YES" 2 | 3 | 4 | def group_by(xs, grouper): 5 | groups = {} 6 | 7 | for x in xs: 8 | group = grouper(x) 9 | 10 | if group not in groups: 11 | groups[group] = [] 12 | 13 | groups[group].append(x) 14 | 15 | return groups 16 | 17 | 18 | print(group_by([1, 2, 3, 4, 5, 6], lambda x: "even" if x % 2 == 0 else "odd")) 19 | -------------------------------------------------------------------------------- /C03-Unit-Testing/22-C03V16/README.md: -------------------------------------------------------------------------------- 1 | # C03V16 - Python modules, part 2 2 | 3 | In this video, we continue exploring the concept of a `module` in Python, focusing on how Python finds the modules to import. 4 | 5 | 6 | ## Materials 7 | 8 | * [Slides](https://docs.google.com/presentation/d/1RkIgN622u_rmBPdRbqEYxndaIp2Ka-pZC2K1Q-Dla0Y/edit?usp=sharing) 9 | * [Video](https://www.youtube.com/watch?v=s9C7SbOF8vo) 10 | 11 | ## Additional materials 12 | 13 | * 14 | -------------------------------------------------------------------------------- /C03-Unit-Testing/22-C03V16/a_folder/main.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | import sys 3 | 4 | pprint(sys.path) 5 | 6 | from utils import f 7 | f() 8 | -------------------------------------------------------------------------------- /C03-Unit-Testing/22-C03V16/utils.py: -------------------------------------------------------------------------------- 1 | def f(): 2 | print("Calling f") 3 | -------------------------------------------------------------------------------- /C03-Unit-Testing/23-C03V17/README.md: -------------------------------------------------------------------------------- 1 | # C03V17 - Python modules, part 3 2 | 3 | In this video, we continue exploring the concept of a `module` in Python, focusing on `builtins` and `__name__`. 4 | 5 | 6 | ## Materials 7 | 8 | * [Slides](https://docs.google.com/presentation/d/1tmIMusvVERNjE5mvKFmLNIzG8t42DNB09omCrN-uNhc/edit?usp=sharing) 9 | * [Video](https://www.youtube.com/watch?v=nIEkHsLapno) 10 | 11 | ## Additional materials 12 | 13 | * 14 | -------------------------------------------------------------------------------- /C03-Unit-Testing/23-C03V17/some_module.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a special docstring for our module. 3 | """ 4 | 5 | 6 | def main(): 7 | print(__name__) 8 | print(__doc__) 9 | 10 | 11 | if __name__ == "__main__": 12 | main() 13 | -------------------------------------------------------------------------------- /C03-Unit-Testing/24-C03V18/README.md: -------------------------------------------------------------------------------- 1 | # C03V18 - Python packages, part 1 2 | 3 | In this video, we explore the concept of a "package". 4 | 5 | ## Materials 6 | 7 | * [Slides](https://docs.google.com/presentation/d/1mow7pRqWJxISxGDJHj929LtsrnuoFanPvHv7kEKDQHI/edit?usp=sharing) 8 | * [Video](https://youtu.be/Uk9YQne8HJo) 9 | 10 | ## Additional materials 11 | 12 | * 13 | -------------------------------------------------------------------------------- /C03-Unit-Testing/25-C03V19/README.md: -------------------------------------------------------------------------------- 1 | # C03V19 - Python packages, part 2 2 | 3 | In this video, we expand on packages, by showing absolute & relative imports & talk about the most important directory. 4 | 5 | ## Materials 6 | 7 | * [Slides](https://docs.google.com/presentation/d/12m1ag-GkRBBY5F4KBTFrrHStpe_GAQjss7UJkX_0CSk/edit?usp=sharing) 8 | * [Video](https://youtu.be/FZstX_Ec5lA) 9 | 10 | ## Additional materials 11 | 12 | * 13 | -------------------------------------------------------------------------------- /C03-Unit-Testing/README.md: -------------------------------------------------------------------------------- 1 | # C03 - Unit testing 2 | 3 | Follow this order: 4 | 5 | 6 | | Order | Name | Link | 7 | |-------|---------------------------------------------------------|--------------------------| 8 | | 1 | C03V01 - Basic OOP concepts in Python | [Click here](01-C03V01/) | 9 | | 2 | C03P01 - Interval extended | [Click here](02-C03P01/) | 10 | | 3 | C03V02 - OOP principles | [Click here](03-C03V02/) | 11 | | 4 | C03V03 - Unit tests | [Click here](04-C03V03/) | 12 | | 5 | C03P02 - Interval extended, tests | [Click here](05-C03P02/) | 13 | | 6 | C03V04 - Context managers | [Click here](06-C03V04/) | 14 | | 7 | C03P03 - Exception silencing | [Click here](07-C03P03/) | 15 | | 8 | C03V05 - Exceptions, part 1 | [Click here](08-C03V05/) | 16 | | 9 | C03V06 - Exceptions, part 2 | [Click here](09-C03V06/) | 17 | | 10 | C03V07 - Exceptions, part 3 | [Click here](10-C03V07/) | 18 | | 11 | C03P04 - Fractions | [Click here](11-C03P04/) | 19 | | 12 | C03V08 - Exceptions, part 4 | *Coming soon* | 20 | | 13 | C03V09 - Testing exceptions | *Coming soon* | 21 | | 14 | C03P05 - Simple template engine | [Click here](14-C03P05/) | 22 | | 15 | C03V10 - Debugging in Python | [Click here](15-C03V10/) | 23 | | 16 | C03V11 - Debugging with print | [Click here](16-C03V11/) | 24 | | 17 | C03V12 - Debugging with pdb | [Click here](17-C03V12/) | 25 | | 18 | C03V13 - Debugging with ipdb | [Click here](18-C03V13/) | 26 | | 19 | C03V14 - Debugging with breakpoint | [Click here](19-C03V14/) | 27 | | 20 | C03P06 - Conway's Game of Life | [Click here](20-C03P06/) | 28 | | 21 | C03V15 - Python modules, part 1 | [Click here](21-C03V15/) | 29 | | 22 | C03V16 - Python modules, part 2 | [Click here](22-C03V16/) | 30 | | 23 | C03V17 - Python modules, part 3 | [Click here](23-C03V17/) | 31 | | 24 | C03V18 - Python packages, part 1 | [Click here](24-C03V18/) | 32 | | 25 | C03V19 - Python packages, part 2 | [Click here](25-C03V19/) | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hack Bulgaria 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Python 101 Forever 🚀 2 | 3 | Official [Python 101 Forever](https://hackbulgaria.com/python-101-forever) GitHub repository. 4 | 5 | [START HERE - CHECK README](C00-Course-Introduction/) 6 | 7 | [SUBSCRIBE FOR UPDATES HERE](https://landing.mailerlite.com/webforms/landing/p2u7b6) 8 | 9 | ## Sponsors 10 | 11 | | [![HackSoft](https://www.hacksoft.io/hacksoft.svg)](https://hacksoft.io) | 12 | |------------------------------------------------------------------------------------------------------------------------| 13 | 14 | Contact us at for sponsorship opportunities. 15 | 16 | ## Concepts 17 | 18 | | Concept | Link | 19 | |---------------------------------------|------------------------------------------| 20 | | 00 - Course Introduction | [Click here](C00-Course-Introduction/) | 21 | | 01 - Python Basics | [Click here](C01-Python-Basics/) | 22 | | 02 - Linux & Python Setup | [Click here](C02-Linux-and-Python-Setup/)| 23 | | 03 - Unit testing | [Click here](C03-Unit-Testing/) | 24 | | 04 - Algorithms and Data structures | *Coming soon* | 25 | | 05 - Modules, namespaces & interfaces | *Coming soon* | 26 | | 06 - Expanding in Python, part 1 | *Coming soon* | 27 | | 07 - Keeping it practical, part 1 | *Coming soon* | 28 | | 08 - Introduction to Databases / SQL | *Coming soon* | 29 | | 09 - Python & SQL | *Coming soon* | 30 | | 10 - Expanding in Python, part 2 | *Coming soon* | 31 | | 11 - Keeping it Practical, part 2 | *Coming soon* | 32 | | 12 - Git | *Coming soon* | 33 | | *There's more to come* | *Coming soon* | 34 | 35 | 36 | ## Glossary 37 | 38 | If you wonder about those `C01V01`, `C01P01`, `C01P01S01` things, here is the explanation: 39 | 40 | 1. Those are **unique identifiers** for the different resources in the course. 41 | 1. `C01V01` means **Concept 01, Video 01**. This is a video, explaining something. Video is located in YouTube and linked here. 42 | 1. `C01P01` means **Concept 01, Problem 01**. This is a problem that needs solving. Problem is located here in markdown format. 43 | 1. `C01P01S01` means **Concept 01, Problem 01, Solution 01**. This is a video, showing a solution to the specific problem. Video is located in YouTube and linked here. Solution code is located here. 44 | --------------------------------------------------------------------------------