我有一个
Game
引用类 HouseRules
、Card
和 Player
。我想为每个类添加功能。
我创建了一个名为
Game
的子类以及名为 HouseRules
、Card
和 Player
的子类,除了相应父类的方法和属性之外,它们还具有新的方法和属性。我希望未更改的方法仍能按预期运行,只是它们引用每个类的更新形式。下面是我的最小可重现示例,其中只有一个类(子类)依赖于一个类(子类)。
我创建了一个类
Foo
,它接受类 Obj
的对象进行初始化。如果未给出对象,则将其设置为默认值 Obj()
。
'src.py'
class Obj:
def __init__(self, a=1, b=1):
self.a = a
self.b = b
class Foo:
def __init__(self, obj=None):
self.obj=Obj() if obj is None else obj
...
我想在新项目中构建
Foo
的功能,因此我创建了一个新文件并子类化 src.Foo
。
这个新的
Foo
的功能需要稍微复杂一点的Obj
,它有一个属性z
,所以我做了一个src.Obj
的子类。
'main.py'
import src
class Obj(src.Obj):
def __init__(self, z=0, **kwargs):
self.z = z
super().__init__(**kwargs)
class Foo(src.Foo):
def __init__(self, obj=None):
#self.obj=Obj() if obj is None else obj
super().__init__(obj)
最初我没有包含注释行。我认为如果
obj
被传递为None
,super().__init__(obj)
会将obj
变成新的改进的Obj()
。
但它变成了旧的 Obj()
并在我尝试访问其不存在的属性 z
时抛出错误。
所以,我添加了该行并修复了它。
另一个想法是替换
'src.py'
class Foo:
def __init__(self, obj=None):
self.obj=Obj() if obj is None else obj
与
'src.py'
class Foo:
def __init__(self, obj=None):
self.obj=self.default_obj if obj is None else obj
@property
def default_obj(self): return Obj()
然后复制 getter 并将其粘贴到 'main.py' 中的
Foo
中,它将从 'main.py' 返回 Obj()
。
第三个想法是使
Obj
成为一个嵌套类并将 self.obj
设置为 self.Obj()
。
'src.py'
class Foo:
class Obj:
def __init__(self, a=1, b=1):
...
def __init__(self, obj=None):
self.obj=self.Obj() if obj is None else obj
...
'main.py'
import src
class Foo(src.Foo):
class Obj(src.Foo.Obj):
def __init__(self, z=0, **kwargs):
...
def __init__(self, obj=None):
super().__init__(obj)
我听说嵌套类通常是应该避免的。另外,我注意到我无法使用
super().Obj
,我必须输入 src.Foo.Obj
,这意味着如果我更改超类的名称或其他内容,我必须确保在两个地方更改它。
但我的问题是:为了避免重复
obj
分配行(或者重复 getter 或类继承,或者让 src.py import main),是否有任何方法可以调用父类的初始化会将 obj
变成 'main.py' 中的 Obj()
而不是 'src.py' 吗?
虽然在这种最小的情况下它只是重复一个简单的行(或 getter),但我实际上有三个更新的子类。要使用我考虑过的这些技术,意味着将创建这些子类实例的所有超类方法逐字复制到子类中,以便它们引用新的子类,这会削弱使用继承的好处。
如果有比子类化项目的每个类更好的实践来实现我正在尝试的目标,请随时告诉我。
首先,不要使用嵌套类。 :-)
因此,一种简单的方法就是将您所依赖的与基相关的类作为基中的类属性
Foo
(或 Game
) - 并检索该属性来实例化每个类。然后,在 Foo
(Game
) 的子类中,只需覆盖这些属性即可。
无需搞乱属性或其他什么:
# src.py
class Obj:
...
class Foo:
# This binds this class.Obj to the "Obj" visible in
# the global scope
Obj = Obj
def __init__(self, obj=None):
self.obj=self.Obj() if obj is None else obj
...
# other.py
import src
class Obj(src.Obj):
# add extra stuff and overrides as desired!
...
class Foo(src.Foo):
# Binds the class Obj attribute to the Obj visible here!
Obj = Obj
# Just with that, if nothing changes in __init__
# you don't even need to redeclare it and call super()...:
# the original Foo.__init__ will take care of
# instantianting the derived Obj visible here!