将类实例分配给python中的类属性是一个好习惯吗?

时间:2015-03-11 03:43:04

标签: python class object relationship composition

我正在尝试在python中使用类组合,我想知道在python中将类实例分配给类属性是否是一个好习惯。以下是我想到的两个例子。如果有经验丰富的程序员可以告诉我何时使用哪个,我感激不尽。

(1)

class A:
   def __init__(self):
      self.x = 10

class B:
   def __init__(self):
      self.y = A()

objB = B()

(2)

class A:
   def __init__(self):
      self.x = 10

class B:
   def __init__(self):
      self.y = None

objB = B()
objB.y = A()

3 个答案:

答案 0 :(得分:4)

这样的事情总是描述性的!每个B都有A个对象吗?如果是这样,那么你最好使用#1。 B是某种容器,在这种情况下是否有A个对象?使用#2。

举个例子,假设我正在构建Tile个对象的地图。每个Tile对象都有一个location,我可以进一步抽象为Location对象,如下所示:

class Tile(object):
    def __init__(self, location, *args, **kwargs):
        self.location = location

class Location(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

x2_y3 = Location(2, 3)
tile = Tile(x2_y3)
tile.location.x # 2

现在我也可以这样做:

class Tile(object):
    def __init__(self, location: "(x, y)", *args, **kwargs):
        x, y = location
        self.location = Location(x, y)

class Location(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

tile = Tile(2,3) # builds its own location object

在这种情况下,第二种策略可能更可取,但我更喜欢第一种策略,因为它更具扩展性。如果稍后我转移到3D空间,我可以改为:

class Location3D(Location):
    def __init__(self, x, y, z):
        self.z = z
        super().__init__(x, y)

tile = Tile(Location3D(2, 3, -1))

在第二个策略中,这会失败,因为Tile总是有Location个对象。您必须使用Tile进行修补才能使其正常工作,或者构建一个新的Tile3D对象,该对象可能与其他Tile一起玩得很好。

答案 1 :(得分:1)

重。 1),我会明确地传入A实例,而不是在B .__ init__中构建它。原因在于,对于非平凡的情况,您将希望为A具有不同的初始化值。或者您可能想要共享的A实例 - 否则,它是1-1关系,这是限制性的。

class A:
   def __init__(self):
      self.x = 10

class B:
   def __init__(self, instanceA):
      self.y = instanceA

#fire-and forget A
objB = B(A())

#can still do something with the instance
a = A()
objB2 = B(a)

例如,我经常使用有“经理人”的B班。或者父母'属性。比如说,一个具有n列子项的Table类。每列都有一个对父表的引用,它们不会动态地生成一个表。

如果您大多不需要担心实例的初始值,则可以进一步完善:

class B:
   #instanceA uses a default value of None
   def __init__(self, instanceA = None):
      #assign the instanceA if it was passed or build your own on the fly
      if instanceA is None:
          self.y = A()
      else:
          self.y = instanceA

P.S。不要这样做以启动默认的A实例。默认值仅计算一次,并且只要您不明确传入值,就会分配相同的A实例。

   def __init__(self, instanceA = A()):
        ....

重。 2)我不会使用事后构图。如果忘记设置.y属性怎么办?实例应尽可能不需要多次链接调用和修改才能使用(几乎是skrrgwasme所说的)。 Python具有这种灵活性,是的,并不意味着它应该在没有充分理由的情况下使用。

最后,虽然您不需要设置self.y = None,但某些Python代码分析工具只有在__init__方法上设置时才会获取实例属性。

答案 2 :(得分:0)

我建议使用方法1,因为它比方法2更强烈地支持封装。

为什么我们关心封装?在方法1中,objB可以在单个语句中初始化,因此用于追踪初始化错误的位置较少。想要使用您的课程的代码不需要知道初始化它所需的一系列步骤;只是一个语句和一个参数列表。这种简单性可以减少错误,提高代码的灵活性和可维护性。

尽管封装在Python中并没有像其他语言(如Java)那样强制执行,但它仍然是一种很好的做法。