【问题标题】:Inherit from Instance rather than Class [Python 3]从实例而不是类继承 [Python 3]
【发布时间】:2018-07-22 00:57:40
【问题描述】:

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

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

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 方法,该方法利用其父类实例中的属性位置,该属性是该建筑物所独有的,而不是一般值。它还需要能够使用父类 Evacuate_Building 中的方法。

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

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

我想要一些传递建筑物特定实例的方法,以便房间继承其特定属性和方法。

欢迎任何反馈、建议、批评以及完全不同的方式! 提前致谢!

【问题讨论】:

  • 我不认为python有所谓的传递特定的Building实例。相反,您可以创建一个子函数来生成房间,以及一个名为 self.rooms 的属性。还有一个函数change_location,当你调用change_location构建函数时,它会改变他属性下所有房间的位置。
  • 一旦建成,Building 几乎不会改变位置,Room 几乎不会改变Building@Marcus.Aurelianus
  • @Reblochon Masque,基本上我在回答“如果我在大楼中添加或更改某些内容,如果添加了其他属性,我需要更改每个房间的初始化和初始化代码。”,它可以是建筑物的另一个属性。我承认我的位置错了。
  • 值得注意的是,你所要求的并不是你想要的,实际上很少是你想要的,但对于某些设计来说并不是不连贯的。它被称为原型继承而不是类继承。并且可以在 Python 中做到这一点,您只需要自己构建整个机制,而不是使用语言中内置的功能。但是除了 JavaScript 之外没有其他主流语言可以进行原型继承,而且 JS 开发人员已经花费了数十年时间尝试将类继承构建到他们的语言中,因为类更适合您的对象模型
  • @Marcus.Aurelianus:听起来正确的解决方案是让建筑物具有属性,并且房间具有对包含它们的建筑物的引用。然后,__getattr__ 可用于在 Room 上进行属性访问,以便从其构建中无缝拉取数据。

标签: python class oop inheritance


【解决方案1】:

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)

【讨论】:

    【解决方案2】:

    你的设计很奇怪。房间不是建筑物,Room 为什么要继承 Building1

    房间和建筑物之间存在关系:每个房间都在建筑物中,每个建筑物都包含零个或多个房间。您可以通过 composition 表示任一关系或两者,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 中看到该更改,而无需您重复自己或做任何特别的事情。

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


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

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

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

    if is_on_fire(room):
        room.building.Evacutate_Building()
    

    1。这就是继承的含义:class Room(Building): 表示任何Room 都是Building。或者,换一种说法:任何想要Building 并获得Room 的代码都会对此感到满意。有时有理由颠覆这种含义(例如,mixin 类只是一大堆方法实现;如果你写 class Skyscraper(Building, MajorConstructionMixin):,你并没有真正声明 SkyscraperMajorConstructionMixin,因为那不是真的意味着什么;你只是借用了一些方便的实现代码)——但这仍然是你要颠覆的意思。

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

    【讨论】:

    • 很好的答案@abamert
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-27
      • 2010-11-08
      • 2019-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多