将 class 实例分配给 python 中的 class 属性是一种好习惯吗?
is it a good practice to assign a class instance to a class attribute in python?
我正在尝试在 python 中使用 class 组合,我想知道将 class 实例分配给 class 属性是否是一个好习惯在 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()
这样的事情总是描述性的!每个 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 space 我可以改为:
class Location3D(Location):
def __init__(self, x, y, z):
self.z = z
super().__init__(x, y)
tile = Tile(Location3D(2, 3, -1))
在第二个策略中,这会失败,因为 Tile
s 总是有 Location
个对象。您必须猴子修补 Tile
才能使其工作,或者构建一个新的 Tile3D
对象,该对象可能与其他 Tile
兼容也可能不兼容。
我建议使用方法 1,因为它比方法 2 更支持封装。
为什么我们关心封装?在方法 1 中,objB
可以在单个语句中初始化,因此可以查找初始化错误的地方较少。想要使用您的 class 的代码不需要知道初始化它所需的一系列步骤;只是一个语句和一个参数列表。这种简单性导致更少的错误和更灵活和可维护的代码。
虽然封装在 Python 中不像在某些其他语言(例如 Java 中那样严格执行,但它仍然是一种很好的做法。
回复。 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)
例如,我经常使用具有 'manager' 或 'parent' 属性的 B classes。比如说,一个 Table class 有 n 个子列。每列都获得对父级 table 的引用,它们不会即时生成。
如果您基本上不需要担心 a 实例的初始值,则可以进一步细化:
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__方法。
我正在尝试在 python 中使用 class 组合,我想知道将 class 实例分配给 class 属性是否是一个好习惯在 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()
这样的事情总是描述性的!每个 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 space 我可以改为:
class Location3D(Location):
def __init__(self, x, y, z):
self.z = z
super().__init__(x, y)
tile = Tile(Location3D(2, 3, -1))
在第二个策略中,这会失败,因为 Tile
s 总是有 Location
个对象。您必须猴子修补 Tile
才能使其工作,或者构建一个新的 Tile3D
对象,该对象可能与其他 Tile
兼容也可能不兼容。
我建议使用方法 1,因为它比方法 2 更支持封装。
为什么我们关心封装?在方法 1 中,objB
可以在单个语句中初始化,因此可以查找初始化错误的地方较少。想要使用您的 class 的代码不需要知道初始化它所需的一系列步骤;只是一个语句和一个参数列表。这种简单性导致更少的错误和更灵活和可维护的代码。
虽然封装在 Python 中不像在某些其他语言(例如 Java 中那样严格执行,但它仍然是一种很好的做法。
回复。 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)
例如,我经常使用具有 'manager' 或 'parent' 属性的 B classes。比如说,一个 Table class 有 n 个子列。每列都获得对父级 table 的引用,它们不会即时生成。
如果您基本上不需要担心 a 实例的初始值,则可以进一步细化:
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__方法。