Below is my code:
class Person():
def __init__(self,name):
self.name=name
self.pet=None
def print_name(self):
print(f"The person's name is {self.name}")
class Employee(Person):
raise_amt=1.04
def __init__(self,name,salary):
super(Employee,self).__init__(name)
self.salary=salary
def apply_raise(self):
self.salary=int(self.salary*self.raise_amt)
class Manager(Person):
def __init__(self,name,salary,employees=None):
super().__init__(name)
self.salar=salary
if employees==None:
self.employees=[]
else:
self.employees=employees
def add_emp(self,emp):
if emp not in self.employees:
self.employees.append(emp)
def print_emps(self):
for emp in self.employees:
emp.print_name()
When I try to run the program with below code, the error will pop up.
frank=Employee("Frank",120000)
john=Employee("John",10000)
sean=Manager("Sean",20000,frank)
sean.add_emp(john)
sean.print_emps()
The error I receive is TypeError: argument of type 'Employee' is not iterable
.
However, when I put the square bracket around [frank]
, the error is gone.
Can you help me to understand the reason?
CodePudding user response:
As others have said, in the Manager
class __init__
method, you allow an optional list of Employees
to be given. If this list is given then the Manager
instance will set it as the employees
variable else it will set an empty list. In your case, you are initializing the Manager
class with an instance of Employee
and not a list.
For the future...
I recommend a few code style changes to help avoid these kind of issues:
- Add type annotations. This is not only great for you reading back your code, it enables linters to catch type errors before you run the code.
- Add more whitespace. Add spaces between operators, variables, parameters, functions, etc. It makes reading the code much easier.
- Use keyword arguments. In the example below, it's much easier to see what each argument is for and by extension, you can see
employees
is clearly a list.
from typing import Optional, List
class Manager(Person):
def __init__(self, name: str, salary: int, employees: Optional[List[Employee]] = None):
super().__init__(name)
self.salary = salary
if employees is None:
self.employees = []
else:
self.employees = employees
def add_emp(self, emp: Employee):
if emp not in self.employees:
self.employees.append(emp)
def print_emps(self):
for emp in self.employees:
emp.print_name()
And then when you're calling the classes:
frank = Employee(name="Frank", salary=120000)
sean = Manager(name="Sean", salary=20000, employees=[frank])
CodePudding user response:
Python is expecting employees
to be a list of items, not a single item. Adding the square brackets around "frank" turns it from a simple Employee object to a list of Employee objects, with the first item being "frank".
You have differing code between your __init__
and add_emp
methods. In __init__
, you set employees to the value specified (making it in your case an Employee object), whereas in add_emp
you use append()
to add the value to the existing values, maintaining the variable as a list.
Let's examine what your code actually does here:
- First you create two instances of
Employee
with names (strings) and salaries (ints) - Then you create a Manager with a name, salary, and a single employee object assigned to
self.employees
- You then check if the "John" employee is in sean's employees variable, but sean's employees variable is not a list of employees, it's just a single employee (frank). You're checking if john is in frank, not if john is in a list of items that currently includes frank.
If you only want to pass a single employee when creating each manager the best fix would be to change your __init__
method as follows:
def __init__(self,name,salary,employees=None):
super().__init__(name)
self.salar=salary
self.employees = []
if employees:
self.employees.append(employees)
If you want to pass in multiple employees, then do exactly as you are currently, pass a list e.g. [frank]
not frank
to the method.