首页 > 解决方案 > 继承类的属性名称更改。可能/不好的做法?

问题描述

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)

forclass School和 for class Company,我希望能够使用更具体的属性,例如self.studentsand self.employees,但仍然可以使用self.membersclass Entity.

Q2。这是不好的做法吗?解决这个问题的最佳方法是什么?在我的真实案例中,我使用的这个词self.members太笼统了。

标签: pythonpython-2.7

解决方案


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

原因是继承是关于可替代性的。aSchool成为 an 的意思Entity是,您可以School在任何预期 an 的代码中使用 aEntity并且它会正常工作。

例如,使用 an 的典型代码Entity可能会执行以下操作:

for member in entity.members:

如果您有一些声称是Entity(甚至通过isinstance(myschool, Entity))的东西,但它要么没有members,要么有一个空的members,因为它的实际成员存储在其他一些属性中,那么该代码就被破坏了。

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


如果您students为设置别名members因此可以在一名称下访问相同的属性,那么您确实有一个子类型: aSchool有它的studentsas members,因此它可以与需要 a 的代码一起使用Entity

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

这与定义在中的方法一样有效Entity

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

所以,这不是完全无效或任何东西。

但它可能具有误导性。

对您的代码的读者来说,添加to 会将他添加cap到是否会很明显?如果是这样,它可能没问题。如果不是,或者如果您不确定,那么您可能不应该这样做。myschool.studentsmyschool.members


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

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是作为一个School,作为一个Entity,一切都很好。


1. 我说通常是因为(不管 OO 教条怎么说)除了子类型之外还有其他原因进行子类化。但这仍然是主要原因。在这种情况下,似乎没有任何其他原因进行子类化——您没有尝试共享存储详细信息,或提供覆盖挂钩或类似的东西。

2. 实际上,您甚至可能想将abc模块拖入并使其成为抽象属性……但我不会在这里展示。


推荐阅读