从实例继承,而不是从类继承[Python 3]

时间:2018-07-22 00:57:40

标签: python class oop inheritance

我想启动一个子类的多个实例,每个实例都从一个类的特定实例而不是常规类中获取其属性。

例如,如果我有建筑物和房间,则每个房间都必须属于建筑物的特定实例,并采用其属性和方法。

class Building:
     def __init__(self, buildingName, location):
        self.buildingName = buildingName
        self.location = location
        # lots more intersting stuff

    def Evacuate_Building(self):
        # do some stuff
        pass

class Room(Building):
     def __init__(self, roomName, roomType):
        self.roomName = roomName
        self.roomType = roomType
        # lots more intersting stuff

    def Directions(self, Current_Location):
        '''do some stuff involving this.roomName and this.location which comes from the specific instance of the class'''
        pass

假设我有三座建筑:“北”,“南”和“西”。

每个建筑物都有自己的房间。

仅看北楼,就有房间“ N1”,“ N2”,“ N3”,“ N4”。

在我的代码中,我将首先遍历并启动三个建筑物,然后再启动房间,以及如何将它们链接回其对应的建筑物。

这使我可以使用Directions方法,该方法从其父类实例中提取属性位置,该属性是该Building所独有的,而不是常规值。它还需要能够使用父类Evacuate_Building中的方法。

我每次使用super(buildingName,location)或Building .__ init __(self,buildingName,location)在更标准的设置中初始化房间时,都可以通过传递位置数据来解决此问题,但这意味着我必须编写每个房间的位置,如果在建筑物中添加或更改某些内容,则需要更改每个房间的初始化和初始化代码(如果添加了其他属性)。

我还可以通过将Building的实例作为参数传递给init,然后进行this.location = building.location来复制属性,但这也具有与上述相同的问题,而且我没有方法这样。

我想通过某种方式传递建筑物的特定实例,以便房间继承其特定属性和方法。

欢迎任何反馈,建议,批评,完全不同的方式进行操作! 提前谢谢!

3 个答案:

答案 0 :(得分:4)

Room不是Building ==> Room不应是Building的子类。
Building有许多Room ==>使用组成。

一种可能的方法是让每个Building都具有Room的集合

class Building:
    def __init__(self, name, location):
        self.name = name
        self.location = location
        self.rooms = []            # or a set as in @Abamert's reply
                                   # or dict if you want to address it by floor and number, maybe?
    def add_room(self, room):
        self.rooms.append(room)

class Room:
    def __init__(self, name, model):
        self.name = roomName
        self.model = model

您也许还可以让房间引用其所在的建筑物;此引用可以是在向建筑物添加房间时分配的属性:

class Building:
    ...
    def add_room(self, room):
        room.bldg = self
        self.rooms.append(room)

答案 1 :(得分:2)

您的设计很奇怪。房间不是建筑物,所以Room为什么要从Building继承? 1

房间和建筑物之间有一个 关系:每个房间都在建筑物中,每个建筑物包含零个或多个房间。您可以通过组成(即通过添加成员)来表示 2 中的一种或两种关系。

class Room:
    def __init__(self, building, roomName, roomType):
        self.building = building
        self.roomName = roomName
        self.roomType = roomType
        # lots more intersting stuff

或者:

class Building:
    def __init__(self, buildingName, location):
        self.buildingName = buildingName
        self.location = location
        self.rooms = set()
    def add(self, room):
        self.rooms.add(room)

如果需要,您甚至可以Room.__init__打给building.add(self)的电话。


请注意,这确实为您提供了所需的内容:每个Room与特定的Building实例self.building相关,而不与Building类相关。

因此,您不必为每个Room编写位置,只需将相同的Building实例传递给所有实例。而且,如果我在Building中添加或更改某些内容,则该建筑物中的每个Room都会在其self.building中看到该更改,而无需重复自己或做任何特殊的事情。

甚至不只是静态地在代码中。您可以在运行时使用Building移动self.location = new_location。然后,对于该Room中的每个Building,其self.building.location现在为new_location


此外,您通常不希望Room支持Building的方法。在Evacuate_Building上调用Room没有多大意义。可能会呼叫Evacuate_Room。在这种情况下,您的Building可能会执行以下操作:

def Evacuate_Building(self):
    for room in self.rooms:
        room.Evacuate_Room()

但是,如果您发现Room着火了,因此需要疏散Building所在的整个Room,包括所有房间,该怎么办?很简单:您撤消了Room的{​​{1}}属性:

building

1。这就是继承的含义:if is_on_fire(room): room.building.Evacutate_Building() 表示任何class Room(Building):Room。或者,换一种说法:任何需要Building并得到Building的代码都会对此感到满意。有时有理由颠覆这种含义(例如,mixin类只是一大堆方法实现;如果您编写Room,实际上并不是在声明class Skyscraper(Building, MajorConstructionMixin):是{{1} },因为这实际上没有任何意义;您只是在借用一些方便的实现代码),但这仍然是您所颠覆的意思。

2。请注意,如果在两个方向上都添加引用,则会有一个引用周期。这不是非法的,甚至通常不是问题;这仅意味着当您放弃建筑物及其所有房间时,CPython垃圾收集器将不会找到它们并将其立即删除,直到下一次收集器运行为止。虽然没什么大不了的,但是大多数程序并不需要双向的关系,因此您应该看看是否可以通过消除一个来简化设计。

答案 2 :(得分:0)

虽然我意识到这是一个老问题,但我认为从实例继承到类的主要问题值得关注。

我不确定之前的答案是否完全理解您从实例继承的重要性的示例,其中的功能无法提前详细说明,根据定义,类需要这样做。

在另一个问题中,我找到了一个描述 the module Acquisition 的答案,它正是这样做的,继承自一个实例。可能值得一看:Inheriting from instance in Python