【问题标题】:Inheritance issue creating objects创建对象的继承问题
【发布时间】:2018-05-01 20:19:19
【问题描述】:

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

ma​​in.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)

错误

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'

此示例仅用于在实际代码中重现问题。实际的解析函数涉及复杂的逻辑。

在上面的示例中,EmployeeCustomer 类扩展了 Person 类。根据错误,Person 类的init 方法由同一类的parse 调用,期待esalary。但是Person init 方法上没有esalary 属性。

我的程序有什么问题?我想我没有正确理解继承。请纠正我。首先,我的程序结构是否正确?

【问题讨论】:

  • cls 将是 Employee 而不是 Person
  • 为什么?你能解释一下吗?
  • @What 第一个参数是什么?
  • @LokeshCherukuri 我不是 100% 确定这就是我删除评论的原因,但通常对象的每个第一个参数都指向 self。所以当你传递 cust_data 时,实际上你只传递给 self。但是我不知道边缘情况。

标签: python inheritance


【解决方案1】:

你的问题是parse调用cls(name),它只有一个参数,但clsEmployeeCustomer,不一定是Person。您必须在构建时(当您调用 parse 时)提供其他参数(工资/国家/地区),或者在未提供时为它们提供默认值。

通过parse 向构造函数提供额外需要的参数:

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

这将允许您执行以下操作:

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

或者在子构造函数中为这些参数添加默认值,这样它们就可以只用一个名字来构造:

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

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

请注意,这可能会导致您在这些属性中出现 None 值,但如果您想构建一个新的对象,例如 Employee 对象,并且不想在同一时间。

【讨论】:

  • 在 Person 的解析中添加 *args, **kwargs 并没有解决问题
  • 问题:如果 cls 是 Employee,我应该调用 cls(name,salary)。如果当时没有工资数据怎么办。如果我只想在创建员工对象后初始化薪水怎么办?
  • 然后添加默认值,就像我在第二个代码块中建议的那样。一个合理的默认值是None,例如def __init__(self, ename, esalary=None):
  • *args**kwargs 只传递传递给 parse 的额外参数。如果它们没有被传递给parse,那么就没有什么可以传递的,所以构造函数仍然接收的参数太少。如果您愿意容忍这些类中的数据缺失,请至少暂时将默认值 None 添加到您的子类中的这些额外参数中。
  • 添加默认值None 解决了这个问题。最后,您能否简单地解释一下为什么 cls 在您的答案中是 Employee ?这就是继承的工作原理吗?
【解决方案2】:

考虑以下几点:

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

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

bar = Bar()
bar.p()

这将打印“Bar”。从Bar.p 内部调用时传递给Foo.p 的类对象实际上是Bar 类,而不是Foo 类。

因此,在您的代码中,当您通过Employee.parse 调用Person.parse 时,您在Person.parsereturn cls(name) 中的行实际上是在调用Employee(name),但Employee 的init 需要2 个位置参数。

要使您的代码使用此结构,您的 __init__s 必须具有相同的签名,或者至少具有兼容的签名(例如,通过将 *args 添加到 Person__init__ 方法并将它们传递给构造函数)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多