代码之家  ›  专栏  ›  技术社区  ›  cap

继承类的属性名更改。可能/不好的做法?

  •  0
  • cap  · 技术社区  · 7 年前

    Q1。 如果我有一个非常普通的类,它的名称可以更好地用更具体的继承类表示,那么如果属性改变了它的名称,我如何从父类访问相同的方法呢?例如(不是我的真实场景,但它显示了我的意思)。

    class Entity(object):
    
      def __init__(self):
        self.members= {}
    
      ... # Methods that use self.members
    
    
    class School(Entity):
    
      def __init__(self):
        super(Entity,self).__init__(self)
    
    class Company(Entity):
    
      def __init__(self):
        super(Entity,self).__init__(self)
    

    对于 class School 以及 class Company ,我希望能够使用更具体的属性,例如 self.students self.employees ,但这仍然适用于为其定义的方法 self.members class Entity .

    Q2。 这会是不好的做法吗?最好的方法是什么?在我的真实案例中,我所用的词 self.成员 太笼统了。

    1 回复  |  直到 7 年前
        1
  •  0
  •   abarnert    7 年前

    通常,在子类中重命名属性是不好的做法。

    原因是 inheritance is about substitutability . 它对一个 School 成为一个 Entity 你可以用一个 学校 在编写的任何代码中, 实体 它会正常工作。

    例如,使用 实体 可能会这样做:

    for member in entity.members:
    

    如果你有什么东西声称是 实体 (甚至传球 isinstance(myschool, Entity) 但是它也没有 members 或者有一个空的 成员 ,因为它的实际成员存储在其他某个属性中,所以该代码被破坏。

    更一般地说,如果更改了基类和被嘲笑的类之间的接口(一组公共方法和属性),则派生类不是子类型,这意味着在第一种情况下,它通常不应该使用继承。


    如果你 students 变成一个 别名 对于 成员 ,以便在 任何一个 名字,然后是你 具有子类型:a 学校 有它的 学生 作为 成员 因此,它可以明智地与期望 实体 :

    myschool.students.append(Person(cap'))
    # ...
    for member in myschool.members:
        # now cap is going to show up here
    

    这对于在 实体 :

    def slap_everyone(self):
        for member in self.members:
            # this will include cap
            member.slap()
    
    myschool.slap_everyone()
    

    你可以用 @property .

    class Student(Entity):
        # ...
        @property
        def students(self):
            return members
        @students.setter
        def students(self, val):
            self.members = val
        @students.deleter
        def students(self):
            del self.members
    

    所以,这并不是完全无效的。

    但这有潜在的误导性。

    对于代码的读者来说,添加 cap myschool.students 将他添加到 myschool.members ?如果是的话,可能没问题。如果没有,或者你不确定,那么你可能不应该这样做。


    另一个需要考虑的是 学校 可能有多种成员:学生、教师、管理人员、辍学者,他们在旧校园里闲逛,因为他们不知道在哪里可以找到毒贩,如果这是你设计的一部分,那么你真正想要的就是 成员 成为一个属性,可能是一个只读属性, 每个子类都可以用对该子类有意义的方式定义什么是“成员”。

    class Entity(object):
        @property
        def members(self):
            return []
        def rollcall(self):
            return ', '.join(self.members)
    
    class School(Entity):
        def __init__(self):
            super(School, self).__init__()
            self.students, self.teachers = [], []
        @property
        def members(self):
            return self.students + self.teachers
    
    school = School()
    school.teachers.append('cap')
    school.students.extend(['marvel', 'america, planet'])
    print(school.rollcall())
    

    这将打印出:

    cap, marvel, america, planet
    

    school 作为一个 学校 作为一个 实体 一切都很好。


    1。我说 通常 因为(不管oo教条怎么说)除了子类型外,还有其他的子类化原因。但它仍然是 主要的 原因。在这种情况下,子类化似乎没有任何其他的原因,你没有试图共享存储细节,或者提供覆盖钩子,或者类似的东西。

    2。事实上,你 可以 甚至想拖进去 abc 模块化,使其成为抽象属性,但我不会在这里显示。