fix(curriculum): update Python V9 setter examples to call setter in __init__ (#66233)

Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
This commit is contained in:
Jeevankumar S
2026-04-17 14:32:14 +05:30
committed by GitHub
parent 1d4a7b13c1
commit a0326c1210
23 changed files with 651 additions and 203 deletions
@@ -2279,7 +2279,7 @@ print(account.__balance) # AttributeError: 'Wallet' object has no attribute '__b
```py
class Circle:
def __init__(self, radius):
self._radius = radius
self.radius = radius # Calling the setter
@property
def radius(self): # A getter to get the radius
@@ -2295,12 +2295,14 @@ print(my_circle.radius) # 3
print(my_circle.area) # 28.26
```
Using self.radius inside `__init__()` ensures the setter runs during object creation, so invalid radius values are caught immediately.
- **Creating a Setter**: To create the setter that will set the radius, you have to define another method with the same name and use `@<property_name>.setter` above it:
```py
class Circle:
def __init__(self, radius):
self._radius = radius
self.radius = radius # Calling the setter
@property
def radius(self): # A getter to get the radius
@@ -2326,7 +2328,7 @@ my_circle.radius # This will call the getter
my_circle.radius = 4 # This will call the setter
```
When setting a value, you should not assign to the property name itself because that will cause a `RecursionError`. Use a separate internal name, often with an underscore, to store the value.
Inside the setter, you should not assign to the property name itself because that will cause a `RecursionError`.
- **Deleter**: After setting and getting a value with setter and getter, you can control how it is deleted with a `deleter`. A deleter runs custom logic when you use the `del` statement on a property. To create a deleter, you use the `@<property_name>.deleter` decorator.
@@ -12,7 +12,7 @@ A setter is a method used to set the value of an attribute, allowing for validat
```py
class Person:
def __init__(self, name):
self._name = name
self.name = name # Calling the setter
@property
def name(self):
@@ -76,14 +76,16 @@ class Employee:
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
--fcc-editable-region--
@property
def name(self):
return self._name
--fcc-editable-region--
@property
--fcc-editable-region--
--fcc-editable-region--
@property
def level(self):
return self._level
@@ -1,8 +1,8 @@
---
id: 68c809c990f253912a9e9209
title: Step 21
title: Step 22
challengeType: 20
dashedName: step-21
dashedName: step-22
---
# --description--
@@ -60,11 +60,11 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
@@ -77,12 +77,14 @@ class Employee:
@property
def name(self):
return self._name
--fcc-editable-region--
@name.setter
def name(self, new_name):
self._name = new_name
--fcc-editable-region--
--fcc-editable-region--
self._name = new_name
@property
def level(self):
return self._level
@@ -1,8 +1,8 @@
---
id: 68c80b63ca4e5eadf1fac738
title: Step 22
title: Step 23
challengeType: 20
dashedName: step-22
dashedName: step-23
---
# --description--
@@ -45,11 +45,11 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
@@ -62,14 +62,16 @@ class Employee:
@property
def name(self):
return self._name
--fcc-editable-region--
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError("'name' must be a string.")
self._name = new_name
--fcc-editable-region--
--fcc-editable-region--
@property
def level(self):
return self._level
@@ -1,8 +1,8 @@
---
id: 68c9a09163d4a42e9bc9b638
title: Step 23
title: Step 24
challengeType: 20
dashedName: step-23
dashedName: step-24
---
# --description--
@@ -36,11 +36,11 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
@@ -1,8 +1,8 @@
---
id: 68c9a624fd54288f694ebdbf
title: Step 24
title: Step 25
challengeType: 20
dashedName: step-24
dashedName: step-25
---
# --description--
@@ -33,11 +33,11 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
@@ -1,8 +1,8 @@
---
id: 68c9a72c9dab42a15ce6972b
title: Step 25
title: Step 26
challengeType: 20
dashedName: step-25
dashedName: step-26
---
# --description--
@@ -45,11 +45,11 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
@@ -69,14 +69,16 @@ class Employee:
raise TypeError("'name' must be a string.")
self._name = new_name
print(f"'name' updated to '{self.name}'.")
--fcc-editable-region--
@property
def level(self):
return self._level
--fcc-editable-region--
--fcc-editable-region--
@property
def salary(self):
return self._salary
@@ -1,13 +1,13 @@
---
id: 68c9c3aa714bc326e026b826
title: Step 26
title: Step 30
challengeType: 20
dashedName: step-26
dashedName: step-30
---
# --description--
The new level cannot be set without checking if it's a valid level. At the beginning of your setter, create an `if` statement that raises a `ValueError` when `new_level` is not a key of `Employee._base_salaries`.
The new level cannot be set without checking if it's a valid level. After the `isinstance` check, create an `if` statement that raises a `ValueError` when `new_level` is not a key of `Employee._base_salaries`.
For the error message, use `Invalid value '{new_level}' for 'level' attribute.`, where `{new_level}` should be replaced by the argument passed to the setter.
@@ -16,7 +16,7 @@ For the error message, use `Invalid value '{new_level}' for 'level' attribute.`,
You should have an `if` statement inside your `level` setter.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[0]`)) })
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[1]`)) })
```
When `new_level` is not a key of `Employee._base_salaries`, you should raise a `ValueError` with the message `Invalid value '{new_level}' for 'level' attribute.`, where `{new_level}` should be replaced by the argument passed to the setter.
@@ -60,12 +60,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -88,13 +84,15 @@ class Employee:
@property
def level(self):
return self._level
--fcc-editable-region--
@level.setter
def level(self, new_level):
self._level = new_level
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
--fcc-editable-region--
--fcc-editable-region--
self._level = new_level
@property
def salary(self):
return self._salary
@@ -1,22 +1,24 @@
---
id: 68c9cab4b1118da59eecfc56
title: Step 27
title: Step 31
challengeType: 20
dashedName: step-27
dashedName: step-31
---
# --description--
After the existing `if` statement, create another one to raise a `ValueError` when `new_level` is already the selected level.
After the existing `if` statements, create another one to raise a `ValueError` when `new_level` is already the selected level.
Note that `_level` doesn't exist yet during initialization, so use `hasattr(self, '_level')` to check if it exists before comparing. This avoids an `AttributeError` when the object is first created.
For the message, use `'{level}' is already the selected level.`, where `{level}` should be replaced by the current level.
# --hints--
You should have a second `if` statement inside your `level` setter.
You should have a third `if` statement inside your `level` setter.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[1]`)) })
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[2]`)) })
```
When `new_level` is equal to `self.level`, you should raise a `ValueError` with the message `'{level}' is already the selected level.`, where `{level}` should be replaced by the current level.
@@ -33,6 +35,12 @@ When `new_level` is equal to `self.level`, you should raise a `ValueError` with
`) })
```
Your `if` statement should use `hasattr(self, '_level')` to check if `_level` exists before comparing.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[2].find_conditions()[0].is_equivalent("hasattr(self, '_level') and new_level == self.level")`)) })
```
# --seed--
## --seed-contents--
@@ -47,12 +55,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -75,16 +79,19 @@ class Employee:
@property
def level(self):
return self._level
--fcc-editable-region--
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
--fcc-editable-region--
--fcc-editable-region--
self._level = new_level
--fcc-editable-region--
@property
def salary(self):
return self._salary
@@ -1,20 +1,28 @@
---
id: 68ca758f8160b11757f877ae
title: Step 28
title: Step 32
challengeType: 20
dashedName: step-28
dashedName: step-32
---
# --description--
Finally, create a third `if` statement that raises a `ValueError` with the message `Cannot change to lower level.` when the base salary of the new level is less than the base salary of the current level.
Finally, create a fourth `if` statement that raises a `ValueError` with the message `Cannot change to lower level.` when the base salary of the new level is less than the base salary of the current level.
Use `hasattr(self, '_level')` to avoid an `AttributeError` during initialization.
# --hints--
You should have a third `if` statement inside your `level` setter.
You should have a fourth `if` statement inside your `level` setter.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[2]`)) })
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[3]`)) })
```
Your `if` statement should use `hasattr(self, '_level')` to check if `_level` exists before comparing.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[3].find_conditions()[0].is_equivalent("hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]")`)) })
```
When `new_level` is lower than `self.level`, you should raise a `ValueError` with the message `Cannot change to lower level.`
@@ -57,12 +65,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -85,17 +89,20 @@ class Employee:
@property
def level(self):
return self._level
--fcc-editable-region--
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
--fcc-editable-region--
--fcc-editable-region--
self._level = new_level
--fcc-editable-region--
@property
def salary(self):
return self._salary
@@ -1,8 +1,8 @@
---
id: 68caa8fb3bed34833ef24aee
title: Step 29
title: Step 33
challengeType: 20
dashedName: step-29
dashedName: step-33
---
# --description--
@@ -17,9 +17,9 @@ You should set `self._salary` to the base salary for the new level.
```js
({ test: () => runPython(`
emp = Employee('Frank', 'trainee')
new_levels = ['junior', 'mid-level', 'senior']
for new_level in new_levels:
emp = Employee('Frank', 'trainee')
new_levels = ['junior', 'mid-level', 'senior']
for new_level in new_levels:
emp.level = new_level
assert emp.salary == Employee._base_salaries.get(new_level)
`) })
@@ -39,12 +39,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -70,16 +66,19 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
--fcc-editable-region--
self._level = new_level
--fcc-editable-region--
self._level = new_level
@property
def salary(self):
return self._salary
@@ -1,8 +1,8 @@
---
id: 68caa9fb6f1602975e41b051
title: Step 30
title: Step 34
challengeType: 20
dashedName: step-30
dashedName: step-34
---
# --description--
@@ -45,12 +45,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -76,17 +72,20 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
--fcc-editable-region--
--fcc-editable-region--
self._salary = Employee._base_salaries[new_level]
self._level = new_level
--fcc-editable-region--
@property
def salary(self):
return self._salary
@@ -1,8 +1,8 @@
---
id: 68caaaef4afb18aab8a684d4
title: Step 31
title: Step 35
challengeType: 20
dashedName: step-31
dashedName: step-35
---
# --description--
@@ -33,12 +33,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -64,12 +60,14 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self._salary = Employee._base_salaries[new_level]
self._level = new_level
@@ -1,8 +1,8 @@
---
id: 68caacb0f4311cc9be9a2132
title: Step 32
title: Step 36
challengeType: 20
dashedName: step-32
dashedName: step-36
---
# --description--
@@ -66,12 +66,8 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
@@ -97,20 +93,23 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self._salary = Employee._base_salaries[new_level]
self._level = new_level
--fcc-editable-region--
@property
def salary(self):
return self._salary
--fcc-editable-region--
--fcc-editable-region--
@@ -1,8 +1,8 @@
---
id: 68caaf1e590806f0b87f5922
title: Step 33
title: Step 38
challengeType: 20
dashedName: step-33
dashedName: step-38
---
# --description--
@@ -66,13 +66,9 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self._salary = Employee._base_salaries[level]
self.name = name
self.level = level
self.salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
@@ -97,12 +93,14 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self._salary = Employee._base_salaries[new_level]
self._level = new_level
@@ -110,18 +108,18 @@ class Employee:
@property
def salary(self):
return self._salary
--fcc-editable-region--
@salary.setter
def salary(self, new_salary):
--fcc-editable-region--
--fcc-editable-region--
self._salary = new_salary
print(f'Salary updated to ${self.salary}.')
--fcc-editable-region--
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
charlie_brown.level = 'junior'
```
@@ -1,13 +1,13 @@
---
id: 68cab02fd80a91042c0165b8
title: Step 34
title: Step 39
challengeType: 20
dashedName: step-34
dashedName: step-39
---
# --description--
After your existing `if` statement, create another one for when the new salary is less than the base salary for the current level.
After your existing `if` statement, create another one for when the new salary is less than the base salary for the current level. Use `hasattr(self, '_level')` to avoid an `AttributeError` during initialization.
Inside the `if` statement, raise a `ValueError` with the message `Salary must be higher than minimum salary $` followed by the base salary for the current level and a period.
@@ -19,6 +19,12 @@ You should have a second `if` statement in your `salary` setter.
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("salary")[1].find_ifs()[1]`)) })
```
Your `if` statement should use `hasattr(self, '_level')` to check if `_level` exists before comparing.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("salary")[1].find_ifs()[1].find_conditions()[0].is_equivalent("hasattr(self, '_level') and new_salary < Employee._base_salaries[self.level]")`)) })
```
When the new salary is less than the base salary for the current level, you should raise a `ValueError` with the message `Salary must be higher than minimum salary $` followed by the base salary for the current level and a period.
```js
@@ -70,13 +76,9 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self._salary = Employee._base_salaries[level]
self.name = name
self.level = level
self.salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
@@ -101,34 +103,35 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self._salary = Employee._base_salaries[new_level]
self.salary = Employee._base_salaries[new_level]
self._level = new_level
@property
def salary(self):
return self._salary
--fcc-editable-region--
@salary.setter
def salary(self, new_salary):
if not isinstance(new_salary, (int, float)):
raise TypeError("'salary' must be a number.")
--fcc-editable-region--
--fcc-editable-region--
self._salary = new_salary
print(f'Salary updated to ${self.salary}.')
--fcc-editable-region--
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
charlie_brown.level = 'junior'
```
@@ -1,8 +1,8 @@
---
id: 68cab17ac51b861a8458c42d
title: Step 35
title: Step 40
challengeType: 20
dashedName: step-35
dashedName: step-40
---
# --description--
@@ -50,13 +50,9 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self._salary = Employee._base_salaries[level]
self.name = name
self.level = level
self.salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
@@ -81,12 +77,14 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
--fcc-editable-region--
self._salary = Employee._base_salaries[new_level]
@@ -101,12 +99,11 @@ class Employee:
def salary(self, new_salary):
if not isinstance(new_salary, (int, float)):
raise TypeError("'salary' must be a number.")
if new_salary <= Employee._base_salaries[self.level]:
if hasattr(self, '_level') and new_salary < Employee._base_salaries[self.level]:
raise ValueError(f'Salary must be higher than minimum salary ${Employee._base_salaries[self.level]}.')
self._salary = new_salary
print(f'Salary updated to ${self.salary}.')
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
@@ -125,13 +122,9 @@ class Employee:
}
def __init__(self, name, level):
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self._salary = Employee._base_salaries[level]
self.name = name
self.level = level
self.salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
@@ -156,12 +149,14 @@ class Employee:
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if new_level == self.level:
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError(f"Cannot change to lower level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self.salary = Employee._base_salaries[new_level]
self._level = new_level
@@ -174,7 +169,7 @@ class Employee:
def salary(self, new_salary):
if not isinstance(new_salary, (int, float)):
raise TypeError("'salary' must be a number.")
if new_salary <= Employee._base_salaries[self.level]:
if hasattr(self, '_level') and new_salary < Employee._base_salaries[self.level]:
raise ValueError(f'Salary must be higher than minimum salary ${Employee._base_salaries[self.level]}.')
self._salary = new_salary
print(f'Salary updated to ${self.salary}.')
@@ -0,0 +1,77 @@
---
id: 69b014adbc624861e6ff3dd1
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
Now that the `name` setter is created, update `__init__` to use `self.name = name` instead of `self._name = name`. This delegates `name` assignment through the setter, and validation will be added to it in the next step.
Also, adjust the `if` condition to only check `level`, and update the `TypeError` message accordingly:
# --hints--
Your `__init__` method should use `self.name = name` instead of `self._name = name`.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self.name = name")`)) })
```
Your `__init__` method should only check `isinstance(level, str)` and update the `TypeError` message accordingly.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").find_if("not isinstance(level, str)").find_body().has_stmt("raise TypeError(\\"'level' attribute must be of type 'str'.\\")")`)) })
```
# --seed--
## --seed-contents--
```py
class Employee:
_base_salaries = {
'trainee': 1000,
'junior': 2000,
'mid-level': 3000,
'senior': 4000,
}
def __init__(self, name, level):
--fcc-editable-region--
if not (isinstance(name, str) and isinstance(level, str)):
raise TypeError("'name' and 'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self._name = name
self._level = level
self._salary = Employee._base_salaries[level]
--fcc-editable-region--
def __str__(self):
return f'{self.name}: {self.level}'
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
self._name = new_name
@property
def level(self):
return self._level
@property
def salary(self):
return self._salary
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
```
@@ -0,0 +1,84 @@
---
id: 69b023541591abde576613db
title: Step 27
challengeType: 20
dashedName: step-27
---
# --description--
Now that the `level` setter is created, update `__init__` to use `self.level = level` instead of `self._level = level`. This delegates `level` assignment through the setter, and validation will be added to it in the next step.
Also, remove the `if not isinstance(level, str)` check from `__init__` since the setter will handle that validation in the next step.
# --hints--
Your `__init__` method should use `self.level = level` instead of `self._level = level`.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self.level = level")`)) })
```
Your `__init__` method should not contain the `isinstance(level, str)` check.
```js
({ test: () => assert(runPython(`not _Node(_code).find_class("Employee").find_function("__init__").find_if("not isinstance(level, str)").find_body().has_stmt("raise TypeError(\\"'level' attribute must be of type 'str'.\\")")`)) })
```
# --seed--
## --seed-contents--
```py
class Employee:
_base_salaries = {
'trainee': 1000,
'junior': 2000,
'mid-level': 3000,
'senior': 4000,
}
def __init__(self, name, level):
--fcc-editable-region--
if not isinstance(level, str):
raise TypeError("'level' attribute must be of type 'str'.")
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self.name = name
self._level = level
self._salary = Employee._base_salaries[level]
--fcc-editable-region--
def __str__(self):
return f'{self.name}: {self.level}'
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError("'name' must be a string.")
self._name = new_name
print(f"'name' updated to '{self.name}'.")
@property
def level(self):
return self._level
@level.setter
def level(self, new_level):
self._level = new_level
@property
def salary(self):
return self._salary
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
```
@@ -0,0 +1,103 @@
---
id: 69c0dc6914ac0434f0cc52b7
title: Step 28
challengeType: 20
dashedName: step-28
---
# --description--
To ensure that `new_level` is the right type, create an `if` statement that raises a `TypeError` with the message `'level' must be a string.` when `new_level` is not an instance of `str`.
# --hints--
You should have an `if` statement inside your `level` setter.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[0]`)) })
```
You should raise a `TypeError` with the message `'level' must be a string.` when `new_level` is not an instance of `str`.
```js
({ test: () => runPython(`
emp = Employee('Frank', 'trainee')
non_levels = [0, True, []]
for i in non_levels:
try:
emp.level = i
except TypeError as e:
assert str(e) == "'level' must be a string."
else:
assert False, "Expected to raise TypeError with non-string new_level"
`) })
```
You should not raise any exception when `new_level` is a string.
```js
({ test: () => runPython(`
emp = Employee('Frank', 'trainee')
try:
emp.level = "junior"
except TypeError:
assert False, "Expected not to raise TypeError with string new_level"
`) })
```
# --seed--
## --seed-contents--
```py
class Employee:
_base_salaries = {
'trainee': 1000,
'junior': 2000,
'mid-level': 3000,
'senior': 4000,
}
def __init__(self, name, level):
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError("'name' must be a string.")
self._name = new_name
print(f"'name' updated to '{self.name}'.")
@property
def level(self):
return self._level
@level.setter
def level(self, new_level):
--fcc-editable-region--
--fcc-editable-region--
self._level = new_level
@property
def salary(self):
return self._salary
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
```
@@ -0,0 +1,76 @@
---
id: 69c0e73cc6f2cf16a453c69e
title: Step 29
challengeType: 20
dashedName: step-29
---
# --description--
Now that the `level` setter handles the validation for invalid levels, remove the `if level not in Employee._base_salaries` check from `__init__`. This will be handled by the setter in the next step.
# --hints--
Your `__init__` method should not contain the `level not in Employee._base_salaries` check.
```js
({ test: () => assert(runPython(`not _Node(_code).find_class("Employee").find_function("__init__").find_if("level not in Employee._base_salaries").find_body().has_stmt("raise ValueError(f\\"Invalid value '{level}' for 'level' attribute.\\")")`)) })
```
# --seed--
## --seed-contents--
```py
class Employee:
_base_salaries = {
'trainee': 1000,
'junior': 2000,
'mid-level': 3000,
'senior': 4000,
}
def __init__(self, name, level):
--fcc-editable-region--
if level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{level}' for 'level' attribute.")
--fcc-editable-region--
self.name = name
self.level = level
self._salary = Employee._base_salaries[level]
def __str__(self):
return f'{self.name}: {self.level}'
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError("'name' must be a string.")
self._name = new_name
print(f"'name' updated to '{self.name}'.")
@property
def level(self):
return self._level
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
self._level = new_level
@property
def salary(self):
return self._salary
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
```
@@ -0,0 +1,90 @@
---
id: 69c2293ea0ecd3718984451b
title: Step 37
challengeType: 20
dashedName: step-37
---
# --description--
Now that the `salary` setter is created, update `__init__` to call it instead of assigning directly to `self._salary`. Change `self._salary = Employee._base_salaries[level]` to `self.salary = Employee._base_salaries[level]`.
# --hints--
Your `__init__` method should use `self.salary = Employee._base_salaries[level]` instead of `self._salary = Employee._base_salaries[level]`.
```js
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self.salary = Employee._base_salaries[level]")`)) })
```
# --seed--
## --seed-contents--
```py
class Employee:
_base_salaries = {
'trainee': 1000,
'junior': 2000,
'mid-level': 3000,
'senior': 4000,
}
def __init__(self, name, level):
self.name = name
self.level = level
--fcc-editable-region--
self._salary = Employee._base_salaries[level]
--fcc-editable-region--
def __str__(self):
return f'{self.name}: {self.level}'
def __repr__(self):
return f"Employee('{self.name}', '{self.level}')"
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if not isinstance(new_name, str):
raise TypeError("'name' must be a string.")
self._name = new_name
print(f"'name' updated to '{self.name}'.")
@property
def level(self):
return self._level
@level.setter
def level(self, new_level):
if not isinstance(new_level, str):
raise TypeError("'level' must be a string.")
if new_level not in Employee._base_salaries:
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
if hasattr(self, '_level') and new_level == self.level:
raise ValueError(f"'{self.level}' is already the selected level.")
if hasattr(self, '_level') and Employee._base_salaries[new_level] < Employee._base_salaries[self.level]:
raise ValueError("Cannot change to lower level.")
print(f"'{self.name}' promoted to '{new_level}'.")
self._salary = Employee._base_salaries[new_level]
self._level = new_level
@property
def salary(self):
return self._salary
@salary.setter
def salary(self, new_salary):
self._salary = new_salary
print(f'Salary updated to ${self.salary}.')
charlie_brown = Employee('Charlie Brown', 'trainee')
print(charlie_brown)
print(f'Base salary: ${charlie_brown.salary}')
charlie_brown.level = 'junior'
```
@@ -25,21 +25,26 @@
{ "id": "68c9a1c241206043c81293d5", "title": "Step 18" },
{ "id": "68c9a2c015ed7b54cf81e789", "title": "Step 19" },
{ "id": "68c8002beb157fe47d2e7699", "title": "Step 20" },
{ "id": "68c809c990f253912a9e9209", "title": "Step 21" },
{ "id": "68c80b63ca4e5eadf1fac738", "title": "Step 22" },
{ "id": "68c9a09163d4a42e9bc9b638", "title": "Step 23" },
{ "id": "68c9a624fd54288f694ebdbf", "title": "Step 24" },
{ "id": "68c9a72c9dab42a15ce6972b", "title": "Step 25" },
{ "id": "68c9c3aa714bc326e026b826", "title": "Step 26" },
{ "id": "68c9cab4b1118da59eecfc56", "title": "Step 27" },
{ "id": "68ca758f8160b11757f877ae", "title": "Step 28" },
{ "id": "68caa8fb3bed34833ef24aee", "title": "Step 29" },
{ "id": "68caa9fb6f1602975e41b051", "title": "Step 30" },
{ "id": "68caaaef4afb18aab8a684d4", "title": "Step 31" },
{ "id": "68caacb0f4311cc9be9a2132", "title": "Step 32" },
{ "id": "68caaf1e590806f0b87f5922", "title": "Step 33" },
{ "id": "68cab02fd80a91042c0165b8", "title": "Step 34" },
{ "id": "68cab17ac51b861a8458c42d", "title": "Step 35" }
{ "id": "69b014adbc624861e6ff3dd1", "title": "Step 21" },
{ "id": "68c809c990f253912a9e9209", "title": "Step 22" },
{ "id": "68c80b63ca4e5eadf1fac738", "title": "Step 23" },
{ "id": "68c9a09163d4a42e9bc9b638", "title": "Step 24" },
{ "id": "68c9a624fd54288f694ebdbf", "title": "Step 25" },
{ "id": "68c9a72c9dab42a15ce6972b", "title": "Step 26" },
{ "id": "69b023541591abde576613db", "title": "Step 27" },
{ "id": "69c0dc6914ac0434f0cc52b7", "title": "Step 28" },
{ "id": "69c0e73cc6f2cf16a453c69e", "title": "Step 29" },
{ "id": "68c9c3aa714bc326e026b826", "title": "Step 30" },
{ "id": "68c9cab4b1118da59eecfc56", "title": "Step 31" },
{ "id": "68ca758f8160b11757f877ae", "title": "Step 32" },
{ "id": "68caa8fb3bed34833ef24aee", "title": "Step 33" },
{ "id": "68caa9fb6f1602975e41b051", "title": "Step 34" },
{ "id": "68caaaef4afb18aab8a684d4", "title": "Step 35" },
{ "id": "68caacb0f4311cc9be9a2132", "title": "Step 36" },
{ "id": "69c2293ea0ecd3718984451b", "title": "Step 37" },
{ "id": "68caaf1e590806f0b87f5922", "title": "Step 38" },
{ "id": "68cab02fd80a91042c0165b8", "title": "Step 39" },
{ "id": "68cab17ac51b861a8458c42d", "title": "Step 40" }
],
"usesMultifileEditor": true,
"hasEditableBoundaries": true