mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(curriculum): add salary tracker workshop (#62253)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com>
This commit is contained in:
@@ -4569,9 +4569,11 @@
|
||||
"Learn about Understanding Object Oriented Programming and Encapsulation in these lessons."
|
||||
]
|
||||
},
|
||||
"workshop-placeholder-oop-1": {
|
||||
"title": "Placeholder - Waiting for title",
|
||||
"intro": [""]
|
||||
"workshop-salary-tracker": {
|
||||
"title": "Build a Salary Tracker",
|
||||
"intro": [
|
||||
"In this workshop, you'll practice encapsulation, properties, and other OOP concepts by building a salary tracking system for employees."
|
||||
]
|
||||
},
|
||||
"lab-placeholder-oop-1": {
|
||||
"title": "Placeholder - Waiting for title",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Introduction to the Build a Salary Tracker
|
||||
block: workshop-salary-tracker
|
||||
superBlock: full-stack-developer
|
||||
---
|
||||
|
||||
## Introduction to the Build a Salary Tracker
|
||||
|
||||
In this workshop, you'll practice encapsulation, properties, and other OOP concepts by building a salary tracking system for employees.
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: 68c15a2a1f0e70ca7154aba5
|
||||
title: Step 1
|
||||
challengeType: 20
|
||||
dashedName: step-1
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this workshop, you are going to build a salary tracking system for employees.
|
||||
|
||||
Start by creating a class named `Employee`. Inside it create an `__init__` method with `self`, `name`, and `level` parameters. Within the `__init__` method, assign `name` and `level` to the instance attributes with the same name.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a class named `Employee`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_class("Employee")`)) })
|
||||
```
|
||||
|
||||
Your `Employee` class should have an `__init__` method with three parameters `self`, `name`, and `level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_args("self, name, level")`)) })
|
||||
```
|
||||
|
||||
Your `__init__` method should set `self.name` to `name`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self.name = name")`)) })
|
||||
```
|
||||
|
||||
Your `__init__` method should set `self.level` to `level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self.level = level")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
---
|
||||
id: 68c16924498728d259e4781c
|
||||
title: Step 2
|
||||
challengeType: 20
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now create an instance of the `Employee` class passing in the strings `Charlie Brown` and `trainee`. Assign the instance to a variable named `charlie_brown`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a variable named `charlie_brown`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_variable("charlie_brown")`)) })
|
||||
```
|
||||
|
||||
You should assign `Employee('Charlie Brown', 'trainee')` to `charlie_brown`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_variable("charlie_brown").is_equivalent("charlie_brown = Employee('Charlie Brown', 'trainee')")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self.name = name
|
||||
self.level = level
|
||||
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: 68c18b1d0fce081f85312d97
|
||||
title: Step 3
|
||||
challengeType: 20
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In a previous lesson, you learned that an attribute prefixed with a single underscore is meant for internal use by convention.
|
||||
|
||||
Modify both `name` and `level` attributes into `_name` and `_level`, since these are not supposed to be modified from outside their class.
|
||||
|
||||
Note that this does not prevent the attribute from being accessed or modified outside the class. Also, in Python there's always a way to access private attributes (prefixed with a double underscore) as well.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `__init__` method should set `self._name` to `name`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self._name = name")`)) })
|
||||
```
|
||||
|
||||
Your `__init__` method should set `self._level` to `level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").has_stmt("self._level = level")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self.name = name
|
||||
self.level = level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 68c27eb292bfec6b9402d58f
|
||||
title: Step 4
|
||||
challengeType: 20
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Add a `__str__` method to the `Employee` class. Make it return an f-string with the format `name: level`, replacing `name` and `level` with the corresponding attributes.
|
||||
|
||||
After that, print `charlie_brown` to the console.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `__str__` method with a `self` parameter .
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__str__").has_args("self")`)) })
|
||||
```
|
||||
|
||||
Your `__str__` method should return an f-string with the format `name: level`, replacing `name` and `level` with the values of `self._name` and `self._level`, respectively.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
import io
|
||||
import sys
|
||||
|
||||
captured_output = io.StringIO()
|
||||
sys.stdout = captured_output
|
||||
|
||||
empl = Employee("Frank", "dreamer")
|
||||
print(empl)
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
output = captured_output.getvalue()
|
||||
|
||||
assert "Frank: dreamer" in output
|
||||
`) })
|
||||
```
|
||||
|
||||
You should print `charlie_brown` to the console.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("print(charlie_brown)")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
id: 68c27fa1697ceb7c578b2c9f
|
||||
title: Step 5
|
||||
challengeType: 20
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `@property` decorator is used in Python to turn a method into a property. It is typically used to define getter methods, which are methods used to retrieve the value of an attribute:
|
||||
|
||||
```py
|
||||
class Person:
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
p = Person('Alice')
|
||||
print(p.name) # Alice
|
||||
```
|
||||
|
||||
Create a method named `name` with a `self` parameter and decorate it with `@property`. Inside the method, return `self._name`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `name` method with a `self` parameter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("name").has_args("self")`)) })
|
||||
```
|
||||
|
||||
Your `name` method should be decorated with `@property`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("name").has_decorators("property")`)) })
|
||||
```
|
||||
|
||||
Your `name` method should return `self._name`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("name").has_return("self._name")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
def __str__(self):
|
||||
return f'{self._name}: {self._level}'
|
||||
|
||||
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: 68c281cecda94ba2c6838e17
|
||||
title: Step 6
|
||||
challengeType: 20
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you defined a getter for `name`, you can access the `_name` attribute through the `name` property. So print `charlie_brown.name` to the console.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print `charlie_brown.name` to the console.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("print(charlie_brown.name)")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
def __str__(self):
|
||||
return f'{self._name}: {self._level}'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: 68c28b7cc3ab8f4daf144b5e
|
||||
title: Step 7
|
||||
challengeType: 20
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Following what you did in the previous steps, create a getter for the `_level` attribute.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have `level` method with a `self` parameter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("level").has_args("self")`)) })
|
||||
```
|
||||
|
||||
Your `level` method should be decorated with `@property`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("level").has_decorators("property")`)) })
|
||||
```
|
||||
|
||||
Your `level` method should return `self._level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("level").has_return("self._level")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
def __str__(self):
|
||||
return f'{self._name}: {self._level}'
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
|
||||
--fcc-editable-region--
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(charlie_brown.name)
|
||||
|
||||
```
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
---
|
||||
id: 68c28c29026ad559f8ab4329
|
||||
title: Step 8
|
||||
challengeType: 20
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now print `charlie_brown.level` to the console.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print `charlie_brown.level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("print(charlie_brown.level)")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
def __str__(self):
|
||||
return f'{self._name}: {self._level}'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
--fcc-editable-region--
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(charlie_brown.name)
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: 68c28c6517e7de5e85f8ebe5
|
||||
title: Step 9
|
||||
challengeType: 20
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you have getters for both `_name` and `_level` attributes, update the string returned by `__str__` to use `self.name` and `self.level`. This will call the getters instead of directly accessing the attributes.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `__str__` method should return `f'{self.name}: {self.level}'`
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__str__").has_return("f'{self.name}: {self.level}'")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
--fcc-editable-region--
|
||||
def __str__(self):
|
||||
return f'{self._name}: {self._level}'
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(charlie_brown.name)
|
||||
print(charlie_brown.level)
|
||||
```
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: 68c28cfab52fef6913d18166
|
||||
title: Step 10
|
||||
challengeType: 20
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now remove the last two `print` calls.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should not have `print(charlie_brown.name)` in your code.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`not _Node(_code).has_stmt("print(charlie_brown.name)")`)) })
|
||||
```
|
||||
|
||||
You should not have `print(charlie_brown.level)` in your code.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`not _Node(_code).has_stmt("print(charlie_brown.level)")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name}: {self.level}'
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
--fcc-editable-region--
|
||||
print(charlie_brown.name)
|
||||
print(charlie_brown.level)
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: 68c28d659d345270d4a61084
|
||||
title: Step 11
|
||||
challengeType: 20
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `__repr__` method is a special method that is supposed to return a string representation of the object that can be used to instantiate it.
|
||||
|
||||
For example, the `__repr__` method of `Employee('Charlie', 'developer')` should return the string `Employee('Charlie', 'developer')`, which is the same string used to create the object.
|
||||
|
||||
Give your `Employee` class a `__repr__` method with a `self` parameter, and make it return a string that can be used to instantiate the object.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `__repr__` method with a `self` parameter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__repr__").has_args("self")`)) })
|
||||
```
|
||||
|
||||
Your `__repr__` method should return the string used to instantiate the object.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
empl = Employee("Frank", "dreamer")
|
||||
assert repr(empl) == 'Employee("Frank", "dreamer")' or repr(empl) == "Employee('Frank', 'dreamer')"
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = level
|
||||
--fcc-editable-region--
|
||||
def __str__(self):
|
||||
return f'{self.name}: {self.level}'
|
||||
|
||||
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: 68c2d2414f5e111c02f4db5e
|
||||
title: Step 12
|
||||
challengeType: 20
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `__repr__` method is called under the hood when you call the `repr` function. To see it in action, print the result of calling `repr(charlie_brown)` at the bottom of your code.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print `repr(charlie_brown)`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("print(repr(charlie_brown))")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = 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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
--fcc-editable-region--
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
---
|
||||
id: 68c2d31a86d5672ca49d4521
|
||||
title: Step 14
|
||||
challengeType: 20
|
||||
dashedName: step-14
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now it's time to add some validation to the `__init__` method. At the beginning of the method, create an `if` statement that checks if either `name` or `level` are not instances of `str`.
|
||||
|
||||
Inside the `if` statement, raise a `TypeError` with the message `'name' and 'level' attribute must be of type 'str'.`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have an `if` statement in your `__init__` method.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").find_ifs()[0]`)) })
|
||||
```
|
||||
|
||||
Your `if` statement should check if either `name` and `level` are not instances of `str`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
cond = _Node(_code).find_class("Employee").find_function("__init__").find_ifs()[0].find_conditions()[0]
|
||||
conditions = [
|
||||
"not (isinstance(name, str) and isinstance(level, str))",
|
||||
"not isinstance(name, str) or not isinstance(level, str)",
|
||||
"not (isinstance(level, str) and isinstance(name, str))",
|
||||
"not isinstance(level, str) or not isinstance(name, str)",
|
||||
]
|
||||
assert any(cond.is_equivalent(c) for c in conditions)
|
||||
`) })
|
||||
```
|
||||
|
||||
Your `if` statement should raise a `TypeError` with the message `'name' and 'level' attribute must be of type 'str'.`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("__init__").find_ifs()[0].find_bodies()[0].has_stmt('raise TypeError("\\'name\\' and \\'level\\' attribute must be of type \\'str\\'.")')`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
--fcc-editable-region--
|
||||
def __init__(self, name, level):
|
||||
|
||||
self._name = name
|
||||
self._level = 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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: 68c7e19810faf3d00dba53d5
|
||||
title: Step 15
|
||||
challengeType: 20
|
||||
dashedName: step-15
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Create a class attribute named `_base_salaries` and assign it the following dictionary:
|
||||
|
||||
```py
|
||||
{
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000
|
||||
}
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a class attribute named `_base_salaries`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").has_variable("_base_salaries")`)) })
|
||||
```
|
||||
|
||||
You should assign `{'trainee': 1000, 'junior': 2000, 'mid-level': 3000, 'senior': 4000}` to the `_base_salaries` class attribute.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
assert Employee._base_salaries == {'trainee': 1000, 'junior': 2000, 'mid-level': 3000, 'senior': 4000}
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
--fcc-editable-region--
|
||||
class Employee:
|
||||
|
||||
--fcc-editable-region--
|
||||
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'.")
|
||||
self._name = name
|
||||
self._level = 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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: 68c7f7012a700243eff1cbc0
|
||||
title: Step 16
|
||||
challengeType: 20
|
||||
dashedName: step-16
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The level used to instantiate an employee should be chosen among specific levels. You'll use `_base_salaries` to validate the level and set the right salary for the employee.
|
||||
|
||||
After your existing `if` statement, create another `if` that checks if `level` is not in `Employee._base_salaries`.
|
||||
|
||||
Inside the new `if` statement, raise a `ValueError` with the message `Invalid value '{level}' for 'level' attribute.`, where `{level}` should be replaced by the value of the `level` argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
When `level` is not in `Employee._base_salaries`, you should raise a `ValueError` with the message `Invalid value '{level}' for 'level' attribute.`, where `{level}` should be replaced by the value of the `level` argument.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
try:
|
||||
Employee("Frank", "dreamer")
|
||||
except ValueError as e:
|
||||
assert str(e) == "Invalid value 'dreamer' for 'level' attribute."
|
||||
else:
|
||||
assert False, "Expected to raise ValueError with invalid level"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `level` is in `Employee._base_salaries`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
levels = [
|
||||
"trainee",
|
||||
"junior",
|
||||
"mid-level",
|
||||
"senior"
|
||||
]
|
||||
for level in levels:
|
||||
try:
|
||||
Employee("Frank", level)
|
||||
except Exception:
|
||||
assert False, f"Expected not to raise ValueError with level '{level}'"
|
||||
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
--fcc-editable-region--
|
||||
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'.")
|
||||
|
||||
self._name = name
|
||||
self._level = 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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 68c7fecda6d210cc8f1960e8
|
||||
title: Step 17
|
||||
challengeType: 20
|
||||
dashedName: step-17
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Finally, at the bottom of the `__init__` method, set the `_salary` attribute to the value corresponding to `level` inside the `_base_salaries` dictionary.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should set `self._salary` to the value corresponding to `level` inside the `_base_salaries` dictionary
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
salaries = Employee._base_salaries
|
||||
assert len(salaries) > 0
|
||||
for level, salary in salaries.items():
|
||||
emp = Employee("Frank", level)
|
||||
assert emp._salary == salary
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
--fcc-editable-region--
|
||||
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
|
||||
|
||||
--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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: 68c8002beb157fe47d2e7699
|
||||
title: Step 20
|
||||
challengeType: 20
|
||||
dashedName: step-20
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
A setter is a method used to set the value of an attribute, allowing for validation checks and restrictions. You can create a setter using the `@propertyName.setter` decorator, where `propertyName` should match the name of the property to set:
|
||||
|
||||
```py
|
||||
class Person:
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self._name = value
|
||||
|
||||
p = Person('Alice')
|
||||
p.name = 'Abigail' # Calls the setter
|
||||
print(p.name) # Abigail
|
||||
```
|
||||
|
||||
After your getter method, create a `name` method with parameters `self` and `new_name`. Decorate the method with `@name.setter`. Inside the method, set `self._name` to `new_name`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `name` method with parameters `self` and `new_name`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("name")[1].has_args("self, new_name")`)) })
|
||||
```
|
||||
|
||||
Your `name` method should be decorated with `@name.setter`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("name")[1].has_decorators("name.setter")`)) })
|
||||
```
|
||||
|
||||
Your `name` method should set `self._name` to `new_name`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("name")[1].has_stmt("self._name = new_name")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name}: {self.level}'
|
||||
|
||||
def __repr__(self):
|
||||
return f"Employee('{self.name}', '{self.level}')"
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
|
||||
--fcc-editable-region--
|
||||
@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}')
|
||||
```
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: 68c809c990f253912a9e9209
|
||||
title: Step 21
|
||||
challengeType: 20
|
||||
dashedName: step-21
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
As you learned in a previous lesson, a setter offers a way to control how an attribute can be modified. To ensure that `new_name` is the right type, create an `if` statement that raises a `TypeError` with the message `'name' must be a string.` when `new_name` is not an instance of `str`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have an `if` statement inside your `name` setter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("name")[1].find_ifs()[0]`)) })
|
||||
```
|
||||
|
||||
You should raise a `TypeError` with the message `'name' must be a string.` when `new_name` is not an instance of `str`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
non_names = [0, True, []]
|
||||
for i in non_names:
|
||||
try:
|
||||
emp.name = i
|
||||
except TypeError as e:
|
||||
assert str(e) == "'name' must be a string."
|
||||
else:
|
||||
assert False, "Expected to raise TypeError with non-string new_name"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `new_name` is a string.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
try:
|
||||
emp.name = "Jack"
|
||||
except Exception:
|
||||
assert False, "Expected not to raise any exception with valid new_name"
|
||||
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@name.setter
|
||||
def name(self, new_name):
|
||||
|
||||
self._name = new_name
|
||||
--fcc-editable-region--
|
||||
@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}')
|
||||
```
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
---
|
||||
id: 68c80b63ca4e5eadf1fac738
|
||||
title: Step 22
|
||||
challengeType: 20
|
||||
dashedName: step-22
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Right after setting the `_name` attribute to `new_name`, print `'name' updated to 'new_name'.`, where `new_name` should be replaced by the new value of `_name`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print `'name' updated to 'new_name'.`, where `new_name` should be replaced by the new value of `_name`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
import io
|
||||
import sys
|
||||
|
||||
captured_output = io.StringIO()
|
||||
sys.stdout = captured_output
|
||||
|
||||
empl = Employee("Frank", "trainee")
|
||||
empl.name = "Terry"
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
output = captured_output.getvalue()
|
||||
|
||||
assert "'name' updated to 'Terry'." in output
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
--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--
|
||||
@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}')
|
||||
```
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: 68c80c1a8e58febb77120bbc
|
||||
title: Step 13
|
||||
challengeType: 20
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remove the last `print` call from your code.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should remove `print(repr(charlie_brown))` from your code.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`not _Node(_code).has_stmt("print(repr(charlie_brown))")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
def __init__(self, name, level):
|
||||
self._name = name
|
||||
self._level = 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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
--fcc-editable-region--
|
||||
print(repr(charlie_brown))
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: 68c9a09163d4a42e9bc9b638
|
||||
title: Step 23
|
||||
challengeType: 20
|
||||
dashedName: step-23
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you have a setter method for the `_name` attribute that exposes the `name` property.
|
||||
|
||||
So try to update `charlie_brown`'s `name` to a different name as you would normally do with any attribute. This will call the setter method under the hood.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should update `charlie_brown`'s `name` to a different name.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
assert _Node(_code).has_variable("charlie_brown.name")
|
||||
assert charlie_brown.name != 'Charlie Brown'
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 68c9a1c241206043c81293d5
|
||||
title: Step 18
|
||||
challengeType: 20
|
||||
dashedName: step-18
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you have a new attribute, you're going to create a getter for it.
|
||||
|
||||
Create a method named `salary` with a `self` parameter and use the `@property` decorator on it. Inside the method, return `self._salary`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `salary` method with a `self` parameter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("salary").has_args("self")`)) })
|
||||
```
|
||||
|
||||
Your `salary` method should be decorated with `@property`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("salary").has_decorators("property")`)) })
|
||||
```
|
||||
|
||||
Your `salary` method should return `self._salary`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_function("salary").has_return("self._salary")`)) })
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
```
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
---
|
||||
id: 68c9a2c015ed7b54cf81e789
|
||||
title: Step 19
|
||||
challengeType: 20
|
||||
dashedName: step-19
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
At the bottom of your code, use an f-string to print `Base salary: $` followed by the amount of `charlie_brown`'s salary.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print an f-string containing `Base salary: $` followed by the amount of `charlie_brown`'s salary.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("print(f'Base salary: \${charlie_brown.salary}')")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
--fcc-editable-region--
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 68c9a624fd54288f694ebdbf
|
||||
title: Step 24
|
||||
challengeType: 20
|
||||
dashedName: step-24
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Feel free to set the `name` property to something that is not a string to see the validation process in action. After that, restore `charlie_brown`'s `name` by removing any line of code that changes its name.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should restore `charlie_brown`'s `name`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
assert charlie_brown.name == 'Charlie Brown'
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
--fcc-editable-region--
|
||||
charlie_brown.name = 'Snoopy'
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 68c9a72c9dab42a15ce6972b
|
||||
title: Step 25
|
||||
challengeType: 20
|
||||
dashedName: step-25
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you'll create a setter for the `_level` attribute. Create a method named `level` with parameters `self` and `new_level`. Decorate the method with `@level.setter`.
|
||||
|
||||
Inside the method, set `self._level` to `new_level`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `level` method with parameters `self` and `new_level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].has_args("self, new_level")`)) })
|
||||
```
|
||||
|
||||
Your `level` method should be decorated with `@level.setter`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].has_decorators("level.setter")`)) })
|
||||
```
|
||||
|
||||
Your `level` method should set `self._level` to `new_level`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].has_stmt("self._level = 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 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]
|
||||
|
||||
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}'.")
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
|
||||
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
---
|
||||
id: 68c9c3aa714bc326e026b826
|
||||
title: Step 26
|
||||
challengeType: 20
|
||||
dashedName: step-26
|
||||
---
|
||||
|
||||
# --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`.
|
||||
|
||||
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.
|
||||
|
||||
# --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]`)) })
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
try:
|
||||
emp.level = "dreamer"
|
||||
except ValueError as e:
|
||||
assert str(e) == "Invalid value 'dreamer' for 'level' attribute."
|
||||
else:
|
||||
assert False, "Expected to raise ValueError with invalid new_level"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `new_level` is a key of `Employee._base_salaries`
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
for i in Employee._base_salaries:
|
||||
try:
|
||||
emp.level = i
|
||||
except Exception:
|
||||
assert False, "Expected not to raise any exception with valid 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 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]
|
||||
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@level.setter
|
||||
def level(self, new_level):
|
||||
|
||||
self._level = new_level
|
||||
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: 68c9cab4b1118da59eecfc56
|
||||
title: Step 27
|
||||
challengeType: 20
|
||||
dashedName: step-27
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
After the existing `if` statement, create another one to raise a `ValueError` when `new_level` is already the selected level.
|
||||
|
||||
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.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("level")[1].find_ifs()[1]`)) })
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
try:
|
||||
emp.level = "trainee"
|
||||
except ValueError as e:
|
||||
assert str(e) == "'trainee' is already the selected level."
|
||||
else:
|
||||
assert False, "Expected to raise ValueError with invalid 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 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]
|
||||
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@level.setter
|
||||
def level(self, new_level):
|
||||
if new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
|
||||
|
||||
self._level = new_level
|
||||
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
---
|
||||
id: 68ca758f8160b11757f877ae
|
||||
title: Step 28
|
||||
challengeType: 20
|
||||
dashedName: step-28
|
||||
---
|
||||
|
||||
# --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.
|
||||
|
||||
# --hints--
|
||||
|
||||
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()[2]`)) })
|
||||
```
|
||||
|
||||
When `new_level` is lower than `self.level`, you should raise a `ValueError` with the message `Cannot change to lower level.`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'junior')
|
||||
try:
|
||||
emp.level = "trainee"
|
||||
except ValueError as e:
|
||||
assert str(e) == "Cannot change to lower level."
|
||||
else:
|
||||
assert False, "Expected to raise ValueError with invalid new_level"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `new_level` is higher than `self.level`.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'junior')
|
||||
try:
|
||||
emp.level = "senior"
|
||||
except Exception:
|
||||
assert False, "Expected not to raise exception with valid 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 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]
|
||||
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@level.setter
|
||||
def level(self, new_level):
|
||||
if new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if new_level == self.level:
|
||||
raise ValueError(f"'{self.level}' is already the selected level.")
|
||||
|
||||
self._level = new_level
|
||||
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: 68caa8fb3bed34833ef24aee
|
||||
title: Step 29
|
||||
challengeType: 20
|
||||
dashedName: step-29
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
When the level is modified, you need to update the salary as well.
|
||||
|
||||
Before setting `self._level`, set `self._salary` to the base salary for the new level.
|
||||
|
||||
# --hints--
|
||||
|
||||
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.level = new_level
|
||||
assert emp.salary == Employee._base_salaries.get(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 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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
--fcc-editable-region--
|
||||
|
||||
self._level = new_level
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: 68caa9fb6f1602975e41b051
|
||||
title: Step 30
|
||||
challengeType: 20
|
||||
dashedName: step-30
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Complete the setter by printing `'{name}' promoted to '{new_level}'.` inside your method. Replace `{name}` and `{new_level}` with the employee's name and new level, respectively.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should print `'{name}' promoted to '{new_level}'.` where `{name}` and `{new_level}` should be replaced with the employee's name and new level, respectively.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
import io
|
||||
import sys
|
||||
|
||||
captured_output = io.StringIO()
|
||||
sys.stdout = captured_output
|
||||
|
||||
empl = Employee("Frank", "trainee")
|
||||
empl.level = 'junior'
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
output = captured_output.getvalue()
|
||||
|
||||
assert "'Frank' promoted to 'junior'." in output
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
--fcc-editable-region--
|
||||
|
||||
self._salary = Employee._base_salaries[new_level]
|
||||
self._level = new_level
|
||||
--fcc-editable-region--
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
```
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 68caaaef4afb18aab8a684d4
|
||||
title: Step 31
|
||||
challengeType: 20
|
||||
dashedName: step-31
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
It's time to test your new setter. Try to assign invalid values such as a random string or the current level (`trainee`) to `charlie_brown.level` and see the error messages in the console.
|
||||
|
||||
Once you've done, remove the lines raising errors and set `charlie_brown.level` to the string `junior`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should set `charlie_brown.level` to the string `junior`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).has_stmt("charlie_brown.level = 'junior'")`)) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
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
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
---
|
||||
id: 68caacb0f4311cc9be9a2132
|
||||
title: Step 32
|
||||
challengeType: 20
|
||||
dashedName: step-32
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you'll focus on coding a `salary` setter. After the `salary` getter, create a simple setter for the `salary` property that sets `self._salary` to the value passed to the method as its argument. After that, print `Salary updated to $` followed by the new salary and a period.
|
||||
|
||||
You'll take care of validating the new salary in the next few steps.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `Employee` class should have a `salary` method decorated with `@salary.setter`.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("salary")[1].has_decorators("salary.setter")`)) })
|
||||
```
|
||||
|
||||
Your `salary` setter should set `self._salary` to the value passed to it as the argument.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
built_in_print = print
|
||||
print = lambda x: x
|
||||
emp.salary += 100
|
||||
print = built_in_print
|
||||
assert emp.salary == Employee._base_salaries['trainee'] + 100
|
||||
`) })
|
||||
```
|
||||
|
||||
Your `salary` setter should print `Salary updated to $` followed by the new salary and a period.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
import io
|
||||
import sys
|
||||
|
||||
captured_output = io.StringIO()
|
||||
sys.stdout = captured_output
|
||||
|
||||
emp = Employee('Frank', 'trainee')
|
||||
emp.salary += 100
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
output = captured_output.getvalue()
|
||||
|
||||
assert f"Salary updated to \${Employee._base_salaries['trainee'] + 100}" in output
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
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--
|
||||
|
||||
charlie_brown = Employee('Charlie Brown', 'trainee')
|
||||
print(charlie_brown)
|
||||
print(f'Base salary: ${charlie_brown.salary}')
|
||||
|
||||
charlie_brown.level = 'junior'
|
||||
|
||||
```
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 68caaf1e590806f0b87f5922
|
||||
title: Step 33
|
||||
challengeType: 20
|
||||
dashedName: step-33
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
At the beginning of your setter, create an `if` statement that raises a `TypeError` with the message `'salary' must be a number.` when `new_salary` is not either an integer or a float.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have an `if` statement in your `salary` setter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("salary")[1].find_ifs()[0]`)) })
|
||||
```
|
||||
|
||||
You should raise a `TypeError` with the message `'salary' must be a number.` when `new_salary` is not either an integer or a float.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
built_in_print = print
|
||||
print = lambda x: x
|
||||
try:
|
||||
emp.salary = "100"
|
||||
except TypeError as e:
|
||||
print = built_in_print
|
||||
assert str(e) == "'salary' must be a number."
|
||||
else:
|
||||
assert False, "Expected to raise TypeError with invalid new_salary"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `new_salary` is either an integer or a float.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
built_in_print = print
|
||||
print = lambda x: x
|
||||
try:
|
||||
emp.salary = 100
|
||||
except Exception:
|
||||
assert False, "Expected not to raise exception with valid new_salary"
|
||||
try:
|
||||
emp.salary = 100.00
|
||||
except Exception:
|
||||
assert False, "Expected not to raise exception with valid new_salary"
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@salary.setter
|
||||
def salary(self, new_salary):
|
||||
|
||||
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'
|
||||
|
||||
```
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
---
|
||||
id: 68cab02fd80a91042c0165b8
|
||||
title: Step 34
|
||||
challengeType: 20
|
||||
dashedName: step-34
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
After your existing `if` statement, create another one for when the new salary is less than the base salary for the current level.
|
||||
|
||||
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.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a second `if` statement in your `salary` setter.
|
||||
|
||||
```js
|
||||
({ test: () => assert(runPython(`_Node(_code).find_class("Employee").find_functions("salary")[1].find_ifs()[1]`)) })
|
||||
```
|
||||
|
||||
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
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
built_in_print = print
|
||||
print = lambda x: x
|
||||
minimum_salary = Employee._base_salaries['trainee']
|
||||
try:
|
||||
emp.salary = minimum_salary - 1
|
||||
except ValueError as e:
|
||||
print = built_in_print
|
||||
assert str(e) == f"Salary must be higher than minimum salary \${minimum_salary}."
|
||||
else:
|
||||
assert False, "Expected to raise ValueError with invalid new_salary"
|
||||
`) })
|
||||
```
|
||||
|
||||
You should not raise any exception when `new_salary` is greater than the current salary.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
emp = Employee('Frank', 'trainee')
|
||||
built_in_print = print
|
||||
print = lambda x: x
|
||||
minimum_salary = Employee._base_salaries['trainee']
|
||||
try:
|
||||
emp.salary = minimum_salary + 1
|
||||
except Exception:
|
||||
assert False, "Expected not to raise exception with valid new_salary"
|
||||
try:
|
||||
emp.salary = minimum_salary + 1000
|
||||
except Exception:
|
||||
assert False, "Expected not to raise exception with valid new_salary"
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
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
|
||||
--fcc-editable-region--
|
||||
@salary.setter
|
||||
def salary(self, new_salary):
|
||||
if not isinstance(new_salary, (int, float)):
|
||||
raise TypeError("'salary' must be a number.")
|
||||
|
||||
|
||||
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'
|
||||
|
||||
```
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
---
|
||||
id: 68cab17ac51b861a8458c42d
|
||||
title: Step 35
|
||||
challengeType: 20
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Finally, now that you have completed the `salary` setter, you can call it within the `level` setter.
|
||||
|
||||
Modify the line `self._salary = Employee._base_salaries[new_level]` so that it calls the `salary` setter instead of modifying directly the `_salary` attribute.
|
||||
|
||||
With that the salary tracker workshop is complete.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should set `self.salary` to `Employee._base_salaries[new_level]` within the `level` setter.
|
||||
|
||||
```js
|
||||
({ test: () => runPython(`
|
||||
import io
|
||||
import sys
|
||||
|
||||
captured_output = io.StringIO()
|
||||
sys.stdout = captured_output
|
||||
|
||||
empl = Employee("Frank", "trainee")
|
||||
empl.level = "junior"
|
||||
|
||||
sys.stdout = sys.__stdout__
|
||||
output = captured_output.getvalue()
|
||||
|
||||
assert "'Frank' promoted to 'junior'." in output
|
||||
assert f"Salary updated to \${Employee._base_salaries['junior']}." in output
|
||||
`) })
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
print(f"'{self.name}' promoted to '{new_level}'.")
|
||||
--fcc-editable-region--
|
||||
self._salary = Employee._base_salaries[new_level]
|
||||
--fcc-editable-region--
|
||||
self._level = new_level
|
||||
|
||||
@property
|
||||
def salary(self):
|
||||
return self._salary
|
||||
|
||||
@salary.setter
|
||||
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]:
|
||||
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}')
|
||||
charlie_brown.level = 'junior'
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```py
|
||||
class Employee:
|
||||
_base_salaries = {
|
||||
'trainee': 1000,
|
||||
'junior': 2000,
|
||||
'mid-level': 3000,
|
||||
'senior': 4000,
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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 new_level not in Employee._base_salaries:
|
||||
raise ValueError(f"Invalid value '{new_level}' for 'level' attribute.")
|
||||
if 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.")
|
||||
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):
|
||||
if not isinstance(new_salary, (int, float)):
|
||||
raise TypeError("'salary' must be a number.")
|
||||
if 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}')
|
||||
charlie_brown.level = 'junior'
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "Build a Salary Tracker",
|
||||
"isUpcomingChange": true,
|
||||
"dashedName": "workshop-salary-tracker",
|
||||
"helpCategory": "Python",
|
||||
"blockLayout": "challenge-grid",
|
||||
"blockType": "workshop",
|
||||
"challengeOrder": [
|
||||
{ "id": "68c15a2a1f0e70ca7154aba5", "title": "Step 1" },
|
||||
{ "id": "68c16924498728d259e4781c", "title": "Step 2" },
|
||||
{ "id": "68c18b1d0fce081f85312d97", "title": "Step 3" },
|
||||
{ "id": "68c27eb292bfec6b9402d58f", "title": "Step 4" },
|
||||
{ "id": "68c27fa1697ceb7c578b2c9f", "title": "Step 5" },
|
||||
{ "id": "68c281cecda94ba2c6838e17", "title": "Step 6" },
|
||||
{ "id": "68c28b7cc3ab8f4daf144b5e", "title": "Step 7" },
|
||||
{ "id": "68c28c29026ad559f8ab4329", "title": "Step 8" },
|
||||
{ "id": "68c28c6517e7de5e85f8ebe5", "title": "Step 9" },
|
||||
{ "id": "68c28cfab52fef6913d18166", "title": "Step 10" },
|
||||
{ "id": "68c28d659d345270d4a61084", "title": "Step 11" },
|
||||
{ "id": "68c2d2414f5e111c02f4db5e", "title": "Step 12" },
|
||||
{ "id": "68c80c1a8e58febb77120bbc", "title": "Step 13" },
|
||||
{ "id": "68c2d31a86d5672ca49d4521", "title": "Step 14" },
|
||||
{ "id": "68c7e19810faf3d00dba53d5", "title": "Step 15" },
|
||||
{ "id": "68c7f7012a700243eff1cbc0", "title": "Step 16" },
|
||||
{ "id": "68c7fecda6d210cc8f1960e8", "title": "Step 17" },
|
||||
{ "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" }
|
||||
],
|
||||
"usesMultifileEditor": true,
|
||||
"hasEditableBoundaries": true
|
||||
}
|
||||
@@ -725,6 +725,7 @@
|
||||
"comingSoon": true,
|
||||
"blocks": [
|
||||
"lecture-understanding-object-oriented-programming-and-encapsulation",
|
||||
"workshop-salary-tracker",
|
||||
"lecture-understanding-inheritance-and-polymorphism",
|
||||
"workshop-media-catalogue",
|
||||
"lab-polygon-area-calculator",
|
||||
|
||||
Reference in New Issue
Block a user