Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance issue creating objects

Person.py

class Person:
    def __init__(self, pname):
        self.name = pname

    @classmethod
    def parse(cls, name):
        return cls(name)

Employee.py

class Employee(Person):
    def __init__(self, ename, esalary):
        super().__init__(ename)
        self.salary = esalary

    @classmethod
    def parse(cls, data):
        person = super().parse(data["name"])
        person.salary = data["salary"]
        return person

Customer.py

class Customer(Person):
    def __init__(self, ename, country):
        super().__init__(ename)
        self.country = country

    @classmethod
    def parse(cls, data):
        person = super().parse(data["name"])
        person.country = data["country"]
        return person

main.py

emp_data = {
    "name": "john",
    "salary": 1000
}
emp = Employee.parse(emp_data)
print(type(emp))
print(emp.name)
print(emp.salary)


cust_data = {
    "name": "peter",
    "country": "USA"
}
cust = Customer.parse(cust_data)
print(type(cust))
print(cust.name)
print(cust.country)

errors

TypeError                                 Traceback (most recent call last)
<ipython-input-32-a5abd51d9870> in <module>()
     36     "salary": 1000
     37 }
---> 38 emp = Employee.parse(emp_data)
     39 print(type(emp))
     40 print(emp.name)

<ipython-input-32-a5abd51d9870> in parse(cls, data)
     14     @classmethod
     15     def parse(cls, data):
---> 16         person = super().parse(data["name"])
     17         person.salary = data["salary"]
     18         return person

<ipython-input-32-a5abd51d9870> in parse(cls, name)
      5     @classmethod
      6     def parse(cls, name):
----> 7         return cls(name)
      8 
      9 class Employee(Person):

TypeError: __init__() missing 1 required positional argument: 'esalary'

This example is just for reproducing the problem in the actual code. The actual parse functions involve complex logic.

In the above example, Employee and Customer classes extend Person class. According to the error, init method of Person class which is invoked by parse form same class, is expecting esalary. But there is no esalary property on Person init method.

What's wrong with my program? I think I did not understand inheritance properly. Please, correct me. First of all, did I structure my program correctly?

like image 640
lch Avatar asked Mar 06 '23 07:03

lch


2 Answers

Your problem is that parse calls cls(name), which only has one argument, but cls is Employee or Customer, not necessarily Person. You must either provide the additional arguments (salary/country) at construction time (when you call parse), or give them a default value when they aren't provided.

Either provide the additional needed arguments to the constructor via parse:

@classmethod
def parse(cls, name, *args, **kwargs):
    return cls(name, *args, **kwargs)

which would allow you do something like:

Employee.parse(name, salary)  # Really no different than Employee(name, salary)

Or add defaults to those parameters in the child constructors so that they can be constructed with only a name:

class Customer(Person):
    def __init__(self, ename, country=None):
        # ...

class Employee(Person):
    def __init__(self, ename, esalary=None):
        # ...

Note that this might result in you having None values floating around in these attributes, but that's inevitable if you want to construct a new, say, Employee object and don't want to also provide a salary at the same time.

like image 56
scnerd Avatar answered Mar 08 '23 21:03

scnerd


Consider the following:

class Foo():
    @classmethod
    def p(cls):
        print(cls.__name__)

 class Bar(Foo):
     @classmethod
     def p(cls):
         super().p()

bar = Bar()
bar.p()

This will print "Bar". The class object passed to Foo.p when called from within Bar.p is actually the Bar class, not the Foo class.

So in your code, when you call Person.parse through Employee.parse, your line in Person.parse, return cls(name) is actually calling Employee(name), but Employee's init take 2 positional arguments.

For your code to work with this structure, your __init__s have to have the same signature, or at least compatible signatures (for example by adding *args to Person's __init__ method and passing them to the constructor).

like image 21
jbch Avatar answered Mar 08 '23 19:03

jbch