-2

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?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 4
    You initialize Manager with an employees list of frank, but frank is not a list, it's an Employee. – Passerby Dec 27 '21 at 22:58
  • 1
    An Employee indeed isn't iterable. A list _is_. – jonrsharpe Dec 27 '21 at 23:00
  • 1
    side note, please use `if employees is None` rather than `==` - [relevant question](https://stackoverflow.com/questions/3257919/what-is-the-difference-between-is-none-and-none) – shriakhilc Dec 27 '21 at 23:03

2 Answers2

4

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])
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Zach10za
  • 100
  • 8
  • You can also simplify: `self.employees = [] if employees is None else employees`. Feel free to add this in. – wjandrea Dec 27 '21 at 23:15
  • 1
    you should also kinda show from where you have imported those names [type hints] (considering it may not be clear to some), also using `Optional[List[Employee]]` seems a bit overkill IMO (especially for beginners) and a simple `list` would have sufficed but sure this is better – Matiiss Dec 27 '21 at 23:16
-2

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.

Christy Kail
  • 146
  • 7