代码之家  ›  专栏  ›  技术社区  ›  FMc TLP

在Python中,可以不使用继承来实现MIXIN行为吗?

  •  4
  • FMc TLP  · 技术社区  · 15 年前

    class Mixin(object):
        def b(self): print "b()"
        def c(self): print "c()"
    
    class Foo(object):
        # Somehow mix in the behavior of the Mixin class,
        # so that all of the methods below will run and
        # the issubclass() test will be False.
    
        def a(self): print "a()"
    
    f = Foo()
    f.a()
    f.b()
    f.c()
    print issubclass(Foo, Mixin)
    

    我有一个模糊的想法,这样做与一个类装饰,但我的尝试导致混乱。我对该主题的大多数搜索都指向使用继承(或在更复杂的场景、多重继承)中实现混合行为的方向。

    8 回复  |  直到 15 年前
        1
  •  9
  •   John La Rooy    15 年前
    def mixer(*args):
        """Decorator for mixing mixins"""
        def inner(cls):
            for a,k in ((a,k) for a in args for k,v in vars(a).items() if callable(v)):
                setattr(cls, k, getattr(a, k).im_func)
            return cls
        return inner
    
    class Mixin(object):
        def b(self): print "b()"
        def c(self): print "c()"
    
    class Mixin2(object):
        def d(self): print "d()"
        def e(self): print "e()"
    
    
    @mixer(Mixin, Mixin2)
    class Foo(object):
        # Somehow mix in the behavior of the Mixin class,
        # so that all of the methods below will run and
        # the issubclass() test will be False.
    
        def a(self): print "a()"
    
    f = Foo()
    f.a()
    f.b()
    f.c()
    f.d()
    f.e()
    print issubclass(Foo, Mixin)
    

    输出:

    a()
    b()
    c()
    d()
    e()
    False
    
        2
  •  4
  •   Ignacio Vazquez-Abrams    15 年前

    可以将方法添加为函数:

    Foo.b = Mixin.b.im_func
    Foo.c = Mixin.c.im_func
    
        3
  •  3
  •   Jörg W Mittag    15 年前

    我对Python不是很熟悉,但是根据我对Python元编程的了解,您实际上可以像在Ruby中那样做。

    在Ruby中,模块基本上由两部分组成:指向方法字典的指针和指向常量字典的指针。类由三部分组成:指向方法字典的指针、指向常量字典的指针和指向超类的指针。

    M 进了一个班 C

    1. 匿名班 α 包含类 )
    2. 的方法字典和常量字典指针设置为
    3. ± 的超类指针设置为
    4. 的超类指针设置为 ±

    换言之:将一个与MIXIN共享行为的伪类注入到继承层次结构中。所以,鲁比实际上 使用遗传算法合成混合蛋白。

    我省略了上面的几个转租:首先,模块实际上并没有作为 C类 C类 “s超类”(即 C类 的单例类)超类。其次,如果Mixin本身在其他混音中混合,那么 也被包装成假类,直接插入到上面 ±

    基本上,整个MIXIN层次结构扁平化为直线,并拼接成继承链。

    实际上,Python允许您在事实发生之后更改类的超类(Ruby做的事情 dict (同样,这在Ruby中是不可能的),所以您应该能够自己实现它。

        4
  •  3
  •   Community Mohan Dere    9 年前

    编辑:修复了可能(也可能应该)被解释为bug的问题。现在,它构建了一个新的DICT,然后从类的DICT中更新它。这样可以防止MIXIN改写直接在类上定义的方法。 代码还未测试,但应该可以工作。我正忙着用自动取款机,所以我稍后再测试。 除了语法错误之外,它工作得很好。回想起来,我觉得我不喜欢它(即使在我进一步改进之后),我更喜欢它 my other solution 即使事情更复杂。这个测试代码也适用于这里,但我不会复制它。

     import inspect
    
     def add_mixins(*mixins):
         Dummy = type('Dummy', mixins, {})
         d = {}
    
         for mixin in reversed(inspect.getmro(Dummy)):
             d.update(mixin.__dict__)
    
         class WithMixins(type):
             def __new__(meta, classname, bases, classdict):
                 d.update(classdict)
                 return super(WithMixins, meta).__new__(meta, classname, bases, d)
         return WithMixins 
    

    然后像这样使用它:

     class Foo(object):
         __metaclass__ = add_mixins(Mixin1, Mixin2)
    
         # rest of the stuff
    
        5
  •  3
  •   Community Mohan Dere    9 年前

    explained by Jörg W Mittag . 所有的密码墙 if __name__=='__main__' 是测试/演示代码。实际上只有13行真正的代码。

    import inspect
    
    def add_mixins(*mixins):
        Dummy = type('Dummy', mixins, {})
        d = {}
    
        # Now get all the class attributes. Use reversed so that conflicts
        # are resolved with the proper priority. This rules out the possibility
        # of the mixins calling methods from their base classes that get overridden
        # using super but is necessary for the subclass check to fail. If that wasn't a
        # requirement, we would just use Dummy above (or use MI directly and
        # forget all the metaclass stuff).
    
        for base in reversed(inspect.getmro(Dummy)):
            d.update(base.__dict__)
    
        # Create the mixin class. This should be equivalent to creating the
        # anonymous class in Ruby.
        Mixin = type('Mixin', (object,), d)
    
        class WithMixins(type):
            def __new__(meta, classname, bases, classdict):
                # The check below prevents an inheritance cycle from forming which
                # leads to a TypeError when trying to inherit from the resulting
                # class.
                if not any(issubclass(base, Mixin) for base in bases):
                    # This should be the the equivalent of setting the superclass 
                    # pointers in Ruby.
                    bases = (Mixin,) + bases
                return super(WithMixins, meta).__new__(meta, classname, bases,
                                                       classdict)
    
        return WithMixins 
    
    
    if __name__ == '__main__':
    
        class Mixin1(object):
            def b(self): print "b()"
            def c(self): print "c()"
    
        class Mixin2(object):
            def d(self): print "d()"
            def e(self): print "e()"
    
        class Mixin3Base(object):
            def f(self): print "f()"
    
        class Mixin3(Mixin3Base): pass
    
        class Foo(object):
            __metaclass__ = add_mixins(Mixin1, Mixin2, Mixin3)
    
            def a(self): print "a()"
    
        class Bar(Foo):
            def f(self): print "Bar.f()"
    
        def test_class(cls):
            print "Testing {0}".format(cls.__name__)
            f = cls()
            f.a()
            f.b()
            f.c()
            f.d()
            f.e()
            f.f()
            print (issubclass(cls, Mixin1) or 
                   issubclass(cls, Mixin2) or
                   issubclass(cls, Mixin3))
    
        test_class(Foo)
        test_class(Bar)
    
        6
  •  0
  •   Community Mohan Dere    9 年前

    你可以装饰课堂 __getattr__ 检查MIXIN。问题是,MIXIN的所有方法总是需要一个对象,即Mixin的类型作为它们的第一个参数,所以你必须进行装饰。 __init__ 同时创建一个MIXIN对象。我相信你可以用 class decorator .

        7
  •  0
  •   eddie_c    15 年前
    from functools import partial
    class Mixin(object):
        @staticmethod
        def b(self): print "b()"
        @staticmethod
        def c(self): print "c()"
    
    class Foo(object):
        def __init__(self, mixin_cls):
            self.delegate_cls = mixin_cls
    
        def __getattr__(self, attr):
            if hasattr(self.delegate_cls, attr):
                return partial(getattr(self.delegate_cls, attr), self)
    
        def a(self): print "a()"
    
    f = Foo(Mixin)
    f.a()
    f.b()
    f.c()
    print issubclass(Foo, Mixin)
    

    这基本上使用 Mixin 分类为要保存的容器 通过将对象实例(self)作为第一个参数,其行为类似于方法的函数(而不是方法)。 __getattr__

    这通过了如下所示的简单测试。但我不能保证它会做你想做的一切。做更彻底的测试来确定。

    $ python mixin.py 
    a()
    b()
    c()
    False
    
        8
  •  0
  •   philosodad    15 年前

    例如,如果我想要 init_covers

    import cove as cov
    
    
    def init_covers(n):
        n.covers.append(cov.Cover((set([n.id]))))
        id_list = []
        for a in n.neighbors:
            id_list.append(a.id)
        n.covers.append(cov.Cover((set(id_list))))
    
    def update_degree(n):
        for a in n.covers:
            a.degree = 0
            for b in n.covers:
                if  a != b:
                    a.degree += len(a.node_list.intersection(b.node_list))    
    

    在我的酒吧类文件中,我会做: import bedg as foo

    如果我想在继承了bar的另一个类中更改foo行为,我会编写

    import bild as foo

    就像我说的,它很邋遢。

    推荐文章