【问题标题】:How can I conditionally add in a mixin to the current class on instantiation?如何在实例化时有条件地将 mixin 添加到当前类中?
【发布时间】:2015-03-28 01:17:51
【问题描述】:

我想要一个类,它根据传递给构造函数的参数添加混入。这是我尝试过的:

class MixinOne(object):
    def print_name(self):
        print("{} is using MixinOne.".format(self.name))


class MixinTwo(object):
    def print_name(self):
        print("{} is using MixinTwo.".format(self.name))


class Sub(object):

    def __new__(cls, *args, **kwargs):

        mixin = args[1]

        if mixin == 'one':
            bases = (MixinOne,) + cls.__bases__
        elif mixin == 'two':
            bases = (MixinTwo,) + cls.__bases__

        return object.__new__(type('Sub', bases, dict(cls.__dict__)))

    def __init__(self, name, mixin):

        print('In Sub.__init__')

        self.name = name

唯一的问题似乎是 __init__ 没有被调用,所以 print_name 方法不起作用。

  1. 如何让Sub 上的__init__ 触发?

  1. 有没有更好的方法来做到这一点?

【问题讨论】:

    标签: python class mixins


    【解决方案1】:

    这是一个使用元类的好地方。您可以将 custom-mixin-inclusion 代码放在 meta 中,然后您的 Sub 类不需要有样板:

    class AutoMixinMeta(type):
        def __call__(cls, *args, **kwargs):
            try:
                mixin = kwargs.pop('mixin')
                name = "{}With{}".format(cls.__name__, mixin.__name__)
                cls = type(name, (mixin, cls), dict(cls.__dict__))
            except KeyError:
                pass
            return type.__call__(cls, *args, **kwargs)
    
    class Sub(metaclass = AutoMixinMeta):
        def __init__(self, name):
            self.name = name
    

    现在您可以创建Sub 对象并指定mixin,如下所示:

    >>> s = Sub('foo', mixin=MixinOne)
    >>> s.print_name()
    foo is using MixinOne.
    

    它会自动从 kwargs 字典中拉出,因此 __init__ 方法可以完全不知道它的存在。


    注意:Python 2 中的元类声明语法略有不同:

    class Sub(object):
        __metaclass__ = AutoMixinMeta
    
        def __init__(self, name):
        self.name = name
    

    【讨论】:

    • 不错的解决方案。我在 Eevee 的回答中使用了快速修复,但可能会重构以使用它。谢谢。
    • 我无法让上面的代码工作。我得到一个 TypeError __init__() got an unexpected keyword argument 'mixin' 。我尝试将**kwargs 添加到__init__ 方法但没有成功
    • @drstevok 您使用的是 Python 2 还是 3?
    • 我使用的是 Python 3.6.1。这是代码gist.github.com/docsteveharris/7693d78e235b0d354cdc5b56ef264280,以防我打错了
    • @drstevok 就是这样。 Python 3 不查看 __metaclass__ 属性,而是需要在声明中提供它。试试class Sub(object, metaclass = AutoMixinMeta)
    【解决方案2】:
    1. __init__ 仅在 __new__ 返回类的实例时调用。你的 on-the-fly 类继承自 mixin 和 Subparents,但不是 Sub 本身。如果您设置bases = (MixinOne, cls),它可能会起作用。

    2. 编写工厂函数(或类方法)而不是重载Sub 的构造。更好的是,只需创建一些子类,而不是在运行时创建类。 :)

    【讨论】:

    • 1.这样可行。谢谢。 2. 我有相当多的 mixin 和一些像 Sub 这样的类,我必须手动编写的子类数组并不保证这个解决方案是实用的。
    猜你喜欢
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    • 2011-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多