工厂函数与子类
我有以下的类结构:
class A(object):
def __init__(self, filename, x):
self.x = x
# initialize A from filename
# rest of A's methods
class B(A):
def __init__(self, filename):
super(B, self).__init__(filename, 10)
# rest of B's methods
这两个类在它们的 __init__
方法中都需要传入一个文件名。然后,这个文件的内容会用来初始化对象中的一些数据。
为了方便测试,我希望能够直接通过传递数据来创建 A
或 B
的实例,而不是让对象从文件中读取数据。我想了一个办法,打算用两个工厂函数,每个函数用 classmethod
来实现,但我在 B
中的版本一直没法正常工作。以下是我目前的代码:
class A(object):
def __init__(self, x):
self.x = x
@classmethod
def construct(cls, filename, x):
a = cls(x)
# initialize a from filename
return a
@classmethod
def test_construct(cls, data, x):
a = cls(x)
# initialize a from data
return a
class B(A):
def __init__(self):
super(B, self).__init__(10)
@classmethod
def construct(cls, filename):
# should construct B from filename
@classmethod
def test_construct(cls, data):
# should construct B from data
2 个回答
1
在 __init__
里处理数据。
你可以把这个变成可选的,在你的程序中使用 filename
,在测试中使用 data
:
class A(object):
def __init__(self, x, filename=None, data=None):
if not any((filename, data)):
raise TypeError('either filename or data needs to be provided')
if all((filename, data)):
raise TypeError("both filename and data can't be provided")
self.x = x
if filename:
with open(filename, 'r') as f:
data = f.read() # just an example
编辑:无论如何,如果你想使用特殊的构造方法,这就是我会做的:
class A(object):
def __init__(self, data, x):
self.x = x
self.data = data
@classmethod
def construct(cls, filename, x):
with open(filename, 'r') as f:
data = f.read()
return cls(data, x)
class B(A):
def __init__(self, data):
super(B, self).__init__(data, 10)
@classmethod
def construct(cls, filename):
with open(filename, 'r') as f:
data = f.read()
# modify data as you wish
return cls(data)
在你的程序中调用 construct
,在测试中调用 __init__
。
2
在B类的方法中,如果想要调用它的父类的方法,可以直接使用 super()
,就像在调用基类的实例一样:
class B(A):
def __init__(self):
super(B, self).__init__(10)
@classmethod
def construct(cls, filename):
# should construct B from filename
return super(B, cls).construct(filename, 10)
编辑:正如你在评论中提到的,这里会出现一个问题,因为你在基类的构造函数中添加了一个参数。你应该避免在基类和子类之间的方法签名不兼容:一个 B
的实例 就是 一个 A
的实例,所以它应该能够接受你对 A
实例的任何方法调用。
一种解决办法是:
class B(A):
def __init__(self, x=10):
# if you're paranoid insert `assert x==10` here
super(B, self).__init__(x)
@classmethod
def construct(cls, filename):
# should construct B from filename
return super(B, cls).construct(filename, 10)
这样就又能正常工作了,但你需要确保自己不要把 'x' 直接传给 B
的实例。更好的做法可能是完全去掉 __init__
中的 x
,看起来你可以通过将它设置为类属性,或者在构造后单独设置来实现。