假设我有一个输入列表,它将生成O个对象,格式如下:
inps = [['A', 5], ['B', 2]]和O有两个子类A和B。A和B都是用一个整数初始化的--在上面的例子中是5或2 --并且有一个update(self, t)方法,所以我认为把它们归入O超类是有意义的。我可以用一个循环来完成这个程序:
Os = []
for inp in inps:
if inp[0] == 'A':
Os.append(A(inp[1]))
elif inp[0] == 'B':
Os.append(B(inp[1]))然后在运行时,
for O in Os: O.update(t)然而,我想知道是否有一种更面向对象的方法来实现这一点。我想,一种方法可能是在O类之外创建一个假的"O构造函数“:
def initO(inp):
if inp[0] == 'A':
return A(inp[1])
elif inp[0] == 'B':
return B(inp[1])
Os = [initO(inp) for inp in inps]在我看来,这更优雅,而且从所有密集的目的来看,它都给了我想要的结果;但这感觉像是对python中类系统的完全滥用。有没有更好的方法来做到这一点,也许是从O构造函数中启动A和B?
编辑:理想的做法是能够使用
Os = [O(inp) for inp in inps]同时保持O作为A和B的超类。
发布于 2017-08-12 22:48:47
您可以使用dict将名称映射到实际的类:
dct = {'A': A, 'B': B}
[dct[name](argument) for name, argument in inps]或者如果你不想要列表-理解:
dct = {'A': A, 'B': B}
Os = []
for inp in inps:
cls = dct[inp[0]]
Os.append(cls(inp[1]))发布于 2017-08-13 22:23:38
如果你真的想在父类中创建一个(直接的)子类,你可以使用特殊的__subclasses__方法:
class O(object):
def __init__(self, integer):
self.value = integer
@classmethod
def get_subclass(cls, subclassname, value):
# probably not a really good name for that method - I'm out of creativity...
subcls = next(sub for sub in cls.__subclasses__() if sub.__name__ == subclassname)
return subcls(value)
def __repr__(self):
return '{self.__class__.__name__}({self.value})'.format(self=self)
class A(O):
pass
class B(O):
pass这就像一个工厂:
>>> O.get_subclass('A', 1)
A(1)或者作为列表理解:
>>> [O.get_subclass(*inp) for inp in inps]如果您想对其进行优化,并且您知道在程序运行期间不会添加子类,则可以将子类放在从__name__映射到子类的字典中:
class O(object):
__subs = {}
def __init__(self, integer):
self.value = integer
@classmethod
def get_subclass(cls, subclassname, value):
if not cls.__subs:
cls.__subs = {sub.__name__: sub for sub in cls.__subclasses__()}
return cls.__subs[subclassname](value)您可能还可以使用__new__来实现该行为或元类,但我认为在这里使用classmethod可能更合适,因为它易于理解,并且具有更大的灵活性。
如果你不仅想要直接的子类,你可能想要检查this recipe来找到你的子类的子类(我也在我的第三方扩展包中实现了它:iteration_utilities.itersubclasses)。
发布于 2017-08-12 23:01:23
如果不了解更多关于A和B的信息,那就很难说了。但这看起来像是C语言中的switch的典型用例。Python没有switch语句,因此使用dict或dict-like构造。
如果您确定您的输入是干净的,您可以使用globals()函数直接获取您的类:
Os = [globals()[f](x) for (f, x) in inps]如果你想进行清理,你可以这样做:
allowed = {'A', 'B'}
Os = [globals()[f](x) for (f, x) in inps if f in allowed]如果您更喜欢使用固定的字典和经过清理的输入,也可以更改此解决方案:
allowed = {'A', 'B'}
classname_to_class = {k: v for (k, v) in globals().iteritems() if k in allowed}
# Now, you can have a dict mapping class names to classes without writing 'A': A, 'B': B ...或者,如果您可以为所有类定义添加前缀,您甚至可以这样做:
classname_to_class = {k[13:]: v for (k, v) in globals().iteritems() if k.startswith('SpecialPrefix'} # 13: is the length of 'SpecialPrefix'这个解决方案允许您只使用前缀命名您的类,并让字典自动填充(如果您选择的话,在去掉特殊前缀之后)。这些字典等同于这里发布的其他解决方案中的trans和dct,只是不需要手动生成字典。
与到目前为止发布的其他解决方案不同,这些解决方案减少了在类比A和B多得多的情况下出现转录错误的可能性(以及所需的样板代码量)。
https://stackoverflow.com/questions/45651512
复制相似问题