首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从父类启动子类

从父类启动子类
EN

Stack Overflow用户
提问于 2017-08-12 22:45:35
回答 4查看 205关注 0票数 2

假设我有一个输入列表,它将生成O个对象,格式如下:

代码语言:javascript
复制
inps = [['A', 5], ['B', 2]]

和O有两个子类A和B。A和B都是用一个整数初始化的--在上面的例子中是5或2 --并且有一个update(self, t)方法,所以我认为把它们归入O超类是有意义的。我可以用一个循环来完成这个程序:

代码语言:javascript
复制
Os = []
for inp in inps:
    if inp[0] == 'A':
        Os.append(A(inp[1]))
    elif inp[0] == 'B':
        Os.append(B(inp[1]))

然后在运行时,

代码语言:javascript
复制
for O in Os: O.update(t)

然而,我想知道是否有一种更面向对象的方法来实现这一点。我想,一种方法可能是在O类之外创建一个假的"O构造函数“:

代码语言:javascript
复制
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?

编辑:理想的做法是能够使用

代码语言:javascript
复制
Os = [O(inp) for inp in inps]

同时保持O作为A和B的超类。

EN

回答 4

Stack Overflow用户

发布于 2017-08-12 22:48:47

您可以使用dict将名称映射到实际的类:

代码语言:javascript
复制
dct = {'A': A, 'B': B}

[dct[name](argument) for name, argument in inps]

或者如果你不想要列表-理解:

代码语言:javascript
复制
dct = {'A': A, 'B': B}
Os = []
for inp in inps:
    cls = dct[inp[0]]
    Os.append(cls(inp[1]))
票数 2
EN

Stack Overflow用户

发布于 2017-08-13 22:23:38

如果你真的想在父类中创建一个(直接的)子类,你可以使用特殊的__subclasses__方法:

代码语言:javascript
复制
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

这就像一个工厂:

代码语言:javascript
复制
>>> O.get_subclass('A', 1)
A(1)

或者作为列表理解:

代码语言:javascript
复制
>>> [O.get_subclass(*inp) for inp in inps]

如果您想对其进行优化,并且您知道在程序运行期间不会添加子类,则可以将子类放在从__name__映射到子类的字典中:

代码语言:javascript
复制
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)。

票数 1
EN

Stack Overflow用户

发布于 2017-08-12 23:01:23

如果不了解更多关于AB的信息,那就很难说了。但这看起来像是C语言中的switch的典型用例。Python没有switch语句,因此使用dictdict-like构造。

如果您确定您的输入是干净的,您可以使用globals()函数直接获取您的类:

代码语言:javascript
复制
Os = [globals()[f](x) for (f, x) in inps]

如果你想进行清理,你可以这样做:

代码语言:javascript
复制
allowed = {'A', 'B'}
Os = [globals()[f](x) for (f, x) in inps if f in allowed]

如果您更喜欢使用固定的字典和经过清理的输入,也可以更改此解决方案:

代码语言:javascript
复制
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 ...

或者,如果您可以为所有类定义添加前缀,您甚至可以这样做:

代码语言:javascript
复制
classname_to_class = {k[13:]: v for (k, v) in globals().iteritems() if k.startswith('SpecialPrefix'} # 13: is the length of 'SpecialPrefix'

这个解决方案允许您只使用前缀命名您的类,并让字典自动填充(如果您选择的话,在去掉特殊前缀之后)。这些字典等同于这里发布的其他解决方案中的transdct,只是不需要手动生成字典。

与到目前为止发布的其他解决方案不同,这些解决方案减少了在类比A和B多得多的情况下出现转录错误的可能性(以及所需的样板代码量)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45651512

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档