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?
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With