Skip to content

Commit 722a1f5

Browse files
authored
Merge branch 'master' into list-comprehension-update
2 parents 1749fbf + e68d0c3 commit 722a1f5

File tree

18 files changed

+644
-0
lines changed

18 files changed

+644
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Inheritance and Composition
2+
3+
This folder provides the code examples for the Real Python tutorial [Inheritance and Composition: A Python OOP Guide](https://realpython.com/inheritance-composition-python/).
4+
5+
Each of the folders contains the state of the code at the end of the three respective sections:
6+
7+
- **`inheritance/`**: [An Overview of Inheritance in Python](https://realpython.com/inheritance-composition-python/#an-overview-of-inheritance-in-python)
8+
- **`composition/`**: [Composition in Python](https://realpython.com/inheritance-composition-python/#composition-in-python)
9+
- **`choosing/`**: [Choosing Between Inheritance and Composition in Python](https://realpython.com/inheritance-composition-python/#choosing-between-inheritance-and-composition-in-python)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from representations import AsDictionaryMixin
2+
3+
4+
class Address(AsDictionaryMixin):
5+
def __init__(self, street, city, state, zipcode, street2=""):
6+
self.street = street
7+
self.street2 = street2
8+
self.city = city
9+
self.state = state
10+
self.zipcode = zipcode
11+
12+
def __str__(self):
13+
lines = [self.street]
14+
if self.street2:
15+
lines.append(self.street2)
16+
lines.append(f"{self.city}, {self.state} {self.zipcode}")
17+
return "\n".join(lines)
18+
19+
20+
class _AddressBook:
21+
def __init__(self):
22+
self._employee_addresses = {
23+
1: Address("121 Admin Rd.", "Concord", "NH", "03301"),
24+
2: Address("67 Paperwork Ave", "Manchester", "NH", "03101"),
25+
3: Address("15 Rose St", "Concord", "NH", "03301", "Apt. B-1"),
26+
4: Address("39 Sole St.", "Concord", "NH", "03301"),
27+
5: Address("99 Mountain Rd.", "Concord", "NH", "03301"),
28+
}
29+
30+
def get_employee_address(self, employee_id):
31+
address = self._employee_addresses.get(employee_id)
32+
if not address:
33+
raise ValueError(employee_id)
34+
return address
35+
36+
37+
_address_book = _AddressBook()
38+
39+
40+
def get_employee_address(employee_id):
41+
return _address_book.get_employee_address(employee_id)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from productivity import get_role
2+
from hr import get_policy
3+
from contacts import get_employee_address
4+
from representations import AsDictionaryMixin
5+
6+
7+
class _EmployeeDatabase:
8+
def __init__(self):
9+
self._employees = {
10+
1: {"name": "Mary Poppins", "role": "manager"},
11+
2: {"name": "John Smith", "role": "secretary"},
12+
3: {"name": "Kevin Bacon", "role": "sales"},
13+
4: {"name": "Jane Doe", "role": "factory"},
14+
5: {"name": "Robin Williams", "role": "secretary"},
15+
}
16+
17+
@property
18+
def employees(self):
19+
return [Employee(id_) for id_ in sorted(self._employees)]
20+
21+
def get_employee_info(self, employee_id):
22+
info = self._employees.get(employee_id)
23+
if not info:
24+
raise ValueError(employee_id)
25+
return info
26+
27+
28+
class Employee(AsDictionaryMixin):
29+
def __init__(self, id):
30+
self.id = id
31+
info = employee_database.get_employee_info(self.id)
32+
self.name = info.get("name")
33+
self.address = get_employee_address(self.id)
34+
self._role = get_role(info.get("role"))
35+
self._payroll = get_policy(self.id)
36+
37+
def work(self, hours):
38+
duties = self._role.perform_duties(hours)
39+
print(f"Employee {self.id} - {self.name}:")
40+
print(f"- {duties}")
41+
print("")
42+
self._payroll.track_work(hours)
43+
44+
def calculate_payroll(self):
45+
return self._payroll.calculate_payroll()
46+
47+
def apply_payroll_policy(self, new_policy):
48+
new_policy.apply_to_policy(self._payroll)
49+
self._payroll = new_policy
50+
51+
52+
employee_database = _EmployeeDatabase()
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
class _PayrollSystem:
2+
def __init__(self):
3+
self._employee_policies = {
4+
1: SalaryPolicy(3000),
5+
2: SalaryPolicy(1500),
6+
3: CommissionPolicy(1000, 100),
7+
4: HourlyPolicy(15),
8+
5: HourlyPolicy(9),
9+
}
10+
11+
def get_policy(self, employee_id):
12+
policy = self._employee_policies.get(employee_id)
13+
if not policy:
14+
return ValueError(employee_id)
15+
return policy
16+
17+
def calculate_payroll(self, employees):
18+
print("Calculating Payroll")
19+
print("===================")
20+
for employee in employees:
21+
print(f"Payroll for: {employee.id} - {employee.name}")
22+
print(f"- Check amount: {employee.calculate_payroll()}")
23+
if employee.address:
24+
print("- Sent to:")
25+
print(employee.address)
26+
print("")
27+
28+
29+
class PayrollPolicy:
30+
def __init__(self):
31+
self.hours_worked = 0
32+
33+
def track_work(self, hours):
34+
self.hours_worked += hours
35+
36+
37+
class SalaryPolicy(PayrollPolicy):
38+
def __init__(self, weekly_salary):
39+
super().__init__()
40+
self.weekly_salary = weekly_salary
41+
42+
def calculate_payroll(self):
43+
return self.weekly_salary
44+
45+
46+
class HourlyPolicy(PayrollPolicy):
47+
def __init__(self, hour_rate):
48+
super().__init__()
49+
self.hour_rate = hour_rate
50+
51+
def calculate_payroll(self):
52+
return self.hours_worked * self.hour_rate
53+
54+
55+
class CommissionPolicy(SalaryPolicy):
56+
def __init__(self, weekly_salary, commission_per_sale):
57+
super().__init__(weekly_salary)
58+
self.commission_per_sale = commission_per_sale
59+
60+
@property
61+
def commission(self):
62+
sales = self.hours_worked / 5
63+
return sales * self.commission_per_sale
64+
65+
def calculate_payroll(self):
66+
fixed = super().calculate_payroll()
67+
return fixed + self.commission
68+
69+
70+
class LTDPolicy:
71+
def __init__(self):
72+
self._base_policy = None
73+
74+
def track_work(self, hours):
75+
self._check_base_policy()
76+
return self._base_policy.track_work(hours)
77+
78+
def calculate_payroll(self):
79+
self._check_base_policy()
80+
base_salary = self._base_policy.calculate_payroll()
81+
return base_salary * 0.6
82+
83+
def apply_to_policy(self, base_policy):
84+
self._base_policy = base_policy
85+
86+
def _check_base_policy(self):
87+
if not self._base_policy:
88+
raise RuntimeError("Base policy missing")
89+
90+
91+
_payroll_system = _PayrollSystem()
92+
93+
94+
def get_policy(employee_id):
95+
return _payroll_system.get_policy(employee_id)
96+
97+
98+
def calculate_payroll(employees):
99+
_payroll_system.calculate_payroll(employees)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
class _ProductivitySystem:
2+
def __init__(self):
3+
self._roles = {
4+
"manager": ManagerRole,
5+
"secretary": SecretaryRole,
6+
"sales": SalesRole,
7+
"factory": FactoryRole,
8+
}
9+
10+
def get_role(self, role_id):
11+
role_type = self._roles.get(role_id)
12+
if not role_type:
13+
raise ValueError("role_id")
14+
return role_type()
15+
16+
def track(self, employees, hours):
17+
print("Tracking Employee Productivity")
18+
print("==============================")
19+
for employee in employees:
20+
employee.work(hours)
21+
print("")
22+
23+
24+
class ManagerRole:
25+
def perform_duties(self, hours):
26+
return f"screams and yells for {hours} hours."
27+
28+
29+
class SecretaryRole:
30+
def perform_duties(self, hours):
31+
return f"does paperwork for {hours} hours."
32+
33+
34+
class SalesRole:
35+
def perform_duties(self, hours):
36+
return f"expends {hours} hours on the phone."
37+
38+
39+
class FactoryRole:
40+
def perform_duties(self, hours):
41+
return f"manufactures gadgets for {hours} hours."
42+
43+
44+
_productivity_system = _ProductivitySystem()
45+
46+
47+
def get_role(role_id):
48+
return _productivity_system.get_role(role_id)
49+
50+
51+
def track(employees, hours):
52+
_productivity_system.track(employees, hours)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from hr import calculate_payroll, LTDPolicy
2+
from productivity import track
3+
from employees import employee_database
4+
5+
employees = employee_database.employees
6+
7+
sales_employee = employees[2]
8+
ltd_policy = LTDPolicy()
9+
sales_employee.apply_payroll_policy(ltd_policy)
10+
11+
track(employees, 40)
12+
calculate_payroll(employees)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class Rectangle:
2+
def __init__(self, length, height):
3+
self._length = length
4+
self._height = height
5+
6+
@property
7+
def area(self):
8+
return self._length * self._height
9+
10+
def resize(self, new_length, new_height):
11+
self._length = new_length
12+
self._height = new_height
13+
14+
15+
class Square(Rectangle):
16+
def __init__(self, side_size):
17+
super().__init__(side_size, side_size)
18+
19+
20+
rectangle = Rectangle(2, 4)
21+
assert rectangle.area == 8
22+
23+
square = Square(2)
24+
assert square.area == 4
25+
26+
rectangle.resize(3, 5)
27+
assert rectangle.area == 15
28+
29+
square.resize(3, 5)
30+
print(f"Square area: {square.area}")
31+
32+
print("OK!")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class AsDictionaryMixin:
2+
def to_dict(self):
3+
return {
4+
prop: self._represent(value)
5+
for prop, value in self.__dict__.items()
6+
if not self._is_internal(prop)
7+
}
8+
9+
def _represent(self, value):
10+
if isinstance(value, object):
11+
if hasattr(value, "to_dict"):
12+
return value.to_dict()
13+
else:
14+
return str(value)
15+
else:
16+
return value
17+
18+
def _is_internal(self, prop):
19+
return prop.startswith("_")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
class Address:
2+
def __init__(self, street, city, state, zipcode, street2=""):
3+
self.street = street
4+
self.street2 = street2
5+
self.city = city
6+
self.state = state
7+
self.zipcode = zipcode
8+
9+
def __str__(self):
10+
lines = [self.street]
11+
if self.street2:
12+
lines.append(self.street2)
13+
lines.append(f"{self.city}, {self.state} {self.zipcode}")
14+
return "\n".join(lines)
15+
16+
17+
class AddressBook:
18+
def __init__(self):
19+
self._employee_addresses = {
20+
1: Address("121 Admin Rd.", "Concord", "NH", "03301"),
21+
2: Address("67 Paperwork Ave", "Manchester", "NH", "03101"),
22+
3: Address("15 Rose St", "Concord", "NH", "03301", "Apt. B-1"),
23+
4: Address("39 Sole St.", "Concord", "NH", "03301"),
24+
5: Address("99 Mountain Rd.", "Concord", "NH", "03301"),
25+
}
26+
27+
def get_employee_address(self, employee_id):
28+
address = self._employee_addresses.get(employee_id)
29+
if not address:
30+
raise ValueError(employee_id)
31+
return address

0 commit comments

Comments
 (0)