创建循环引用

时间:2012-04-30 19:17:04

标签: python reference initialization circular-reference

我需要创建几个对象(通常是循环的)相互引用的对象。一般情况下,可能涉及多个对象,但这里有一个简单的例子,我只需要一对相互引用的汽车:

class Car:
  def __init__(self, position, speed):
    self.position = position
    self.speed = speed
  def set_reference(self, other_car):
    self.other_car = other_car
  # ...


def main():
  # ...
  car1 = Car(pos1, spd1)
  car2 = Car(pos2, spd2)
  car1.set_reference(car2)
  car2.set_reference(car1)

没有引用另一辆车的汽车不是有效物品。理想情况下,我想从set_reference方法中执行__init__;这将更安全(没有使用无效对象的机会)和更清洁(所有初始化将在__init__执行,如人们所料)。

有没有实现这一目标的简洁解决方案?我不介意一次创建一对汽车,但每辆汽车都是一个独立的实体,所以它需要一个独立的实例。

我也知道循环引用对于GC很麻烦;我会处理那个。

用例:

每辆车都作为另一辆车的备用。汽车实例是“聪明的”,即他们可以做很多工作。如果一个汽车实例不知道它的备份是很烦人的,因为它可以防止汽车完成动作,而不需要每次都从外面引用。

3 个答案:

答案 0 :(得分:2)

我认为没有一种方法可以将set_reference()电话转移到__init__(),因为另一辆车可能还不存在。我可能会做这样的事情。

class Car:
  def __init__(self, position, speed):
    self.position = position
    self.speed = speed
  def set_reference(self, other_car):
    self.other_car = other_car
  @classmethod
  def create_pair(cls, car1_args, car2_args):
    car1 = cls(*car1_args)
    car2 = cls(*car2_args)
    car1.set_reference(car2)
    car2.set_reference(car1)
    return car1, car2

def main():
  car1, car2 = Car.create_pair((pos1, spd1), (pos2, spd2))

以下是如何为更大的循环参考结构扩展相同的概念:

class Car:
  # ...
  @classmethod
  def create_loop(cls, *args):
    cars = [cls(*car_args) for car_args in args]
    for i, car in enumerate(cars[:-1]):
        car.set_reference(cars[i+1])
    cars[-1].set_reference(cars[0])
    return cars

然后你可以像这样(任意数量的汽车)调用它:

car1, car2, car3 = Car.create_loop((pos1, spd1), (pos2, spd2), (pos3, spd3))

您应该使用这样的参考设置:

>>> car1.other_car is car2 and car2.other_car is car3 and car3.other_car is car1
True

答案 1 :(得分:0)

如果您真的不想使用工厂方法,那么唯一真正的选择是强制用户以后配置它们。 (在评论中记录你所说的可能需要循环的依赖关系):

class Car:
  def __init__(self, position, speed):
    self.position = position
    self.speed = speed

  @staticmethod
  def setup(*args):
    for car, next in zip(args, args[1:]):
      car.other_car = next
    args[-1].other_car = args[0]

def main():
  ...
  car1 = Car(pos1, spd1)
  car2 = Car(pos2, spd2)
  Car.setup(car1, car2)

另一种选择是拆分分组:

class CarGroup(list):
    def other_car(self, car):
        index = self.index(car)+1
        index = 0 if index >= len(self) else index
        return self[index]

class Car:
    def __init__(self, position, speed, group):
        self.position = position
        self.speed = speed
        self.group = group
        self.group.append(self)

    @property
    def other_car(self):
        return self.group.other_car(self)

def main():
    ...
    group = CarGroup()
    car1 = Car(pos1, spd1, group)
    car2 = Car(pos2, spd2, group)

答案 2 :(得分:0)

我建议将汽车数据结构与引用一系列汽车的数据结构分开。 other_car看起来并不像car中严格属于的数据,而是代表一些可迭代的汽车序列。因此,最简单和最逻辑一致的解决方案是定义汽车,然后将其放入一系列汽车中。