我正在尝试在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()
答案 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)那样强制执行,但它仍然是一种很好的做法。