diff --git a/L01/T01/main.py b/L01/T01/main.py
new file mode 100644
index 0000000..0a9300d
--- /dev/null
+++ b/L01/T01/main.py
@@ -0,0 +1,3 @@
+if __name__ == "__main__":
+ # Write your solution here
+ pass
diff --git a/L01/T01/task-info.yaml b/L01/T01/task-info.yaml
new file mode 100644
index 0000000..2c2b10d
--- /dev/null
+++ b/L01/T01/task-info.yaml
@@ -0,0 +1,7 @@
+type: theory
+custom_name: About
+files:
+ - name: task.md
+ visible: false
+ - name: main.py
+ visible: true
diff --git a/L01/T01/task.md b/L01/T01/task.md
new file mode 100644
index 0000000..df064b6
--- /dev/null
+++ b/L01/T01/task.md
@@ -0,0 +1,14 @@
+# Welcome
+
+Welcome to the **Complete Python Developer Zero to Mastery** course.
+
+In this course, you will learn Python from the ground up, starting with the fundamentals and gradually moving on to more practical exercises and real working programs. Each section builds on the previous one, helping you develop both understanding and confidence as you progress.
+
+In the next two tasks, you will take your first steps in the development environment. We will explore the IDE interface in more detail and run your first Python program.
+
+#### Prerequisites
+
+You don't need to have any previous experience in Python or programming in general
+to complete this course.
+
+**Click the "Next" button to navigate to the next task and good luck!**
diff --git a/L01/T02/images/back.svg b/L01/T02/images/back.svg
new file mode 100644
index 0000000..c5b84d1
--- /dev/null
+++ b/L01/T02/images/back.svg
@@ -0,0 +1,9 @@
+
diff --git a/L01/T02/images/edu_course_overview.png b/L01/T02/images/edu_course_overview.png
new file mode 100644
index 0000000..d156c31
Binary files /dev/null and b/L01/T02/images/edu_course_overview.png differ
diff --git a/L01/T02/images/edu_course_overview_dark.png b/L01/T02/images/edu_course_overview_dark.png
new file mode 100644
index 0000000..a2ced2b
Binary files /dev/null and b/L01/T02/images/edu_course_overview_dark.png differ
diff --git a/L01/T02/images/forward.svg b/L01/T02/images/forward.svg
new file mode 100644
index 0000000..0ce7c70
--- /dev/null
+++ b/L01/T02/images/forward.svg
@@ -0,0 +1,9 @@
+
diff --git a/L01/T02/images/reset.svg b/L01/T02/images/reset.svg
new file mode 100644
index 0000000..33a3d1c
--- /dev/null
+++ b/L01/T02/images/reset.svg
@@ -0,0 +1,6 @@
+
diff --git a/L01/T02/main.py b/L01/T02/main.py
new file mode 100644
index 0000000..0a9300d
--- /dev/null
+++ b/L01/T02/main.py
@@ -0,0 +1,3 @@
+if __name__ == "__main__":
+ # Write your solution here
+ pass
diff --git a/L01/T02/task-info.yaml b/L01/T02/task-info.yaml
new file mode 100644
index 0000000..510ef56
--- /dev/null
+++ b/L01/T02/task-info.yaml
@@ -0,0 +1,22 @@
+type: theory
+custom_name: Navigating Around
+files:
+ - name: task.md
+ visible: false
+ - name: main.py
+ visible: true
+ - name: images/edu_course_overview.png
+ visible: false
+ is_binary: true
+ - name: images/edu_course_overview_dark.png
+ visible: false
+ is_binary: true
+ - name: images/forward.svg
+ visible: false
+ is_binary: true
+ - name: images/back.svg
+ visible: false
+ is_binary: true
+ - name: images/reset.svg
+ visible: false
+ is_binary: true
diff --git a/L01/T02/task.md b/L01/T02/task.md
new file mode 100644
index 0000000..c77847c
--- /dev/null
+++ b/L01/T02/task.md
@@ -0,0 +1,55 @@
+## JetBrains Academy plugin overview
+
+This lesson will help you take your first steps with the [JetBrains Academy plugin](https://www.jetbrains.com/help/education/educational-products.html) and use it to learn Python.
+
+With the JetBrains Academy plugin, you can learn programming languages and tools by completing coding tasks and get instant feedback right inside the IDE.
+
+If you're already familiar with the interface, you can skip this lesson.
+
+### Working with courses
+When you open a course, you will see the main tool windows used for navigation: Course View, Editor, and Task Description.
+
+
+
+
+In Course View, you can switch between lessons and tasks. In the Editor, you write code and complete the task. In Task Description, you can read the theory and the task instructions.
+
+### Task Description
+
+The **Task Description** window gives you all the information you need to complete a task:
+
+- For theoretical tasks, the description provides learning and reading materials.
+- For programming assignments, it states the problem to be solved.
+
+Use Task Description icons for the following actions:
+
+| Icon | Description |
+|------------------------------------|-------------------------------|
+|**Check** | Check the correctness of your answer (for a quiz) or your code solution (for a programming task)|
+| **Run** | Run your code (for a theoretical task)|
+| | Go to the previous task |
+| or **Next** | Go to the next task|
+| | Discard all the changes you’ve made in the task, and start over|
+|Peek Solution... | Reveal the correct answer and show the diff|
+
+
diff --git a/L01/T03/OurFirstPythonProgram.py b/L01/T03/OurFirstPythonProgram.py
new file mode 100644
index 0000000..eb47f65
--- /dev/null
+++ b/L01/T03/OurFirstPythonProgram.py
@@ -0,0 +1,3 @@
+name = input("what is your name?\n")
+print("Hello " + name)
+
diff --git a/L01/T03/images/hello_username.gif b/L01/T03/images/hello_username.gif
new file mode 100644
index 0000000..581ecfe
Binary files /dev/null and b/L01/T03/images/hello_username.gif differ
diff --git a/L01/T03/images/run.svg b/L01/T03/images/run.svg
new file mode 100644
index 0000000..dd11f50
--- /dev/null
+++ b/L01/T03/images/run.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/L01/T03/images/run_dark.svg b/L01/T03/images/run_dark.svg
new file mode 100644
index 0000000..0c199c7
--- /dev/null
+++ b/L01/T03/images/run_dark.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/L01/T03/task-info.yaml b/L01/T03/task-info.yaml
new file mode 100644
index 0000000..2fc8ba1
--- /dev/null
+++ b/L01/T03/task-info.yaml
@@ -0,0 +1,16 @@
+type: theory
+custom_name: Your First Python Program
+files:
+ - name: task.md
+ visible: false
+ - name: OurFirstPythonProgram.py
+ visible: true
+ - name: images/run.svg
+ visible: false
+ is_binary: true
+ - name: images/run_dark.svg
+ visible: false
+ is_binary: true
+ - name: images/hello_username.gif
+ visible: false
+ is_binary: true
diff --git a/L01/T03/task.md b/L01/T03/task.md
new file mode 100644
index 0000000..6c04721
--- /dev/null
+++ b/L01/T03/task.md
@@ -0,0 +1,25 @@
+### Understanding the Code
+
+```python
+name = input("what is your name?\n") # Gets input from you
+print("Hello " + name) # Prints the greeting
+```
+
+- `input()` - Asks you to type something
+- The Run tool window (at bottom) is where you type and see output
+- `print()` - Shows messages on screen
+
+**Try It!**
+
+1. Click the **Run** button () at the top right
+2. Type your name when asked
+3. Press Enter
+4. See your personalized greeting!
+
+
+
+
\ No newline at end of file
diff --git a/L01/T04/images/placeholder.png b/L01/T04/images/placeholder.png
new file mode 100644
index 0000000..f04ccc9
Binary files /dev/null and b/L01/T04/images/placeholder.png differ
diff --git a/L01/T04/images/placeholder_dark.png b/L01/T04/images/placeholder_dark.png
new file mode 100644
index 0000000..3af04c4
Binary files /dev/null and b/L01/T04/images/placeholder_dark.png differ
diff --git a/L01/T04/main.py b/L01/T04/main.py
new file mode 100644
index 0000000..0a9300d
--- /dev/null
+++ b/L01/T04/main.py
@@ -0,0 +1,3 @@
+if __name__ == "__main__":
+ # Write your solution here
+ pass
diff --git a/L01/T04/task-info.yaml b/L01/T04/task-info.yaml
new file mode 100644
index 0000000..e2e3923
--- /dev/null
+++ b/L01/T04/task-info.yaml
@@ -0,0 +1,13 @@
+type: theory
+custom_name: Before You Continue
+files:
+ - name: task.md
+ visible: false
+ - name: main.py
+ visible: true
+ - name: images/placeholder.png
+ visible: false
+ is_binary: true
+ - name: images/placeholder_dark.png
+ visible: false
+ is_binary: true
diff --git a/L01/T04/task.md b/L01/T04/task.md
new file mode 100644
index 0000000..2345396
--- /dev/null
+++ b/L01/T04/task.md
@@ -0,0 +1,15 @@
+### Entering Multiple Answers
+
+In future tasks, you may see several input fields for your answers. The highlighted box in the image below shows where you need to enter them.
+
+
+
+In such cases, the test checks all fields together. If you enter some correct answers but leave the others empty, the test will not pass. **Make sure to fill in all answer fields before running the test.**
+
+### Comments
+In Python, comments start with the hash character (`#`) followed by a single space and extend to the end of the line. They are shown in gray in the editor and **do not affect how the program runs.**
+
+```python
+attempt += 1 # Count the number of login attempts
+```
+You can read more about proper commenting in PEP 8 – Style Guide for Python Code.
diff --git a/L01/lesson-info.yaml b/L01/lesson-info.yaml
new file mode 100644
index 0000000..894448f
--- /dev/null
+++ b/L01/lesson-info.yaml
@@ -0,0 +1,6 @@
+custom_name: Introduction
+content:
+ - T01
+ - T02
+ - T03
+ - T04
diff --git a/L02/T01/ExerciseOperatorPrecedence.py b/L02/T01/ExerciseOperatorPrecedence.py
new file mode 100644
index 0000000..900e31c
--- /dev/null
+++ b/L02/T01/ExerciseOperatorPrecedence.py
@@ -0,0 +1,14 @@
+# Operator Precedence Exercise
+# Predict the output of each expression before you run the code
+# Replace each placeholder value with your prediction (as a number)
+
+# What will each of these print?
+answer_1 = 45.0
+
+answer_2 = 45.0
+
+answer_3 = 45.0
+
+answer_4 = 25.0
+
+answer_5 = 25
diff --git a/L02/T01/task-info.yaml b/L02/T01/task-info.yaml
new file mode 100644
index 0000000..d90bc0c
--- /dev/null
+++ b/L02/T01/task-info.yaml
@@ -0,0 +1,25 @@
+type: edu
+custom_name: Operators And Precedence
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseOperatorPrecedence.py
+ visible: true
+ placeholders:
+ - offset: 208
+ length: 4
+ placeholder_text: "# calculate (5 + 4) * 10 / 2"
+ - offset: 225
+ length: 4
+ placeholder_text: "# calculate ((5 + 4) * 10) / 2"
+ - offset: 242
+ length: 4
+ placeholder_text: "# calculate (5 + 4) * (10 / 2)"
+ - offset: 259
+ length: 4
+ placeholder_text: "# calculate 5 + (4 * 10) / 2"
+ - offset: 276
+ length: 2
+ placeholder_text: "# calculate 5 + 4 * 10 // 2"
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T01/task.md b/L02/T01/task.md
new file mode 100644
index 0000000..3457217
--- /dev/null
+++ b/L02/T01/task.md
@@ -0,0 +1,22 @@
+# Operator Precedence Exercise
+
+## Task
+Calculate the results for the five expressions below and fill in the answers in `ExerciseOperatorPrecedence.py`.
+Now calculate these 5 expressions and enter your answers.
+
+## Operator Precedence
+Python evaluates operators in this order (highest to lowest):
+1. `**` (exponentiation)
+2. `*`, `/`, `//`, `%` (multiplication, division, floor division, modulo)
+3. `+`, `-` (addition, subtraction)
+4. Parentheses `()` override the default order
+
+## Your Task
+Now calculate these 5 expressions and enter your answers.
+```python
+answer_1 = ... # calculate (5 + 4) * 10 / 2
+answer_2 = ... # calculate ((5 + 4) * 10) / 2
+answer_3 = ... # calculate (5 + 4) * (10 / 2)
+answer_4 = ... # calculate 5 + (4 * 10) / 2
+answer_5 = ... # calculate 5 + 4 * 10 // 2
+```
diff --git a/L02/T01/tests/test_task.py b/L02/T01/tests/test_task.py
new file mode 100644
index 0000000..72684b5
--- /dev/null
+++ b/L02/T01/tests/test_task.py
@@ -0,0 +1,94 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+TASK_NAME = 'operator_precedence'
+TASK_FILE = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseOperatorPrecedence.py'
+)
+
+
+def try_import():
+ return importlib.import_module(TASK_NAME)
+
+
+class TestCase(unittest.TestCase):
+ task_name = TASK_NAME
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ spec = importlib.util.spec_from_file_location(TASK_NAME, TASK_FILE)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[TASK_NAME] = module
+ except NameError:
+ pass
+ except SyntaxError as se:
+ self.fail("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers (empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ expected_value = 45.0
+
+ try:
+ actual_value = try_import().answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+ def test_answer_2(self):
+ expected_value = 45.0
+
+ try:
+ actual_value = try_import().answer_2
+ except AttributeError:
+ self.fail(msg="The variable answer_2 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_2 value seems to be a bit off.")
+
+ def test_answer_3(self):
+ expected_value = 45.0
+
+ try:
+ actual_value = try_import().answer_3
+ except AttributeError:
+ self.fail(msg="The variable answer_3 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_3 value seems to be a bit off.")
+
+ def test_answer_4(self):
+ expected_value = 25.0
+
+ try:
+ actual_value = try_import().answer_4
+ except AttributeError:
+ self.fail(msg="The variable answer_4 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_4 value seems to be a bit off.")
+
+ def test_answer_5(self):
+ expected_value = 25
+
+ try:
+ actual_value = try_import().answer_5
+ except AttributeError:
+ self.fail(msg="The variable answer_5 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_5 value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T02/ExerciseAugmentedAssignmentOperator.py b/L02/T02/ExerciseAugmentedAssignmentOperator.py
new file mode 100644
index 0000000..c0431a9
--- /dev/null
+++ b/L02/T02/ExerciseAugmentedAssignmentOperator.py
@@ -0,0 +1,14 @@
+# Augmented Assignment Operator Exercise
+# Trace through the code and predict what will be printed
+
+counter = 0
+
+counter += 1
+counter += 1
+counter += 1
+counter += 1
+counter -= 1
+counter *= 2
+
+# What will counter be after all these operations?
+prediction = 6
diff --git a/L02/T02/task-info.yaml b/L02/T02/task-info.yaml
new file mode 100644
index 0000000..9b97813
--- /dev/null
+++ b/L02/T02/task-info.yaml
@@ -0,0 +1,13 @@
+type: edu
+custom_name: Augmented Assignment
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseAugmentedAssignmentOperator.py
+ visible: true
+ placeholders:
+ - offset: 256
+ length: 1
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T02/task.md b/L02/T02/task.md
new file mode 100644
index 0000000..9ef5502
--- /dev/null
+++ b/L02/T02/task.md
@@ -0,0 +1,15 @@
+# Augmented Assignment Operator
+
+## Task
+
+Trace through the code in `ExerciseAugmentedAssignmentOperator.py` and predict the final value of `counter`.
+
+Replace `???` with your prediction.
+
+## Augmented Assignment Operators
+
+Shorthand for updating a variable:
+- `x += 5` is the same as `x = x + 5`
+- `x -= 3` is the same as `x = x - 3`
+- `x *= 2` is the same as `x = x * 2`
+- `x /= 4` is the same as `x = x / 4`
diff --git a/L02/T02/tests/test_task.py b/L02/T02/tests/test_task.py
new file mode 100644
index 0000000..5ab2aaa
--- /dev/null
+++ b/L02/T02/tests/test_task.py
@@ -0,0 +1,47 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+def try_import():
+ return importlib.import_module('augmented_assignment')
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'augmented_assignment'
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseAugmentedAssignmentOperator.py'
+ )
+ spec = importlib.util.spec_from_file_location(self.task_name, task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_prediction(self):
+ expected_value = 6
+
+ try:
+ actual_value = try_import().prediction
+ except AttributeError:
+ self.fail(msg="The variable prediction seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The prediction value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T03/ExerciseFormattedStrings.py b/L02/T03/ExerciseFormattedStrings.py
new file mode 100644
index 0000000..bdbe307
--- /dev/null
+++ b/L02/T03/ExerciseFormattedStrings.py
@@ -0,0 +1,18 @@
+# Formatted Strings Exercise
+# Predict what each will print, then write an f-string version
+
+# What will these output?
+# answer_1 is "Hello {}, your balance is {}.".format("Cindy", 50)
+answer_1 = "Hello Cindy, your balance is 50."
+
+# answer_2 is "Hello {0}, your balance is {1}.".format("Cindy", 50)
+answer_2 = "Hello Cindy, your balance is 50."
+
+# answer_3 is "Hello {name}, your balance is {amount}.".format(name="Cindy", amount=50)
+answer_3 = "Hello Cindy, your balance is 50."
+
+# Now rewrite the SAME sentence using an f-string
+# Output should be: "Hello Cindy, your balance is 50."
+name = 'Cindy'
+amount = 50
+f_string_version = f"Hello {name}, your balance is {amount}."
diff --git a/L02/T03/task-info.yaml b/L02/T03/task-info.yaml
new file mode 100644
index 0000000..c088238
--- /dev/null
+++ b/L02/T03/task-info.yaml
@@ -0,0 +1,22 @@
+type: edu
+custom_name: Formatted Strings
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseFormattedStrings.py
+ visible: true
+ placeholders:
+ - offset: 196
+ length: 34
+ placeholder_text: "# Write the resulting string here"
+ - offset: 311
+ length: 34
+ placeholder_text: "# Write the resulting string here"
+ - offset: 446
+ length: 34
+ placeholder_text: "# Write the resulting string here"
+ - offset: 635
+ length: 39
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T03/task.md b/L02/T03/task.md
new file mode 100644
index 0000000..9ccba2a
--- /dev/null
+++ b/L02/T03/task.md
@@ -0,0 +1,47 @@
+# Formatted Strings
+
+## Mutable vs Immutable
+
+A mutable object can be changed after it is created.
+Examples: `list`, `dict`, `set`.
+
+An immutable object cannot be changed after it is created.
+Examples: `str`, `int`, `float`, `tuple`.
+
+Strings are immutable, so any “change” to a string actually creates and returns a new string.
+
+## String formatting with format()
+
+`str.format()` inserts values into a string.
+It returns a new string (strings are immutable).
+
+```python
+"Hello {}, you have {} messages.".format("Alex", 3)
+```
+
+### Positional placeholders
+
+Use `{0}`, `{1}`, ... to refer to arguments by index:
+
+```python
+"Hello {0}, you have {1} messages.".format("Alex", 3)
+```
+
+### Named placeholders
+
+Use names inside braces and pass keyword arguments:
+
+```python
+"Hello {name}, you have {count} messages.".format(name="Alex", count=3)
+```
+
+## f-strings
+
+Prefix a string with `f` and put expressions inside `{}`.
+This is often the most readable option.
+
+```python
+name = "Alex"
+count = 3
+f"Hello {name}, you have {count} messages."
+```
diff --git a/L02/T03/tests/test_task.py b/L02/T03/tests/test_task.py
new file mode 100644
index 0000000..e6eb5bd
--- /dev/null
+++ b/L02/T03/tests/test_task.py
@@ -0,0 +1,104 @@
+import importlib.util
+from io import StringIO
+import os
+import re
+import sys
+import unittest
+from unittest.mock import patch
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'formatted_strings_exercise'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseFormattedStrings.py'
+ )
+
+ def setUp(self):
+ self.import_error = None
+ self.module = None
+ try:
+ with patch('sys.stdout', new=StringIO()):
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ self.module = module
+ except SyntaxError as se:
+ self.import_error = ("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings "
+ "(empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.import_error = ("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ expected_value = 'Hello Cindy, your balance is 50.'
+
+ try:
+ actual_value = self.module.answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+ def test_answer_2(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ expected_value = 'Hello Cindy, your balance is 50.'
+
+ try:
+ actual_value = self.module.answer_2
+ except AttributeError:
+ self.fail(msg="The variable answer_2 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_2 value seems to be a bit off.")
+
+ def test_answer_3(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ expected_value = 'Hello Cindy, your balance is 50.'
+
+ try:
+ actual_value = self.module.answer_3
+ except AttributeError:
+ self.fail(msg="The variable answer_3 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_3 value seems to be a bit off.")
+
+ def test_ordered_4_f_string_version(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ expected_value = f"Hello {self.module.name}, your balance is {self.module.amount}."
+
+ try:
+ actual_value = self.module.f_string_version
+ except AttributeError:
+ self.fail(msg="The variable f_string_version seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The f_string_version value seems to be a bit off.")
+
+ try:
+ with open(self.task_file, 'r', encoding='utf-8') as handle:
+ source = handle.read()
+ except OSError:
+ self.fail("Could not read the task file to verify the f-string syntax.")
+
+ pattern = r'^[ \t]*f_string_version[ \t]*=[ \t]*f"Hello {name}, your balance is {amount}\."[ \t]*$'
+ match = re.search(pattern, source, flags=re.MULTILINE)
+ self.assertIsNotNone(
+ match,
+ msg="The f_string_version must be defined exactly as: "
+ "f\"Hello {name}, your balance is {amount}.\""
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T04/ExerciseStringIndexes.py b/L02/T04/ExerciseStringIndexes.py
new file mode 100644
index 0000000..d68ec5e
--- /dev/null
+++ b/L02/T04/ExerciseStringIndexes.py
@@ -0,0 +1,15 @@
+# String Indexes Exercise
+# Predict the output of each print statement
+
+python = 'I am PYTHON'
+
+# What will each of these print?
+answer_1 = ' am' # python[1:4]
+answer_2 = ' am PYTHON' # python[1:]
+answer_3 = 'I am PYTHON' # python[:]
+answer_4 = ' am PYTHON' # python[1:100]
+answer_5 = 'N' # python[-1]
+answer_6 = 'T' # python[-4]
+answer_7 = 'I am PYT' # python[:-3]
+answer_8 = 'HON' # python[-3:]
+answer_9 = 'NOHTYP ma I' # python[::-1]
diff --git a/L02/T04/task-info.yaml b/L02/T04/task-info.yaml
new file mode 100644
index 0000000..60f3845
--- /dev/null
+++ b/L02/T04/task-info.yaml
@@ -0,0 +1,37 @@
+type: edu
+custom_name: String Indexes
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseStringIndexes.py
+ visible: true
+ placeholders:
+ - offset: 140
+ length: 5
+ placeholder_text: ???
+ - offset: 172
+ length: 12
+ placeholder_text: ???
+ - offset: 210
+ length: 13
+ placeholder_text: ???
+ - offset: 248
+ length: 12
+ placeholder_text: ???
+ - offset: 289
+ length: 3
+ placeholder_text: ???
+ - offset: 318
+ length: 3
+ placeholder_text: ???
+ - offset: 347
+ length: 10
+ placeholder_text: ???
+ - offset: 384
+ length: 5
+ placeholder_text: ???
+ - offset: 416
+ length: 13
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T04/task.md b/L02/T04/task.md
new file mode 100644
index 0000000..0b01bb5
--- /dev/null
+++ b/L02/T04/task.md
@@ -0,0 +1,43 @@
+# String Indexes
+
+## Indexing a string
+
+Strings are ordered sequences of characters.
+Indexing starts at 0.
+
+```python
+text = "Data Science"
+text[0] # 'D'
+text[-1] # 'e' (negative indexes count from the end)
+```
+
+## Slicing a string
+
+Slicing uses `[start:stop:step]` and returns a new string.
+`start` is inclusive, `stop` is exclusive.
+
+```python
+text[1:4] # 'ata'
+text[5:] # from index 5 to the end
+text[:] # full copy of the string
+text[2:9] # stop beyond length is allowed too
+```
+
+## Negative indexes in slices
+
+Negative values count from the end:
+
+```python
+text[:-4] # everything except the last 4 characters
+text[-4:] # last 4 characters
+```
+
+## Step and reverse
+
+The step controls how many characters to skip.
+A step of `-1` reverses the string.
+
+```python
+text[::2] # every second character
+text[::-1] # reversed string
+```
diff --git a/L02/T04/tests/test_task.py b/L02/T04/tests/test_task.py
new file mode 100644
index 0000000..f031097
--- /dev/null
+++ b/L02/T04/tests/test_task.py
@@ -0,0 +1,132 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import(task_name):
+ return importlib.import_module(task_name)
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'string_indexes_exercise'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseStringIndexes.py'
+ )
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except SyntaxError as se:
+ self.fail("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings (empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ expected_value = ' am'
+
+ try:
+ actual_value = try_import(self.task_name).answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+ def test_answer_2(self):
+ expected_value = ' am PYTHON'
+
+ try:
+ actual_value = try_import(self.task_name).answer_2
+ except AttributeError:
+ self.fail(msg="The variable answer_2 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_2 value seems to be a bit off.")
+
+ def test_answer_3(self):
+ expected_value = 'I am PYTHON'
+
+ try:
+ actual_value = try_import(self.task_name).answer_3
+ except AttributeError:
+ self.fail(msg="The variable answer_3 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_3 value seems to be a bit off.")
+
+ def test_answer_4(self):
+ expected_value = ' am PYTHON'
+
+ try:
+ actual_value = try_import(self.task_name).answer_4
+ except AttributeError:
+ self.fail(msg="The variable answer_4 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_4 value seems to be a bit off.")
+
+ def test_answer_5(self):
+ expected_value = 'N'
+
+ try:
+ actual_value = try_import(self.task_name).answer_5
+ except AttributeError:
+ self.fail(msg="The variable answer_5 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_5 value seems to be a bit off.")
+
+ def test_answer_6(self):
+ expected_value = 'T'
+
+ try:
+ actual_value = try_import(self.task_name).answer_6
+ except AttributeError:
+ self.fail(msg="The variable answer_6 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_6 value seems to be a bit off.")
+
+ def test_answer_7(self):
+ expected_value = 'I am PYT'
+
+ try:
+ actual_value = try_import(self.task_name).answer_7
+ except AttributeError:
+ self.fail(msg="The variable answer_7 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_7 value seems to be a bit off.")
+
+ def test_answer_8(self):
+ expected_value = 'HON'
+
+ try:
+ actual_value = try_import(self.task_name).answer_8
+ except AttributeError:
+ self.fail(msg="The variable answer_8 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_8 value seems to be a bit off.")
+
+ def test_answer_9(self):
+ expected_value = 'NOHTYP ma I'
+
+ try:
+ actual_value = try_import(self.task_name).answer_9
+ except AttributeError:
+ self.fail(msg="The variable answer_9 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_9 value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T05/ExerciseTypeConversion.py b/L02/T05/ExerciseTypeConversion.py
new file mode 100644
index 0000000..a14b21e
--- /dev/null
+++ b/L02/T05/ExerciseTypeConversion.py
@@ -0,0 +1,10 @@
+# Type Conversion Exercise
+# Replace the value below with your own birth year.
+# Then run the file and make sure the printed age looks correct to you.
+
+birth_year = '1990' # This is a string!
+
+# Convert birth_year to an integer and calculate age
+age = 2026 - int(birth_year)
+
+print(f'Your age is {age} years.')
diff --git a/L02/T05/task-info.yaml b/L02/T05/task-info.yaml
new file mode 100644
index 0000000..fb3d471
--- /dev/null
+++ b/L02/T05/task-info.yaml
@@ -0,0 +1,13 @@
+type: edu
+custom_name: Type Conversion
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseTypeConversion.py
+ visible: true
+ placeholders:
+ - offset: 260
+ length: 15
+ placeholder_text: birth_year
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T05/task.md b/L02/T05/task.md
new file mode 100644
index 0000000..3637830
--- /dev/null
+++ b/L02/T05/task.md
@@ -0,0 +1,39 @@
+# Type Conversion
+
+In this task you'll see:
+```python
+birth_year = '1990' # This is a string!
+age = 2026 - birth_year
+```
+
+If you run this code, you'll get an error. Why?
+
+## Understanding Types
+
+Python has different data types:
+- **Strings** - text in quotes: `'1990'`, `"hello"`
+- **Integers** - whole numbers: `1990`, `42`, `-5`
+- **Floats** - decimals: `3.14`, `2.5`
+
+Each type has different capabilities. You can't perform math directly on strings!
+
+## Type Conversion Functions
+
+Python provides functions to convert between types:
+
+| Function | Purpose | Example Input | Example Output |
+|----------|---------|---------------|----------------|
+| `int()` | Convert to integer | `'1990'` | `1990` |
+| `float()` | Convert to float | `'3.14'` | `3.14` |
+| `str()` | Convert to string | `1990` | `'1990'` |
+
+## How to Use Conversion Functions
+
+Wrap the value you want to convert:
+```python
+text_number = '42'
+real_number = conversion_function(text_number)
+```
+
+## Your Task
+The variable `birth_year` is currently a string. To use it in math, you need to convert it to a number type first.
diff --git a/L02/T05/tests/test_task.py b/L02/T05/tests/test_task.py
new file mode 100644
index 0000000..58e4539
--- /dev/null
+++ b/L02/T05/tests/test_task.py
@@ -0,0 +1,51 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import():
+ return importlib.import_module('type_conversion')
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'type_conversion'
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ importlib.reload(sys.modules[self.task_name])
+ else:
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseTypeConversion.py'
+ )
+ spec = importlib.util.spec_from_file_location(self.task_name, task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError as ne:
+ pass
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_age_type_and_value(self):
+ try:
+ age = try_import().age
+ except AttributeError:
+ self.fail(msg="The variable age seems to be undefined. "
+ "Do not remove it from the task code.")
+
+ self.assertIsInstance(age, int, msg="The age variable has a wrong type. "
+ "Make sure you convert birth_year using int().")
+ self.assertGreater(age, 0, msg="The age variable should be greater than 0. "
+ "Make sure you calculate it correctly.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T06/ExercisePasswordChecker.py b/L02/T06/ExercisePasswordChecker.py
new file mode 100644
index 0000000..8aec7cb
--- /dev/null
+++ b/L02/T06/ExercisePasswordChecker.py
@@ -0,0 +1,10 @@
+# Password Checker Exercise
+# Hide the password with asterisks
+
+username = input('Enter your username:\t')
+password = input('Enter you password:\t')
+
+# Hide the password by creating a string of asterisks
+secret_password = '*' * len(password)
+
+print(f'Hey {username}, your password {secret_password} is {len(password)} letters long.')
diff --git a/L02/T06/task-info.yaml b/L02/T06/task-info.yaml
new file mode 100644
index 0000000..28d27a1
--- /dev/null
+++ b/L02/T06/task-info.yaml
@@ -0,0 +1,16 @@
+type: edu
+custom_name: Password Checker
+files:
+ - name: task.md
+ visible: false
+ - name: ExercisePasswordChecker.py
+ visible: true
+ placeholders:
+ - offset: 223
+ length: 1
+ placeholder_text: ???
+ - offset: 228
+ length: 13
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T06/task.md b/L02/T06/task.md
new file mode 100644
index 0000000..41b8309
--- /dev/null
+++ b/L02/T06/task.md
@@ -0,0 +1,23 @@
+# Password Checker
+
+## What Is Password Masking?
+
+When applications display passwords, they often hide the actual characters by replacing them with asterisks (`*`). This protects privacy by preventing others from seeing the password on screen.
+
+For example:
+- Actual password: `super_secret`
+- Displayed password: `************`
+
+## String Operations Needed
+
+### String Repetition
+You can repeat strings using the `*` operator:
+```python
+character * number # Repeats the character 'number' times
+```
+
+### String Length
+The `len()` function returns how many characters are in a string:
+```python
+len('hello') # Returns 5
+```
diff --git a/L02/T06/tests/test_task.py b/L02/T06/tests/test_task.py
new file mode 100644
index 0000000..c56aa8c
--- /dev/null
+++ b/L02/T06/tests/test_task.py
@@ -0,0 +1,82 @@
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'password_checker_exercise'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExercisePasswordChecker.py'
+ )
+
+ def setUp(self):
+ self.import_error = None
+ self.module = None
+ try:
+ with patch('sys.stdout', new=StringIO()), patch(
+ 'builtins.input',
+ side_effect=['alice', 'SuperSecret123']
+ ):
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ self.module = module
+ except SyntaxError as se:
+ self.import_error = ("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings "
+ "(empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.import_error = ("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_username_is_set(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ try:
+ actual_value = self.module.username
+ except AttributeError:
+ self.fail(msg="The variable username seems to be undefined. Do not remove it from the task code.")
+
+ self.assertIsInstance(actual_value, str, msg="The username should be a string.")
+ self.assertTrue(actual_value, msg="The username should not be empty.")
+
+ def test_password_is_set(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ try:
+ actual_value = self.module.password
+ except AttributeError:
+ self.fail(msg="The variable password seems to be undefined. Do not remove it from the task code.")
+
+ self.assertIsInstance(actual_value, str, msg="The password should be a string.")
+ self.assertTrue(actual_value, msg="The password should not be empty.")
+
+ def test_secret_password_is_masked(self):
+ if self.import_error:
+ self.fail(self.import_error)
+
+ try:
+ secret_password = self.module.secret_password
+ except AttributeError:
+ self.fail(msg="The variable secret_password seems to be undefined. Do not remove it from the task code.")
+
+ self.assertIsInstance(secret_password, str, msg="The secret_password should be a string.")
+ self.assertTrue(secret_password, msg="The secret_password should not be empty.")
+ self.assertTrue(set(secret_password) == {'*'}, msg="The secret_password should contain only '*'.")
+ self.assertEqual(
+ '*' * len(self.module.password),
+ secret_password,
+ msg="The secret_password length should match the password length."
+ )
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T07/ExerciseListSlicing.py b/L02/T07/ExerciseListSlicing.py
new file mode 100644
index 0000000..f0d18e2
--- /dev/null
+++ b/L02/T07/ExerciseListSlicing.py
@@ -0,0 +1,24 @@
+# Lists Slicing
+# Complete the list operations
+
+# Create lists
+letters = ['a', 'b', 'c'] # Create list with 'a', 'b', 'c'
+
+# List operations
+answer_1 = letters[1] # Access the second element
+answer_2 = letters[-2] # Access second from the end
+answer_3 = letters[1:3] # Slice 1..3
+
+# List modification
+letters[0] = 'z'
+answer_4 = ['z', 'b', 'c'] # Predict numbers list after the change
+
+# List operations
+bonus = letters + ['e']
+letters[0] = 'x'
+
+answer_5 = ['x', 'b', 'c'] # Predict numbers now
+answer_6 = ['z', 'b', 'c', 'e'] # Predict bonus now
+
+
+
diff --git a/L02/T07/task-info.yaml b/L02/T07/task-info.yaml
new file mode 100644
index 0000000..a8c1a3b
--- /dev/null
+++ b/L02/T07/task-info.yaml
@@ -0,0 +1,31 @@
+type: edu
+custom_name: List Slicing
+files:
+ - name: task.md
+ visible: false
+ - name: tests/test_task.py
+ visible: false
+ - name: ExerciseListSlicing.py
+ visible: true
+ placeholders:
+ - offset: 74
+ length: 13
+ placeholder_text: ???
+ - offset: 161
+ length: 1
+ placeholder_text: ???
+ - offset: 212
+ length: 2
+ placeholder_text: ???
+ - offset: 265
+ length: 3
+ placeholder_text: ???
+ - offset: 334
+ length: 13
+ placeholder_text: ???
+ - offset: 462
+ length: 13
+ placeholder_text: ???
+ - offset: 511
+ length: 18
+ placeholder_text: ???
diff --git a/L02/T07/task.md b/L02/T07/task.md
new file mode 100644
index 0000000..97d6240
--- /dev/null
+++ b/L02/T07/task.md
@@ -0,0 +1,28 @@
+# List Slicing
+
+## What Is Slicing?
+
+Slicing lets you extract portions of a list using a range of indices. It's a powerful way to work with subsets of data without modifying the original list.
+
+## Indexing Recap
+
+Before slicing, remember basic indexing:
+- **Positive indices** - Count from the start (0, 1, 2, ...)
+- **Negative indices** - Count from the end (-1 is last, -2 is second-to-last, ...)
+
+## Slice Syntax
+
+```python
+list[start:end]
+```
+
+- **start** - First index to include
+- **end** - Stop BEFORE this index (not included!)
+- Creates a new list with elements from start up to (but not including) end
+
+## List Independence
+
+When you create new lists (through slicing, concatenation, etc.), they are independent:
+- Changes to the original don't affect copies
+- Changes to copies don't affect the original
+
diff --git a/L02/T07/tests/test_task.py b/L02/T07/tests/test_task.py
new file mode 100644
index 0000000..44184d9
--- /dev/null
+++ b/L02/T07/tests/test_task.py
@@ -0,0 +1,102 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import(task_name):
+ return importlib.import_module(task_name)
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'lists_exercise'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseListSlicing.py'
+ )
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except SyntaxError as se:
+ self.fail("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings (empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ expected_value = 'b'
+
+ try:
+ actual_value = try_import(self.task_name).answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+ def test_answer_2(self):
+ expected_value = 'b'
+
+ try:
+ actual_value = try_import(self.task_name).answer_2
+ except AttributeError:
+ self.fail(msg="The variable answer_2 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_2 value seems to be a bit off.")
+
+ def test_answer_3(self):
+ expected_value = ['b', 'c']
+
+ try:
+ actual_value = try_import(self.task_name).answer_3
+ except AttributeError:
+ self.fail(msg="The variable answer_3 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_3 value seems to be a bit off.")
+
+ def test_answer_4(self):
+ expected_value = ['z', 'b', 'c']
+
+ try:
+ actual_value = try_import(self.task_name).answer_4
+ except AttributeError:
+ self.fail(msg="The variable answer_4 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_4 value seems to be a bit off.")
+
+ def test_answer_5(self):
+ expected_value = ['x', 'b', 'c']
+
+ try:
+ actual_value = try_import(self.task_name).answer_5
+ except AttributeError:
+ self.fail(msg="The variable answer_5 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_5 value seems to be a bit off.")
+
+ def test_answer_6(self):
+ expected_value = ['z', 'b', 'c', 'e']
+
+ try:
+ actual_value = try_import(self.task_name).answer_6
+ except AttributeError:
+ self.fail(msg="The variable answer_6 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_6 value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T08/ExerciseMatrix.py b/L02/T08/ExerciseMatrix.py
new file mode 100644
index 0000000..6d86e3b
--- /dev/null
+++ b/L02/T08/ExerciseMatrix.py
@@ -0,0 +1,7 @@
+# Matrix Exercise
+# Access nested elements
+
+basket = ["Banana", ["Apples", ["Oranges"], "Blueberries"]]
+
+# Access "Oranges" and assign it to result
+result = basket[1][1][0]
\ No newline at end of file
diff --git a/L02/T08/task-info.yaml b/L02/T08/task-info.yaml
new file mode 100644
index 0000000..8846de3
--- /dev/null
+++ b/L02/T08/task-info.yaml
@@ -0,0 +1,13 @@
+type: edu
+custom_name: Matrix Operations
+files:
+ - name: task.md
+ visible: false
+ - name: ExerciseMatrix.py
+ visible: true
+ placeholders:
+ - offset: 157
+ length: 15
+ placeholder_text: "basket[?][?][?]"
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T08/task.md b/L02/T08/task.md
new file mode 100644
index 0000000..c8f2b76
--- /dev/null
+++ b/L02/T08/task.md
@@ -0,0 +1,36 @@
+# Matrix Operations
+
+## What Are Nested Lists?
+
+Lists can contain other lists as elements. These "lists within lists" are called **nested lists** or **multi-dimensional lists**. They're useful for representing complex data structures like grids, tables, or hierarchical data.
+
+```python
+simple_list = [1, 2, 3]
+nested_list = [1, [2, 3], 4]
+deeply_nested = ["a", ["b", ["c", "d"], "e"], "f"]
+```
+
+## Accessing Nested Elements
+
+To access elements in nested lists, use **chained indexing** - apply multiple index operations one after another:
+
+```python
+data = ["first", ["second", "third"], "fourth"]
+
+# Access the nested list at index 1
+data[1] # Returns ["second", "third"]
+
+# Chain indices to access elements within the nested list
+data[1][0] # Returns "second"
+data[1][1] # Returns "third"
+```
+
+## How It Works
+
+Each index operation selects one level deeper:
+- First index: selects from the outer list
+- Second index: selects from the list you just accessed
+- Third index: goes even deeper (if nested further)
+
+Think of it like drilling down through layers - each index takes you one layer deeper into the structure.
+
diff --git a/L02/T08/tests/test_task.py b/L02/T08/tests/test_task.py
new file mode 100644
index 0000000..334f312
--- /dev/null
+++ b/L02/T08/tests/test_task.py
@@ -0,0 +1,48 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import():
+ return importlib.import_module('matrix_operations')
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'matrix_operations'
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseMatrix.py'
+ )
+ spec = importlib.util.spec_from_file_location(self.task_name, task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_result(self):
+ expected_value = "Oranges"
+
+ try:
+ actual_value = try_import().result
+ except AttributeError:
+ self.fail(msg="The variable result seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The result value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T09/ExerciseListMethods.py b/L02/T09/ExerciseListMethods.py
new file mode 100644
index 0000000..1ca78fb
--- /dev/null
+++ b/L02/T09/ExerciseListMethods.py
@@ -0,0 +1,21 @@
+# List Methods Exercise
+
+basket = ["Banana", "Apples", "Oranges", "Blueberries"]
+
+# Remove "Banana" from the list
+basket.remove("Banana")
+
+# Remove "Blueberries" from the list
+basket.remove("Blueberries")
+
+# Put "Kiwi" at the end of the list
+basket.append("Kiwi")
+
+# Add "Apples" at the beginning of the list
+basket.insert(0, "Apples")
+
+# Count how many apples are in the basket
+apple_count = basket.count("Apples")
+
+# You could also use basket.clear() to empty the basket,
+# but let’s not do that here so we can check the result.
\ No newline at end of file
diff --git a/L02/T09/task-info.yaml b/L02/T09/task-info.yaml
new file mode 100644
index 0000000..055b879
--- /dev/null
+++ b/L02/T09/task-info.yaml
@@ -0,0 +1,25 @@
+type: edu
+custom_name: List Methods
+files:
+ - name: task.md
+ visible: false
+ - name: tests/test_task.py
+ visible: false
+ - name: ExerciseListMethods.py
+ visible: true
+ placeholders:
+ - offset: 121
+ length: 16
+ placeholder_text: ???
+ - offset: 183
+ length: 21
+ placeholder_text: ???
+ - offset: 249
+ length: 14
+ placeholder_text: ???
+ - offset: 316
+ length: 19
+ placeholder_text: ???
+ - offset: 400
+ length: 15
+ placeholder_text: ???
diff --git a/L02/T09/task.md b/L02/T09/task.md
new file mode 100644
index 0000000..0f6a810
--- /dev/null
+++ b/L02/T09/task.md
@@ -0,0 +1,46 @@
+# List Methods
+
+## What Are List Methods?
+
+Methods are built-in functions that belong to specific data types. Lists come with powerful methods that let you add, remove, find, and manipulate elements. These methods modify the list directly rather than creating a new one.
+
+## Common List Methods
+
+### Adding Elements
+- **append()** - Adds an item to the end of the list
+- **insert()** - Adds an item at a specific position
+- **extend()** - Adds all items from another list to the end
+
+### Removing Elements
+- **remove()** - Removes the first occurrence of a specific value
+- **pop()** - Removes and returns an item at a specific position (or the last item if no position given)
+- **clear()** - Removes all items from the list
+
+### Finding Information
+- **count()** - Returns how many times a value appears in the list
+- **index()** - Returns the position of the first occurrence of a value
+
+### Reordering and Copying
+- **sort()** - Sorts the list in place
+- **reverse()** - Reverses the list in place
+- **copy()** - Creates a shallow copy of the list
+
+## Method Syntax
+
+Methods are called using dot notation:
+
+```python
+my_list.method_name(arguments)
+```
+
+Some methods need arguments (values to work with), others don't:
+- `basket.append("Kiwi")` - needs the item to add
+- `basket.remove("Banana")` - needs the item to remove
+- `basket.count("Apples")` - needs the item to count
+
+## Mutability Reminder
+
+List methods typically **modify the original list** - they don't create a copy. After calling a method, your list has changed.
+Most list methods return `None` because the list itself is modified.
+
+
diff --git a/L02/T09/tests/test_task.py b/L02/T09/tests/test_task.py
new file mode 100644
index 0000000..1e01557
--- /dev/null
+++ b/L02/T09/tests/test_task.py
@@ -0,0 +1,58 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import():
+ return importlib.import_module('list_methods')
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'list_methods'
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseListMethods.py'
+ )
+ spec = importlib.util.spec_from_file_location(self.task_name, task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_basket(self):
+ expected_value = ["Apples", "Apples", "Oranges", "Kiwi"]
+
+ try:
+ actual_value = try_import().basket
+ except AttributeError:
+ self.fail(msg="The variable basket seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The basket value seems to be a bit off.")
+
+ def test_apple_count(self):
+ expected_value = 2
+
+ try:
+ actual_value = try_import().apple_count
+ except AttributeError:
+ self.fail(msg="The variable apple_count seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The apple_count value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T10/ExerciseCommonListPatterns.py b/L02/T10/ExerciseCommonListPatterns.py
new file mode 100644
index 0000000..16a2361
--- /dev/null
+++ b/L02/T10/ExerciseCommonListPatterns.py
@@ -0,0 +1,8 @@
+# List Patterns Exercise
+# Fix the code so it prints an alphabetically sorted list of all friends
+
+friends = ['Simon', 'Patty', 'Joy', 'Carrie', 'Amira', 'Chu']
+new_friend = ['Stanley']
+
+friends.extend(new_friend)
+friends.sort()
\ No newline at end of file
diff --git a/L02/T10/task-info.yaml b/L02/T10/task-info.yaml
new file mode 100644
index 0000000..626d347
--- /dev/null
+++ b/L02/T10/task-info.yaml
@@ -0,0 +1,14 @@
+type: edu
+custom_name: Common List Patterns
+files:
+ - name: task.md
+ visible: false
+ - name: tests/test_task.py
+ visible: false
+ - name: ExerciseCommonListPatterns.py
+ visible: true
+ placeholders:
+ - offset: 187
+ length: 41
+ placeholder_text: "friends = friends.sort() + new_friend # Wrong solution, try\
+ \ to fix it"
diff --git a/L02/T10/task.md b/L02/T10/task.md
new file mode 100644
index 0000000..db2efb4
--- /dev/null
+++ b/L02/T10/task.md
@@ -0,0 +1,90 @@
+# Common List Patterns
+
+## Adding items to a list: extend()
+
+`extend()` adds all elements from another list to the end of the current list.
+It modifies the list in place (the original list changes).
+
+```python
+letters = ['a', 'b']
+extra = ['c', 'd']
+
+letters.extend(extra)
+# letters is now ['a', 'b', 'c', 'd']
+```
+
+Use `extend()` when you already have a list and want to add multiple items from another list.
+
+## Sorting a list in place: sort()
+
+`sort()` arranges items in the list (alphabetical for strings).
+It modifies the list in place.
+
+```python
+names = ['B', 'A', 'C']
+names.sort()
+# names is now ['A', 'B', 'C']
+```
+
+You can also sort in reverse order:
+
+```python
+names.sort(reverse=True)
+# names is now ['C', 'B', 'A']
+```
+
+## Creating a new sorted list: sorted()
+
+`sorted()` returns a new sorted list and does not change the original one.
+
+```python
+names = ['B', 'A', 'C']
+sorted_names = sorted(names)
+
+# names is still ['B', 'A', 'C']
+# sorted_names is ['A', 'B', 'C']
+```
+
+## Reversing a list with slicing: [::-1]
+
+Slicing with `[::-1]` returns a new list in reverse order.
+It does not change the original list.
+
+```python
+numbers = [1, 2, 3]
+reversed_numbers = numbers[::-1]
+
+# numbers is still [1, 2, 3]
+# reversed_numbers is [3, 2, 1]
+```
+
+## Creating a sequence of numbers: range()
+
+`range()` creates a sequence of numbers.
+It does not create a list by itself, but you can convert it to a list with `list()`.
+
+```python
+nums = range(1, 5) # 1, 2, 3, 4
+nums_list = list(nums) # [1, 2, 3, 4]
+```
+
+## Joining list items into a string: join()
+
+`join()` combines list items into a single string with a separator.
+It is a string method and expects a list of strings.
+
+```python
+words = ['Hi', 'my', 'name', 'is', 'JOJO']
+sentence = '! '
+result = sentence.join(words)
+
+# result is 'Hi! my! name! is! JOJO'
+```
+
+## Which one should you use?
+
+Use `list.sort()` when you want to sort the existing list itself.
+
+Use `sorted(list)` when you want to keep the original list unchanged and get a new sorted version.
+
+In this task, you’ll need to add a new friend to the list and then make sure the final output is alphabetically sorted.
diff --git a/L02/T10/tests/test_task.py b/L02/T10/tests/test_task.py
new file mode 100644
index 0000000..9876a01
--- /dev/null
+++ b/L02/T10/tests/test_task.py
@@ -0,0 +1,48 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import():
+ return importlib.import_module('common_list_patterns')
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'common_list_patterns'
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'ExerciseCommonListPatterns.py'
+ )
+ spec = importlib.util.spec_from_file_location(self.task_name, task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_friends(self):
+ expected_value = ['Amira', 'Carrie', 'Chu', 'Joy', 'Patty', 'Simon', 'Stanley']
+
+ try:
+ actual_value = try_import().friends
+ except AttributeError:
+ self.fail(msg="The variable friends seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The friends value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/L02/T11/Dictionaries.py b/L02/T11/Dictionaries.py
new file mode 100644
index 0000000..8f6c536
--- /dev/null
+++ b/L02/T11/Dictionaries.py
@@ -0,0 +1,33 @@
+# Dictionaries Exercise
+
+# Create a user profile for your new game.
+# Store it in a dictionary:
+user_profile = {
+ 'age': 0,
+ 'username': '',
+ 'weapons': None,
+ 'is_active': False,
+ 'clan': None
+}
+
+# Get all the keys from user_profile and convert them to a list
+answer_1 = list(user_profile.keys())
+
+# Set the value under key 'weapons' to 'Katana'
+user_profile['weapons'] = 'Katana'
+answer_2 = user_profile['weapons']
+
+# Add a new key 'is_banned' and set it to False
+user_profile.update({'is_banned': False})
+answer_3 = user_profile['is_banned']
+
+# Change the value under key 'is_banned' to True
+user_profile['is_banned'] = True
+answer_4 = user_profile['is_banned']
+
+# Copy user_profile into user2 (create a new dictionary)
+user2 = user_profile.copy()
+
+# Change age to 50 and username to 'User2' only in user2
+user2.update({'age': 50, 'username': 'User2'})
+answer_5 = user2
\ No newline at end of file
diff --git a/L02/T11/task-info.yaml b/L02/T11/task-info.yaml
new file mode 100644
index 0000000..e974309
--- /dev/null
+++ b/L02/T11/task-info.yaml
@@ -0,0 +1,34 @@
+type: edu
+custom_name: Dictionary Methods
+files:
+ - name: task.md
+ visible: false
+ - name: Dictionaries.py
+ visible: true
+ placeholders:
+ - offset: 287
+ length: 25
+ placeholder_text: ???
+ - offset: 375
+ length: 9
+ placeholder_text: ???
+ - offset: 388
+ length: 8
+ placeholder_text: ???
+ - offset: 494
+ length: 28
+ placeholder_text: ???
+ - offset: 623
+ length: 11
+ placeholder_text: ???
+ - offset: 638
+ length: 4
+ placeholder_text: ???
+ - offset: 746
+ length: 19
+ placeholder_text: ???
+ - offset: 830
+ length: 40
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T11/task.md b/L02/T11/task.md
new file mode 100644
index 0000000..17b7353
--- /dev/null
+++ b/L02/T11/task.md
@@ -0,0 +1,23 @@
+# Working With Dictionaries
+
+## Theory
+
+A dictionary (`dict`) is a data structure for storing data as **key -> value** pairs. It is useful when you want to access information by a meaningful name (the key), not by position like in a list. A common example is storing information about a book, a product, or a student: keys like "title", "price", "grade" point to their values.
+
+### Creating and accessing
+
+You can create a dictionary with curly braces `{}` using `key: value` pairs. Keys are often strings, and values can be different types: numbers, strings, booleans, `None`, lists, or even other dictionaries.
+
+To **read** a value, use square brackets with the key: `item['price']`. To **change** a value, assign to that key: `item['price'] = 10`. If the key does not exist yet, this assignment creates a new key in the dictionary.
+
+### Inspecting keys
+
+`dict.keys()` returns a view of all keys stored in the dictionary. If you need a regular list (for example, for tests or for printing), convert it with `list(item.keys())`.
+
+### Updating
+
+`dict.update(...)` updates the dictionary using another dictionary. It adds new keys and overwrites existing keys with new values. For example, `item.update({'in_stock': True, 'price': 12})` can add "in_stock" and also replace the current "price".
+
+### Copying
+
+`dict.copy()` creates a new dictionary with the same key-value pairs. This is useful when you want to make changes to a copy without modifying the original dictionary. (It is a shallow copy: nested mutable values like lists are still shared, but simple values like numbers and strings are copied safely.)
diff --git a/L02/T11/tests/test_task.py b/L02/T11/tests/test_task.py
new file mode 100644
index 0000000..f103632
--- /dev/null
+++ b/L02/T11/tests/test_task.py
@@ -0,0 +1,99 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import(task_name):
+ return importlib.import_module(task_name)
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'dictionaries_exercise'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'Dictionaries.py'
+ )
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except SyntaxError as se:
+ self.fail("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings (empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ expected_value = ['age', 'username', 'weapons', 'is_active', 'clan']
+
+ try:
+ actual_value = try_import(self.task_name).answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+ def test_answer_2(self):
+ expected_value = 'Katana'
+
+ try:
+ actual_value = try_import(self.task_name).answer_2
+ except AttributeError:
+ self.fail(msg="The variable answer_2 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_2 value seems to be a bit off.")
+
+ def test_answer_3(self):
+ expected_value = False
+
+ try:
+ actual_value = try_import(self.task_name).answer_3
+ except AttributeError:
+ self.fail(msg="The variable answer_3 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_3 value seems to be a bit off.")
+
+ def test_answer_4(self):
+ expected_value = True
+
+ try:
+ actual_value = try_import(self.task_name).answer_4
+ except AttributeError:
+ self.fail(msg="The variable answer_4 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_4 value seems to be a bit off.")
+
+ def test_answer_5(self):
+ expected_value = {
+ 'age': 50,
+ 'username': 'User2',
+ 'weapons': 'Katana',
+ 'is_active': False,
+ 'clan': None,
+ 'is_banned': True
+ }
+
+ try:
+ actual_value = try_import(self.task_name).answer_5
+ except AttributeError:
+ self.fail(msg="The variable answer_5 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_5 value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/L02/T12/Sets.py b/L02/T12/Sets.py
new file mode 100644
index 0000000..491c435
--- /dev/null
+++ b/L02/T12/Sets.py
@@ -0,0 +1,7 @@
+# Sets Exercise: School attendance
+
+school = {'Bobby', 'Tammy', 'Jammy', 'Sally', 'Danny'}
+attendance_list = ['Jammy', 'Bobby', 'Danny', 'Sally']
+
+# 1 Find the students who missed class (in school but NOT in attendance_list).
+answer_1 = school.difference(attendance_list)
\ No newline at end of file
diff --git a/L02/T12/task-info.yaml b/L02/T12/task-info.yaml
new file mode 100644
index 0000000..8ceb6b1
--- /dev/null
+++ b/L02/T12/task-info.yaml
@@ -0,0 +1,13 @@
+type: edu
+custom_name: Working With Sets
+files:
+ - name: task.md
+ visible: false
+ - name: Sets.py
+ visible: true
+ placeholders:
+ - offset: 244
+ length: 27
+ placeholder_text: ???
+ - name: tests/test_task.py
+ visible: false
diff --git a/L02/T12/task.md b/L02/T12/task.md
new file mode 100644
index 0000000..78ddf6f
--- /dev/null
+++ b/L02/T12/task.md
@@ -0,0 +1,35 @@
+# Working With Sets
+
+## Theory
+
+A **set** (`set`) is a collection that stores **unique** values. That means duplicates are removed automatically. Sets are great when you need to quickly check “is this value present?” or when you want to compare groups of items (for example: who is in the school database vs who was present today).
+
+## Difference
+
+One of the most useful set operations is **difference**.
+
+`A.difference(B)` returns a **new set** with all elements that are in `A` but **not** in `B`.
+So it answers the question: “Who/what is missing from `B`, compared to `A`?”
+
+`difference()` does not require `B` to be a set. The second argument can be any **iterable** (like a list or tuple). Python will compare the values and return the missing ones.
+
+## Intersection
+
+Another useful operation is **intersection**.
+
+`A.intersection(B)` returns a **new set** with elements that appear in **both** `A` and `B`.
+This answers: “Who/what is included in both groups?”
+
+## Union
+
+You can also combine groups using **union**.
+
+`A.union(B)` returns a **new set** with **all unique** elements from `A` and `B`.
+This answers: “What are all the unique names/items we have in total?”
+
+## Disjoint
+
+Finally, `A.isdisjoint(B)` checks if two collections have **no elements in common**.
+It returns `True` if they do not overlap at all, and `False` if they share at least one value.
+
+Just like `difference()`, these methods (`intersection`, `union`, `isdisjoint`) can take any **iterable** as the second argument.
diff --git a/L02/T12/tests/test_task.py b/L02/T12/tests/test_task.py
new file mode 100644
index 0000000..11faa4a
--- /dev/null
+++ b/L02/T12/tests/test_task.py
@@ -0,0 +1,52 @@
+import importlib
+import importlib.util
+from io import StringIO
+import os
+import sys
+import unittest
+from unittest.mock import patch
+
+
+def try_import(task_name):
+ return importlib.import_module(task_name)
+
+
+class TestCase(unittest.TestCase):
+ task_name = 'sets'
+ task_file = os.path.join(
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
+ 'Sets.py'
+ )
+
+ def setUp(self):
+ try:
+ with patch('sys.stdout', new=StringIO()) as self.actualOutput:
+ if self.task_name in sys.modules:
+ del sys.modules[self.task_name]
+ spec = importlib.util.spec_from_file_location(self.task_name, self.task_file)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ sys.modules[self.task_name] = module
+ except NameError:
+ pass
+ except SyntaxError as se:
+ self.fail("Syntax error while loading the solution – {0}. "
+ "Please fill in all answers with valid numbers or strings (empty placeholders are invalid)."
+ .format(str(se)))
+ except Exception as e:
+ self.fail("There was a problem while loading the solution – {0}. Check the solution for "
+ "IDE-highlighted errors and warnings.".format(str(e)))
+
+ def test_answer_1(self):
+ expected_value = {'Tammy'}
+
+ try:
+ actual_value = try_import(self.task_name).answer_1
+ except AttributeError:
+ self.fail(msg="The variable answer_1 seems to be undefined. Do not remove it from the task code.")
+
+ self.assertEqual(expected_value, actual_value, msg="The answer_1 value seems to be a bit off.")
+
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/L02/lesson-info.yaml b/L02/lesson-info.yaml
new file mode 100644
index 0000000..f980e91
--- /dev/null
+++ b/L02/lesson-info.yaml
@@ -0,0 +1,14 @@
+custom_name: Python Basics
+content:
+ - T01
+ - T02
+ - T03
+ - T04
+ - T05
+ - T06
+ - T07
+ - T08
+ - T09
+ - T10
+ - T11
+ - T12
diff --git a/Notes from classes/1. Python Basics/Dictionaries.py b/Notes from classes/1. Python Basics/Dictionaries.py
new file mode 100644
index 0000000..1389d92
--- /dev/null
+++ b/Notes from classes/1. Python Basics/Dictionaries.py
@@ -0,0 +1,41 @@
+# Dictionaries
+
+dictionary = {
+ 'a': [1, 2, 3],
+ 'b': 'Hello',
+ 'c': True
+}
+
+print(dictionary['a'][1])
+print(dictionary)
+print('\n')
+
+
+# Dictionary Methods
+
+user = {
+ 'basket': [1, 2, 3],
+ 'greet': 'Hello',
+ 'age': 20
+}
+
+print(user.get('age', 55))
+
+user2 = dict(name='JohnJohn')
+print(user2)
+
+print('basket' in user)
+print('size' in user)
+print('age' in user.keys())
+print('Hello' in user.values())
+print(user.items())
+
+user2 = user.copy()
+user.clear()
+print(user)
+print(user2)
+print(user2.pop('age'))
+print(user2.popitem())
+user2.update({'age': 55})
+print(user2)
+print('\n')
diff --git a/Notes from classes/1. Python Basics/Int&Float.py b/Notes from classes/1. Python Basics/Int&Float.py
new file mode 100644
index 0000000..8982d46
--- /dev/null
+++ b/Notes from classes/1. Python Basics/Int&Float.py
@@ -0,0 +1,42 @@
+# Integers and Floating point numbers
+# int & float
+
+print(2 + 4)
+print(2 - 4)
+print(2 * 4)
+print(2 / 4)
+print("\n")
+
+print(type(2 + 4))
+print(type(2 - 4))
+print(type(2 * 4))
+print(type(2 / 4))
+print(type(0))
+print("\n")
+
+print(type(20 + 1.1))
+print(type(9.9 + 1.1))
+print(9.9 + 1.1)
+print("\n")
+
+print((2 ** 3)) # to the power of
+print(5 // 3) # integer division rounded down
+print(5 % 3) # modulo / remainder
+print("\n")
+
+# Maths functions
+# Round
+print(round(3.1))
+print(round(3.9))
+
+# Absolute values
+print(abs(-20))
+print("\n")
+
+# Complex Numbers
+complex()
+
+# Binary numbers
+print(bin(5))
+print(int("0b101", 2)) # ("number", base of number)
+print("\n")
diff --git a/Notes from classes/1. Python Basics/Lists.py b/Notes from classes/1. Python Basics/Lists.py
new file mode 100644
index 0000000..19875dc
--- /dev/null
+++ b/Notes from classes/1. Python Basics/Lists.py
@@ -0,0 +1,107 @@
+# Lists
+
+li = [1, 2, 3, 4, 5]
+li2 = ['a', 'b', 'c']
+li3 = [1, 2.5, 'a', True]
+amazon_cart = ['notebooks', 'sunglasses']
+print(amazon_cart[0])
+print('\n')
+
+
+# List Slicing
+# [Start : Stop : Step-Over]
+
+amazon_cart = ['notebooks', 'sunglasses', 'toys', 'grapes']
+print(amazon_cart)
+print(amazon_cart[0:2])
+print(amazon_cart[0::2])
+print(amazon_cart[::-1])
+
+amazon_cart[0] = 'laptop'
+print(amazon_cart)
+
+new_cart = amazon_cart[:]
+new_cart[0] = 'gum'
+print(new_cart)
+print(amazon_cart)
+
+new_cart1 = amazon_cart
+new_cart1[0] = 'gum'
+print(new_cart1)
+print(amazon_cart)
+print('\n')
+
+
+# Matrix
+
+matrix = [
+ [1, 0, 1],
+ [0, 1, 0],
+ [1, 0, 1]
+]
+print(matrix)
+print(matrix[0][2])
+print('\n')
+
+
+# List Methods
+
+basket = [1, 2, 3, 4, 5]
+
+# Adding
+new_list = basket.append(100)
+print(basket)
+print(new_list)
+basket.insert(4, 50)
+print(basket)
+basket.extend([10, 20, 30, 40])
+print(basket)
+print('\n')
+
+# Removing
+basket.pop()
+print(basket)
+basket.pop(4)
+print(basket)
+basket.remove(100)
+print(basket)
+# basket.clear()
+# print(basket)
+print('\n')
+
+print(basket.index(5))
+print(5 in basket)
+print(basket.count(4))
+print('\n')
+
+basket.sort()
+print(basket)
+new_basket = basket.copy()
+new_basket.sort(reverse=True)
+print(new_basket)
+print(sorted(new_basket))
+basket.reverse()
+print(basket)
+print('\n')
+
+
+# Common List Patterns
+
+print(basket[::-1])
+print(range(1, 100))
+print(list(range(101)))
+
+sentence = '! '
+new_sentence = sentence.join(['Hi', 'my', 'name', 'is', 'JOJO'])
+print(new_sentence)
+print('\n')
+
+
+# List Unpacking
+
+a, b, c, *other, d = [1, 2, 3, 4, 5, 6, 7, 8, 9]
+print(a)
+print(b)
+print(c)
+print(other)
+print(d)
diff --git a/Notes from classes/1. Python Basics/Sets.py b/Notes from classes/1. Python Basics/Sets.py
new file mode 100644
index 0000000..175bc77
--- /dev/null
+++ b/Notes from classes/1. Python Basics/Sets.py
@@ -0,0 +1,34 @@
+# Sets
+
+my_set = {1, 2, 3, 4, 5, 5}
+my_set.add(100)
+my_set.add(2)
+print(my_set)
+
+my_list = [1, 2, 3, 4, 4, 5, 5]
+print(set(my_list))
+print(list(my_set))
+
+new_set = my_set.copy()
+my_set.clear()
+print(my_set)
+print(new_set)
+print('\n')
+
+
+# Set Methods
+
+my_set = {1, 2, 3, 4, 5}
+your_set = {4, 5, 6, 7, 8, 9, 10}
+
+print(my_set.difference(your_set))
+print(my_set.intersection(your_set))
+print(my_set.isdisjoint(your_set))
+print(my_set.issubset(your_set))
+print(my_set.issuperset(your_set))
+print(my_set.union(your_set))
+my_set.difference_update(your_set)
+print(my_set)
+my_set.discard(5)
+print(my_set)
+
diff --git a/Notes from classes/1. Python Basics/String.py b/Notes from classes/1. Python Basics/String.py
new file mode 100644
index 0000000..20083c7
--- /dev/null
+++ b/Notes from classes/1. Python Basics/String.py
@@ -0,0 +1,87 @@
+# Strings
+
+print(type('Hi Hello there!'))
+username = 'SuperCoder'
+password = "SuperSecret"
+long_string = '''
+WOW
+o_o
+___
+'''
+print(long_string)
+
+first_name = 'Nitesh'
+last_name = 'Kumar'
+full_name = first_name + ' ' + last_name
+print(full_name)
+print("\n")
+
+# String Concatenation
+
+print('hello' + ' Nitesh')
+print('hello ' + str(5))
+print("\n")
+
+# Type Conversion
+
+print(type(int(str(100))))
+
+a = str(100)
+b = int(a)
+c = type(b)
+print(c)
+print("\n")
+
+
+# Escape Sequences
+
+weather = "\t It\'s \"kind of\" sunny \n Hope you have a good day!"
+print(weather)
+print("\n")
+
+
+# Formatted Strings
+
+name = 'Nitesh'
+age = 27
+print('Hi' + name + '. You are' + str(age) + ' years olf.')
+print(f'Hi {name}. You are {age} years old.')
+
+# Python2 Formatted Strings
+print('Hi {}. Your are {} years old.'.format(name, age))
+print('Hi {1}. You are {0} years olf.'.format(age, name))
+print('Hi {new_name}. You are {age} years old.'.format(new_name='Sally', age=100))
+print('\n')
+
+
+# String Indexes
+# String Slicing
+# [Start : Stop : Step-over]
+
+selfish = '01234567'
+# 01234567
+print((selfish[0]))
+print((selfish[7]))
+print((selfish[0:2]))
+print((selfish[0:7]))
+print((selfish[0:8]))
+print((selfish[0:8:2]))
+print((selfish[1:5]))
+print((selfish[::1]))
+print((selfish[-1]))
+print((selfish[-2]))
+print((selfish[::-1]))
+print((selfish[::-2]))
+print('\n')
+
+
+# Built-in Functions and Methods
+
+print(len('Helllooooo'))
+quote = 'to be or not to be'
+print(quote.upper())
+print(quote.capitalize())
+print(quote.find('be'))
+print(quote.replace('be', 'me'))
+print(quote)
+print('\n')
diff --git a/Notes from classes/1. Python Basics/Tuples.py b/Notes from classes/1. Python Basics/Tuples.py
new file mode 100644
index 0000000..70f013c
--- /dev/null
+++ b/Notes from classes/1. Python Basics/Tuples.py
@@ -0,0 +1,13 @@
+# Tuples
+
+my_tuple = (1, 2, 3, 4, 5)
+print(my_tuple[1])
+print(5 in my_tuple)
+print(my_tuple)
+
+x, y, z, *other = (1, 2, 3, 4, 5)
+print(x)
+print(z)
+
+print(my_tuple.count(5))
+print(my_tuple.index(5))
diff --git a/Notes from classes/2. Python Basics II/ArgsAndKwargs.py b/Notes from classes/2. Python Basics II/ArgsAndKwargs.py
new file mode 100644
index 0000000..3aca72c
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/ArgsAndKwargs.py
@@ -0,0 +1,14 @@
+# *args and **kwargs
+
+
+def super_func(name, *args, i='hi', **kwargs):
+ total = 0
+ for item in kwargs.values():
+ total += item
+ print(i, name)
+ return sum(args) + total
+
+
+print(super_func('nitesh', 1, 2, 3, 4, 5, i='hello', num1=5, num2=10))
+
+# Rule: params, *args, default parameters, **kwargs
diff --git a/Notes from classes/2. Python Basics II/BreakContinuePass.py b/Notes from classes/2. Python Basics II/BreakContinuePass.py
new file mode 100644
index 0000000..631c66f
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/BreakContinuePass.py
@@ -0,0 +1,23 @@
+# Break, Continue, Pass
+
+my_list = [1, 2, 3]
+i = 0
+while i < len(my_list):
+ print(my_list[i])
+ i += 1
+ continue
+print()
+
+i = 0
+while i < len(my_list):
+ print(my_list[i])
+ i += 1
+ break
+print()
+
+i = 0
+while i < len(my_list):
+ i += 1
+ pass
+
+print("No error")
diff --git a/Notes from classes/2. Python Basics II/CleanCode.py b/Notes from classes/2. Python Basics II/CleanCode.py
new file mode 100644
index 0000000..dcf3ea8
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/CleanCode.py
@@ -0,0 +1,10 @@
+# Clean Code
+# Write a function to check if number is odd or not.
+
+
+def is_odd(num):
+ return num % 2 == 1
+
+
+print(is_odd(5))
+print(is_odd(10))
diff --git a/Notes from classes/2. Python Basics II/ConditionalLogic.py b/Notes from classes/2. Python Basics II/ConditionalLogic.py
new file mode 100644
index 0000000..1fc873f
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/ConditionalLogic.py
@@ -0,0 +1,22 @@
+# Conditional Logic
+
+is_old = True
+is_licenced = True
+
+if is_old:
+ print('You are old enough to drive!')
+elif is_licenced:
+ print('You can drive now!')
+else:
+ print('You are not of age!')
+
+print('ok ok')
+print()
+
+# improved version
+if is_old and is_licenced:
+ print('You are old enough to drive, and you have a licence!')
+else:
+ print('You are not of age!')
+
+print('ok ok')
diff --git a/Notes from classes/2. Python Basics II/Docstrings.py b/Notes from classes/2. Python Basics II/Docstrings.py
new file mode 100644
index 0000000..3b9bf96
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Docstrings.py
@@ -0,0 +1,17 @@
+# Docstrings
+
+def test(a):
+ """
+ Info : This function tests and prints param a
+ :param a:
+ :return:
+ """
+
+ print(a)
+
+
+test('!!!')
+
+help(test)
+print()
+print(test.__doc__)
diff --git a/Notes from classes/2. Python Basics II/Enumerate().py b/Notes from classes/2. Python Basics II/Enumerate().py
new file mode 100644
index 0000000..2e1d439
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Enumerate().py
@@ -0,0 +1,11 @@
+# Enumerate()
+
+for i, char in enumerate('Helllooo'):
+ print(i, char)
+
+for i, char in enumerate([1, 2, 3]):
+ print(i, char)
+
+for i, char in enumerate(range(100)):
+ if char == 50:
+ print(f"index of 50 is: {i}")
diff --git a/Notes from classes/2. Python Basics II/ForLoops.py b/Notes from classes/2. Python Basics II/ForLoops.py
new file mode 100644
index 0000000..d6eafb4
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/ForLoops.py
@@ -0,0 +1,23 @@
+# For Loops
+
+for item in 'Zero to Mastery':
+ print(item, end=' ')
+print()
+
+for item in [1, 2, 3, 4, 5]:
+ print(item, end=' ')
+print()
+
+for item in {1, 2, 3, 4, 5}:
+ print(item, end=' ')
+print()
+
+for item in (1, 2, 3, 4, 5):
+ print(item, end=' ')
+print(item)
+print()
+
+# Nested Loops
+for item in (1, 2, 3, 4, 5):
+ for x in ['a', 'b', 'c']:
+ print(item, x, end='\t')
diff --git a/Notes from classes/2. Python Basics II/Functions.py b/Notes from classes/2. Python Basics II/Functions.py
new file mode 100644
index 0000000..e9cde16
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Functions.py
@@ -0,0 +1,35 @@
+# Functions
+
+def say_hello():
+ print('Helllooooo!')
+
+
+say_hello()
+print()
+
+picture = [
+ [0, 0, 0, 1, 0, 0, 0],
+ [0, 0, 1, 1, 1, 0, 0],
+ [0, 1, 1, 1, 1, 1, 0],
+ [1, 1, 1, 1, 1, 1, 1],
+ [0, 0, 0, 1, 0, 0, 0],
+ [0, 0, 0, 1, 0, 0, 0]
+]
+
+
+def show_tree():
+ for array in picture:
+ for item in array:
+ if item:
+ print('*', end='')
+ else:
+ print(' ', end='')
+ print()
+
+
+show_tree()
+print()
+show_tree()
+print()
+show_tree()
+print()
diff --git a/Notes from classes/2. Python Basics II/GlobalKeyword.py b/Notes from classes/2. Python Basics II/GlobalKeyword.py
new file mode 100644
index 0000000..88deb9d
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/GlobalKeyword.py
@@ -0,0 +1,14 @@
+# Global Keyword
+
+total = 0
+
+
+def count():
+ global total
+ total += 1
+ return total
+
+
+count()
+count()
+print(count())
diff --git a/Notes from classes/2. Python Basics II/IsVs==.py b/Notes from classes/2. Python Basics II/IsVs==.py
new file mode 100644
index 0000000..71591a0
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/IsVs==.py
@@ -0,0 +1,24 @@
+# Is vs ==
+
+print(True == 1)
+print('' == 1)
+print([] == 1)
+print(10 == 10.0)
+print([] == [])
+print()
+
+print(True == True)
+print('1' == 1)
+print([1, 2, 3] == [1, 2, 3])
+print()
+
+print(True is 1)
+print('1' is 1)
+print([] is 1)
+print(10 is 10.0)
+print([1, 2, 3] is [1, 2, 3])
+print()
+
+print(True is True)
+print('1' is '1')
+print([] is [])
diff --git a/Notes from classes/2. Python Basics II/Iterables.py b/Notes from classes/2. Python Basics II/Iterables.py
new file mode 100644
index 0000000..ead90b8
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Iterables.py
@@ -0,0 +1,22 @@
+# Iterables
+
+user = {
+ 'name': 'Golem',
+ 'age': 5006,
+ 'can_swim': False
+}
+
+for item in user:
+ print(item)
+
+for item in user.items():
+ print(item)
+
+for item in user.keys():
+ print(item)
+
+for item in user.values():
+ print(item)
+
+for key, value in user.items():
+ print(key, value)
diff --git a/Notes from classes/2. Python Basics II/NonLocalKeyword.py b/Notes from classes/2. Python Basics II/NonLocalKeyword.py
new file mode 100644
index 0000000..90740c7
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/NonLocalKeyword.py
@@ -0,0 +1,16 @@
+# Nonlocal Keyword
+
+
+def outer():
+ x = 'local'
+
+ def inner():
+ nonlocal x
+ x = 'nonlocal'
+ print('inner:', x)
+
+ inner()
+ print('outer:', x)
+
+
+outer()
diff --git a/Notes from classes/2. Python Basics II/ParametersAndArguments.py b/Notes from classes/2. Python Basics II/ParametersAndArguments.py
new file mode 100644
index 0000000..e0efc16
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/ParametersAndArguments.py
@@ -0,0 +1,23 @@
+# Parameters and Arguments
+
+# Parameters // Positional Parameters
+def say_hello(name, emoji):
+ print(f'helllooooo {name} {emoji}')
+
+
+# Arguments // Positional Arguments
+say_hello('Nitesh', ':)')
+say_hello('Emily', ':p')
+say_hello('Dah', ':\'(')
+print()
+
+
+# Default Parameters
+def say_hello(name='Darth Vader', emoji='>:('):
+ print(f'helllooooo {name} {emoji}')
+
+
+# Keyword Arguments
+say_hello(emoji=':)', name='Bibi')
+say_hello()
+say_hello('Timmy')
diff --git a/Notes from classes/2. Python Basics II/Range().py b/Notes from classes/2. Python Basics II/Range().py
new file mode 100644
index 0000000..e1f62da
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Range().py
@@ -0,0 +1,35 @@
+# Range()
+
+print(range(100))
+print(range(0, 100))
+
+for number in range(10):
+ print(number, end=' ')
+print()
+
+for _ in range(10):
+ print('Hi', end=' ')
+print()
+
+for number in range(0, 10, 2):
+ print(number, end=' ')
+print()
+
+for number in range(0, 10, -1):
+ print(number, end=' ')
+print()
+
+for number in range(10, 0, -1):
+ print(number, end=' ')
+print()
+
+for number in range(10, 0):
+ print(number, end=' ')
+print()
+
+for number in range(10, 0, -2):
+ print(number, end=' ')
+print()
+
+for _ in range(2):
+ print(list(range(10)))
diff --git a/Notes from classes/2. Python Basics II/Return.py b/Notes from classes/2. Python Basics II/Return.py
new file mode 100644
index 0000000..bf46233
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Return.py
@@ -0,0 +1,19 @@
+# Return
+
+def sum1(num1, num2):
+ return num1 + num2
+
+
+total = sum1(10, 5)
+print(sum1(10, total))
+print()
+
+
+def sum2(num1, num2):
+ def another_func(n1, n2):
+ return n1 + n2
+ return another_func(num1, num2)
+
+
+total = sum2(10, 20)
+print(total)
diff --git a/Notes from classes/2. Python Basics II/ScopeRules.py b/Notes from classes/2. Python Basics II/ScopeRules.py
new file mode 100644
index 0000000..77e925d
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/ScopeRules.py
@@ -0,0 +1,17 @@
+# Scope Rules
+
+a = 1
+
+
+def parent():
+ a = 10
+
+ def confusion():
+ a = 5
+ return a
+ return confusion()
+
+
+print(a)
+print(parent())
+print(sum)
diff --git a/Notes from classes/2. Python Basics II/TernaryOperator.py b/Notes from classes/2. Python Basics II/TernaryOperator.py
new file mode 100644
index 0000000..f3ca088
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/TernaryOperator.py
@@ -0,0 +1,5 @@
+# Ternary Operator
+
+is_friend = True
+can_message = "message allowed" if is_friend else "not allowed to message"
+print(can_message)
diff --git a/Notes from classes/2. Python Basics II/Walrus.py b/Notes from classes/2. Python Basics II/Walrus.py
new file mode 100644
index 0000000..e9e09cb
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/Walrus.py
@@ -0,0 +1,10 @@
+a = 'helloooooooooo'
+
+if ((n := len(a)) > 10):
+ print(f"too long {n} elements")
+
+while ((n := len(a)) > 1):
+ print(n)
+ a = a[:-1]
+
+print(a)
diff --git a/Notes from classes/2. Python Basics II/WhileLoops.py b/Notes from classes/2. Python Basics II/WhileLoops.py
new file mode 100644
index 0000000..bd877fb
--- /dev/null
+++ b/Notes from classes/2. Python Basics II/WhileLoops.py
@@ -0,0 +1,21 @@
+# While loops
+
+i = 0
+while i < 50:
+ print(i, end='\t')
+ i += 1
+else:
+ print('\nDone with all the work.')
+print()
+
+my_list = [1, 2, 3]
+i = 0
+while i < len(my_list):
+ print(my_list[i])
+ i += 1
+print()
+
+while True:
+ response = input('Say Something: ')
+ if response == 'bye':
+ break
diff --git a/Notes from classes/3. Advanced Python/AttributesAndMethods.py b/Notes from classes/3. Advanced Python/AttributesAndMethods.py
new file mode 100644
index 0000000..e459a2d
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/AttributesAndMethods.py
@@ -0,0 +1,22 @@
+# Attributes And Methods
+
+class PlayerCharacter:
+ # Class Object Attribute
+ membership = True
+
+ def __init__(self, name, age):
+ if self.membership: # Or PLayerCharacter.membership
+ self.name = name
+ self.age = age
+
+ def shout(self):
+ print(f'My name is {self.name}')
+
+ def run(self, hello):
+ print(f'{hello} {self.name}')
+
+
+player1 = PlayerCharacter('Cindy', 44)
+player1.shout()
+player1.run('Hi')
+print(player1.membership, '\t', player1.name, '\t', player1.age)
diff --git a/Notes from classes/3. Advanced Python/ClassmethodAndStaticmethod.py b/Notes from classes/3. Advanced Python/ClassmethodAndStaticmethod.py
new file mode 100644
index 0000000..df8f5f3
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/ClassmethodAndStaticmethod.py
@@ -0,0 +1,19 @@
+# @classmethod and @staticmethod
+
+class PlayerCharacter:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ @classmethod
+ def adding_things(cls, num1, num2):
+ return cls('Teddy', num1 + num2)
+
+ @staticmethod
+ def adding_things2(num1, num2):
+ return num1 + num2
+
+
+player3 = PlayerCharacter.adding_things(2, 3) # Instantiated class using classmethod
+print(player3)
+print(PlayerCharacter.adding_things2(5, 9))
diff --git a/Notes from classes/3. Advanced Python/CreatingOurOwnObjects.py b/Notes from classes/3. Advanced Python/CreatingOurOwnObjects.py
new file mode 100644
index 0000000..13f91c1
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/CreatingOurOwnObjects.py
@@ -0,0 +1,21 @@
+# Creating Our Own Objects
+
+class PlayerCharacter:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ def run(self):
+ print('Run')
+
+
+player1 = PlayerCharacter('Cindy', 44)
+player2 = PlayerCharacter('Tom', 21)
+player2.attack = 50
+
+print(player1.name)
+print(player2.name)
+print(player1.age)
+print(player2.age)
+player1.run()
+print(player2.attack)
diff --git a/Notes from classes/3. Advanced Python/DunderMethods.py b/Notes from classes/3. Advanced Python/DunderMethods.py
new file mode 100644
index 0000000..262d9b1
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/DunderMethods.py
@@ -0,0 +1,46 @@
+# Dunder Methods
+
+class Toy:
+ def __init__(self, color, age):
+ self.color = color
+ self.age = age
+ self.my_dict= {
+ 'name': 'Yoyo',
+ 'has_pets': False
+ }
+
+ def __str__(self):
+ return f'{self.color}'
+
+ def __len__(self):
+ return 5
+
+ # def __del__(self):
+ # print('deleted!')
+
+ def __call__(self):
+ return 'Yess?'
+
+ def __getitem__(self, i):
+ return self.my_dict[i]
+
+ def __abs__(self, num):
+ return num
+
+ def __hex__(self):
+ return 6
+
+ def __set__(self):
+ return 'done setting'
+
+
+action_figure = Toy('red', 0)
+print(action_figure.__str__())
+print(str(action_figure))
+print(len(action_figure))
+# del action_figure
+print(action_figure())
+print(action_figure['name'])
+print(action_figure.__abs__(-50))
+print(action_figure.__hex__())
+print(action_figure.__set__())
diff --git a/Notes from classes/3. Advanced Python/Encapsulation.py b/Notes from classes/3. Advanced Python/Encapsulation.py
new file mode 100644
index 0000000..756831b
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/Encapsulation.py
@@ -0,0 +1,14 @@
+# Encapsulation
+
+class PlayerCharacter:
+ def __init__(self, name, age):
+ if age > 18:
+ self.name = name
+ self.age = age
+
+ def speak(self):
+ print(f'My name is {self.name} and I am {self.age} years old.')
+
+
+player1 = PlayerCharacter('Tom', 20)
+player1.speak()
diff --git a/Notes from classes/3. Advanced Python/Generators.py b/Notes from classes/3. Advanced Python/Generators.py
new file mode 100644
index 0000000..40b82ac
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/Generators.py
@@ -0,0 +1,34 @@
+#generators.
+
+#generators are much more performant than lists. (i.e range > list in performance.)
+
+#So generators are really, really useful when calculating large sets of data.
+
+from time import time
+
+def performance(fn):
+ def wrapper(*args, **kwargs):
+ t1 = time()
+ result = fn(*args, *kwargs)
+ t2 = time()
+ print(f'took {t2-t1} s')
+ return result
+ return wrapper
+
+@performance
+def long_time():
+ print('1')
+ for i in range(1000000): #it finishes after.
+ i*5
+
+long_time()
+print()
+
+@performance
+def long_time2():
+ print('2')
+ for i in list(range(1000000)): #it took longer.
+ i*5
+
+long_time2()
+print()
\ No newline at end of file
diff --git a/Notes from classes/3. Advanced Python/Inheritance.py b/Notes from classes/3. Advanced Python/Inheritance.py
new file mode 100644
index 0000000..5289c44
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/Inheritance.py
@@ -0,0 +1,35 @@
+# Inheritance
+
+# Parent Class
+class User:
+ def sign_in(self):
+ print('logged in')
+
+
+# Sub Class/ Child Class/ Derived Class
+class Wizard(User):
+ def __init__(self, name, power):
+ self.name = name
+ self.power = power
+
+ def attack(self):
+ print(f'Attacking with power of {self.power}')
+
+
+class Archer(User):
+ def __init__(self, name, num_arrows):
+ self.name = name
+ self.num_arrows = num_arrows
+
+ def attack(self):
+ print(f'Attacking with arrows: Arrows left- {self.num_arrows}')
+
+
+wizard1 = Wizard('Merlin', 50)
+archer1 = Archer('Robbin', 100)
+wizard1.attack()
+archer1.attack()
+
+print(isinstance(wizard1, Wizard))
+print(isinstance(wizard1, User))
+print(isinstance(wizard1, object))
diff --git a/Notes from classes/3. Advanced Python/Init.py b/Notes from classes/3. Advanced Python/Init.py
new file mode 100644
index 0000000..74565aa
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/Init.py
@@ -0,0 +1,17 @@
+# __init__
+
+class PlayerCharacter:
+ # Class Object Attribute
+ membership = True
+
+ def __init__(self, name, age):
+ if age > 18:
+ self.name = name
+ self.age = age
+
+ def shout(self):
+ print(f'My name is {self.name}')
+
+
+player1 = PlayerCharacter('Tom', 20)
+print(player1.shout())
diff --git a/Notes from classes/3. Advanced Python/MroMethodResolutionOrder.py b/Notes from classes/3. Advanced Python/MroMethodResolutionOrder.py
new file mode 100644
index 0000000..b206474
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/MroMethodResolutionOrder.py
@@ -0,0 +1,47 @@
+# MRO - Method Resolution Order
+
+class A:
+ num = 10
+
+
+class B(A):
+ pass
+
+
+class C(A):
+ num = 1
+
+
+class D(B, C):
+ pass
+
+
+print(D.num)
+print(D.mro())
+
+
+class X:
+ pass
+
+
+class Y:
+ pass
+
+
+class Z:
+ pass
+
+
+class A(X, Y):
+ pass
+
+
+class B(Y, Z):
+ pass
+
+
+class M(B, A, Z):
+ pass
+
+
+print(M.mro())
diff --git a/Notes from classes/3. Advanced Python/MultipleInheritance.py b/Notes from classes/3. Advanced Python/MultipleInheritance.py
new file mode 100644
index 0000000..bdc7930
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/MultipleInheritance.py
@@ -0,0 +1,38 @@
+# Multiple Inheritance
+
+class User:
+ def sign_in(self):
+ print('logged in')
+
+
+class Wizard(User):
+ def __init__(self, name, power):
+ self.name = name
+ self.power = power
+
+ def attack(self):
+ print(f'Attacking with power of {self.power}')
+
+
+class Archer(User):
+ def __init__(self, name, num_arrows):
+ self.name = name
+ self.num_arrows = num_arrows
+
+ def check_arrows(self):
+ print(f'{self.num_arrows} remaining')
+
+ def run(self):
+ print('Ran really fast.')
+
+
+class HybridBorg(Wizard, Archer):
+ def __init__(self, name, power, arrows):
+ Archer.__init__(self, name, arrows)
+ Wizard.__init__(self, name, power)
+
+
+hb1 = HybridBorg('Borgie', 50, 100)
+print(hb1.run())
+print(hb1.check_arrows())
+hb1.attack()
diff --git a/Notes from classes/3. Advanced Python/OOP.py b/Notes from classes/3. Advanced Python/OOP.py
new file mode 100644
index 0000000..c919ad3
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/OOP.py
@@ -0,0 +1,19 @@
+# OOP
+
+class BigObject:
+ pass
+
+
+obj1 = BigObject()
+obj2 = BigObject()
+obj3 = BigObject()
+
+print(type(None))
+print(type(True))
+print(type(5))
+print(type(5.5))
+print(type('hi'))
+print(type([]))
+print(type(()))
+print(type({}))
+print(type(obj1))
diff --git a/Notes from classes/3. Advanced Python/ObjectIntrospection.py b/Notes from classes/3. Advanced Python/ObjectIntrospection.py
new file mode 100644
index 0000000..6f8adf7
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/ObjectIntrospection.py
@@ -0,0 +1,24 @@
+# Object Introspection
+
+class User:
+ def __init__(self, email):
+ self.email = email
+
+ def sign_in(self):
+ print('logged in')
+
+
+# Sub Class/ Child Class/ Derived Class
+class Wizard(User):
+ def __init__(self, name, power, email):
+ super().__init__(email)
+ # same as:: User.__init__(self, email)
+ self.name = name
+ self.power = power
+
+ def attack(self):
+ print(f'Attacking with power of {self.power}')
+
+
+wizard1 = Wizard('Merlin', 60, 'merlin@gmail.com')
+print(dir(wizard1))
diff --git a/Notes from classes/3. Advanced Python/Polymorphism.py b/Notes from classes/3. Advanced Python/Polymorphism.py
new file mode 100644
index 0000000..7a49607
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/Polymorphism.py
@@ -0,0 +1,43 @@
+# Polymorphism
+
+# Parent Class
+class User:
+ def sign_in(self):
+ print('logged in')
+
+ def attack(self):
+ print('Do nothing.')
+
+
+# Sub Class/ Child Class/ Derived Class
+class Wizard(User):
+ def __init__(self, name, power):
+ self.name = name
+ self.power = power
+
+ def attack(self):
+ print(f'Attacking with power of {self.power}')
+
+
+class Archer(User):
+ def __init__(self, name, num_arrows):
+ self.name = name
+ self.num_arrows = num_arrows
+
+ def attack(self):
+ print(f'Attacking with arrows: Arrows left- {self.num_arrows}')
+
+
+wizard1 = Wizard('Merlin', 50)
+archer1 = Archer('Robbin', 30)
+
+
+def player_attack(char_):
+ char_.attack()
+
+
+player_attack(wizard1)
+player_attack(archer1)
+
+for char in [wizard1, archer1]:
+ char.attack()
diff --git a/Notes from classes/3. Advanced Python/PrivateVsPublicVariables.py b/Notes from classes/3. Advanced Python/PrivateVsPublicVariables.py
new file mode 100644
index 0000000..769045a
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/PrivateVsPublicVariables.py
@@ -0,0 +1,14 @@
+# Private Vs Public Variables
+
+class PlayerCharacter:
+ def __init__(self, name, age):
+ if age > 18:
+ self._name = name
+ self._age = age
+
+ def speak(self):
+ print(f'My name is {self._name} and I am {self._age} years old.')
+
+
+player1 = PlayerCharacter('Tom', 20)
+player1.speak()
diff --git a/Notes from classes/3. Advanced Python/UndertheHoodGenerators.py b/Notes from classes/3. Advanced Python/UndertheHoodGenerators.py
new file mode 100644
index 0000000..f1f2d03
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/UndertheHoodGenerators.py
@@ -0,0 +1,30 @@
+def special_for(iterable):
+ iterator = iter(iterable)
+ while True:
+ try:
+ iterator*5
+ next(iterator)
+ except StopIteration:
+ break
+
+
+class MyGen:
+ current = 0
+ def __init__(self, first, last):
+ self.first = first
+ self.last = last
+ MyGen.current = self.first #this line allows us to use the current number as the starting point for the iteration
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if MyGen.current < self.last:
+ num = MyGen.current
+ MyGen.current += 1
+ return num
+ raise StopIteration
+
+gen = MyGen(1,100)
+for i in gen:
+ print(i)
\ No newline at end of file
diff --git a/Notes from classes/3. Advanced Python/super().py b/Notes from classes/3. Advanced Python/super().py
new file mode 100644
index 0000000..a73c9c6
--- /dev/null
+++ b/Notes from classes/3. Advanced Python/super().py
@@ -0,0 +1,24 @@
+# super()
+
+class User:
+ def __init__(self, email):
+ self.email = email
+
+ def sign_in(self):
+ print('logged in')
+
+
+# Sub Class/ Child Class/ Derived Class
+class Wizard(User):
+ def __init__(self, name, power, email):
+ super().__init__(email)
+ # same as:: User.__init__(self, email)
+ self.name = name
+ self.power = power
+
+ def attack(self):
+ print(f'Attacking with power of {self.power}')
+
+
+wizard1 = Wizard('Merlin', 60, 'merlin@gmail.com')
+print(wizard1.email)
diff --git a/course-info.yaml b/course-info.yaml
new file mode 100644
index 0000000..1952306
--- /dev/null
+++ b/course-info.yaml
@@ -0,0 +1,97 @@
+type: marketplace
+title: Zero To Mastery Python Course
+language: English
+summary: Learn Python fundamentals through hands-on exercises from the Complete Python
+ Developer course
+vendor:
+ name: Zero To Mastery
+programming_language: Python
+programming_language_version: 3.8
+environment: unittest
+content:
+ - L01
+ - L02
+additional_files:
+ - name: Notes from classes/1. Python Basics/Dictionaries.py
+ visible: true
+ - name: Notes from classes/1. Python Basics/Int&Float.py
+ visible: true
+ - name: Notes from classes/1. Python Basics/Lists.py
+ visible: true
+ - name: Notes from classes/1. Python Basics/Sets.py
+ visible: true
+ - name: Notes from classes/1. Python Basics/String.py
+ visible: true
+ - name: Notes from classes/1. Python Basics/Tuples.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/ArgsAndKwargs.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/BreakContinuePass.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/CleanCode.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/ConditionalLogic.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Docstrings.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Enumerate().py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/ForLoops.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Functions.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/GlobalKeyword.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/IsVs==.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Iterables.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/NonLocalKeyword.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/ParametersAndArguments.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Range().py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Return.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/ScopeRules.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/TernaryOperator.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/Walrus.py
+ visible: true
+ - name: Notes from classes/2. Python Basics II/WhileLoops.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/AttributesAndMethods.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/ClassmethodAndStaticmethod.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/CreatingOurOwnObjects.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/DunderMethods.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/Encapsulation.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/Generators.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/Inheritance.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/Init.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/MroMethodResolutionOrder.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/MultipleInheritance.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/ObjectIntrospection.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/OOP.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/Polymorphism.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/PrivateVsPublicVariables.py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/super().py
+ visible: true
+ - name: Notes from classes/3. Advanced Python/UndertheHoodGenerators.py
+ visible: true
+yaml_version: 5