【问题标题】:Python: Programatically create subclasses based on __init__ valuesPython:基于 __init__ 值以编程方式创建子类
【发布时间】:2013-08-26 12:19:40
【问题描述】:

我有一个基类,我想从中创建许多子类。子类的不同之处仅在于实例化期间用于调用基类的参数。下面的例子展示了如何创建一个子类Apple。有没有办法以编程方式执行此操作,而无需编写子类的 __init__ 方法?这似乎是元类的工作,但在这种情况下,我无法修改基类。

apple = {'color': 'red', 'shape': 'sphere'}
pear = {'color': 'yellow', 'shape': 'cone'}
melon = {'color': 'green', 'shape': 'prolate'}

class Fruit(object):
    def __init__(self, color, shape):
        self.color = color
        self.shape = shape        

class Apple(Fruit):
    def __init__(self):
        Fruit.__init__(self, **apple)

【问题讨论】:

  • 如果它们只是初始值不同,那么它们是子类型的意义何在?为什么不只是def apple(): return Fruit("red", "sphere")
  • 想要使用子类的动机是什么?对于Fruit 的实例,这似乎是一个明显的用例,而不是子类。
  • 内部表示不应该定义API;所以如果 OP 想要子类,可能是有原因的。
  • 是有原因的,但我承认它们非常微不足道:如果类带有其对象的名称,则使用子类的代码会更清晰,我只是好奇这是否可能.

标签: python dynamic inner-classes


【解决方案1】:

我认为您正在寻求的解决方案不存在:我所有的子类共享相同的构造函数,那么唯一使该类独一无二的是它的名称。而且我认为您不希望有一个通用构造函数来检查类名以选择要做什么。

所以我认为:

  • 要么在每个子类中重新定义构造函数,并明确将哪个参数传递给父构造函数,
  • 要么将特定的常量值放在某个类成员中,构造函数使用它来调用父构造函数

在另一个线程中看到有趣的事情:Class factory in Python

【讨论】:

    【解决方案2】:

    使用type

    class Fruit(object):
        def __init__(self, color, shape):
            self.color = color
            self.shape = shape        
    
    apple = {'color': 'red', 'shape': 'sphere'}
    pear = {'color': 'yellow', 'shape': 'cone'}
    melon = {'color': 'green', 'shape': 'prolate'}
    
    g = globals()
    for clsname, attrs in [('Apple', apple), ('Pear', pear), ('Melon', melon)]:
        def temp(attrs):
            g[clsname] = type(clsname, (Fruit,), {
                '__init__': lambda self: Fruit.__init__(self, **attrs)
            })
        temp(attrs)
    

    >>> a = Apple()
    >>> p = Pear()
    >>> m = Melon()
    >>> assert a.color == 'red' and a.shape == 'sphere'
    >>> assert p.color == 'yellow' and p.shape == 'cone'
    >>> assert m.color == 'green' and m.shape == 'prolate'
    

    【讨论】:

    • 很好的解决方案(然后我可以中止写作……)。但是我不得不不赞成修改globals()的结果。这并非在所有情况下都有效。 (locals() 也好不到哪里去。)SO 中有很多关于此的问题。以编程方式更改当前范围的唯一真正有效的方法是通过exec,这是一个丑陋的黑客攻击。
    【解决方案3】:

    查看 type() 函数。

    def make_fruit(name, kwargs):
        def my_init(self):
            Fruit.__init__(self, **kwargs)
        return type(name, (Fruit,), {'__init__': my_init})
    
    Apple = make_fruit('Apple', apple)
    

    【讨论】:

    • 更好。我喜欢使用def 而不是lambda 来定义__init__()
    • 我更喜欢这个而不是@falsetru 的回复,因为它不使用lambda。使用type 中的dict 参数来定义__init__ 是关键。谢谢。
    猜你喜欢
    • 2012-08-15
    • 1970-01-01
    • 2012-01-12
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 2018-08-22
    相关资源
    最近更新 更多