What is a mixin, and why are they useful?

后端 未结 16 2116
無奈伤痛
無奈伤痛 2020-11-22 00:18

In \"Programming Python\", Mark Lutz mentions \"mixins\". I\'m from a C/C++/C# background and I have not heard the term before. What is a mixin?

Reading between the

相关标签:
16条回答
  • 2020-11-22 00:58

    First, you should note that mixins only exist in multiple-inheritance languages. You can't do a mixin in Java or C#.

    Basically, a mixin is a stand-alone base type that provides limited functionality and polymorphic resonance for a child class. If you're thinking in C#, think of an interface that you don't have to actually implement because it's already implemented; you just inherit from it and benefit from its functionality.

    Mixins are typically narrow in scope and not meant to be extended.

    [edit -- as to why:]

    I suppose I should address why, since you asked. The big benefit is that you don't have to do it yourself over and over again. In C#, the biggest place where a mixin could benefit might be from the Disposal pattern. Whenever you implement IDisposable, you almost always want to follow the same pattern, but you end up writing and re-writing the same basic code with minor variations. If there were an extendable Disposal mixin, you could save yourself a lot of extra typing.

    [edit 2 -- to answer your other questions]

    What separates a mixin from multiple inheritance? Is it just a matter of semantics?

    Yes. The difference between a mixin and standard multiple inheritance is just a matter of semantics; a class that has multiple inheritance might utilize a mixin as part of that multiple inheritance.

    The point of a mixin is to create a type that can be "mixed in" to any other type via inheritance without affecting the inheriting type while still offering some beneficial functionality for that type.

    Again, think of an interface that is already implemented.

    I personally don't use mixins since I develop primarily in a language that doesn't support them, so I'm having a really difficult time coming up with a decent example that will just supply that "ahah!" moment for you. But I'll try again. I'm going to use an example that's contrived -- most languages already provide the feature in some way or another -- but that will, hopefully, explain how mixins are supposed to be created and used. Here goes:

    Suppose you have a type that you want to be able to serialize to and from XML. You want the type to provide a "ToXML" method that returns a string containing an XML fragment with the data values of the type, and a "FromXML" that allows the type to reconstruct its data values from an XML fragment in a string. Again, this is a contrived example, so perhaps you use a file stream, or an XML Writer class from your language's runtime library... whatever. The point is that you want to serialize your object to XML and get a new object back from XML.

    The other important point in this example is that you want to do this in a generic way. You don't want to have to implement a "ToXML" and "FromXML" method for every type that you want to serialize, you want some generic means of ensuring that your type will do this and it just works. You want code reuse.

    If your language supported it, you could create the XmlSerializable mixin to do your work for you. This type would implement the ToXML and the FromXML methods. It would, using some mechanism that's not important to the example, be capable of gathering all the necessary data from any type that it's mixed in with to build the XML fragment returned by ToXML and it would be equally capable of restoring that data when FromXML is called.

    And.. that's it. To use it, you would have any type that needs to be serialized to XML inherit from XmlSerializable. Whenever you needed to serialize or deserialize that type, you would simply call ToXML or FromXML. In fact, since XmlSerializable is a fully-fledged type and polymorphic, you could conceivably build a document serializer that doesn't know anything about your original type, accepting only, say, an array of XmlSerializable types.

    Now imagine using this scenario for other things, like creating a mixin that ensures that every class that mixes it in logs every method call, or a mixin that provides transactionality to the type that mixes it in. The list can go on and on.

    If you just think of a mixin as a small base type designed to add a small amount of functionality to a type without otherwise affecting that type, then you're golden.

    Hopefully. :)

    0 讨论(0)
  • 2020-11-22 00:59

    This answer aims to explain mixins with examples that are:

    • self-contained: short, with no need to know any libraries to understand the example.

    • in Python, not in other languages.

      It is understandable that there were examples from other languages such as Ruby since the term is much more common in those languages, but this is a Python thread.

    It shall also consider the controversial question:

    Is multiple inheritance necessary or not to characterize a mixin?

    Definitions

    I have yet to see a citation from an "authoritative" source clearly saying what is a mixin in Python.

    I have seen 2 possible definitions of a mixin (if they are to be considered as different from other similar concepts such as abstract base classes), and people don't entirely agree on which one is correct.

    The consensus may vary between different languages.

    Definition 1: no multiple inheritance

    A mixin is a class such that some method of the class uses a method which is not defined in the class.

    Therefore the class is not meant to be instantiated, but rather serve as a base class. Otherwise the instance would have methods that cannot be called without raising an exception.

    A constraint which some sources add is that the class may not contain data, only methods, but I don't see why this is necessary. In practice however, many useful mixins don't have any data, and base classes without data are simpler to use.

    A classic example is the implementation of all comparison operators from only <= and ==:

    class ComparableMixin(object):
        """This class has methods which use `<=` and `==`,
        but this class does NOT implement those methods."""
        def __ne__(self, other):
            return not (self == other)
        def __lt__(self, other):
            return self <= other and (self != other)
        def __gt__(self, other):
            return not self <= other
        def __ge__(self, other):
            return self == other or self > other
    
    class Integer(ComparableMixin):
        def __init__(self, i):
            self.i = i
        def __le__(self, other):
            return self.i <= other.i
        def __eq__(self, other):
            return self.i == other.i
    
    assert Integer(0) <  Integer(1)
    assert Integer(0) != Integer(1)
    assert Integer(1) >  Integer(0)
    assert Integer(1) >= Integer(1)
    
    # It is possible to instantiate a mixin:
    o = ComparableMixin()
    # but one of its methods raise an exception:
    #o != o 
    

    This particular example could have been achieved via the functools.total_ordering() decorator, but the game here was to reinvent the wheel:

    import functools
    
    @functools.total_ordering
    class Integer(object):
        def __init__(self, i):
            self.i = i
        def __le__(self, other):
            return self.i <= other.i
        def __eq__(self, other):
            return self.i == other.i
    
    assert Integer(0) < Integer(1)
    assert Integer(0) != Integer(1)
    assert Integer(1) > Integer(0)
    assert Integer(1) >= Integer(1)
    

    Definition 2: multiple inheritance

    A mixin is a design pattern in which some method of a base class uses a method it does not define, and that method is meant to be implemented by another base class, not by the derived like in Definition 1.

    The term mixin class refers to base classes which are intended to be used in that design pattern (TODO those that use the method, or those that implement it?)

    It is not easy to decide if a given class is a mixin or not: the method could be just implemented on the derived class, in which case we're back to Definition 1. You have to consider the author's intentions.

    This pattern is interesting because it is possible to recombine functionalities with different choices of base classes:

    class HasMethod1(object):
        def method(self):
            return 1
    
    class HasMethod2(object):
        def method(self):
            return 2
    
    class UsesMethod10(object):
        def usesMethod(self):
            return self.method() + 10
    
    class UsesMethod20(object):
        def usesMethod(self):
            return self.method() + 20
    
    class C1_10(HasMethod1, UsesMethod10): pass
    class C1_20(HasMethod1, UsesMethod20): pass
    class C2_10(HasMethod2, UsesMethod10): pass
    class C2_20(HasMethod2, UsesMethod20): pass
    
    assert C1_10().usesMethod() == 11
    assert C1_20().usesMethod() == 21
    assert C2_10().usesMethod() == 12
    assert C2_20().usesMethod() == 22
    
    # Nothing prevents implementing the method
    # on the base class like in Definition 1:
    
    class C3_10(UsesMethod10):
        def method(self):
            return 3
    
    assert C3_10().usesMethod() == 13
    

    Authoritative Python occurrences

    At the official documentatiton for collections.abc the documentation explicitly uses the term Mixin Methods.

    It states that if a class:

    • implements __next__
    • inherits from a single class Iterator

    then the class gets an __iter__ mixin method for free.

    Therefore at least on this point of the documentation, mixin does not not require multiple inheritance, and is coherent with Definition 1.

    The documentation could of course be contradictory at different points, and other important Python libraries might be using the other definition in their documentation.

    This page also uses the term Set mixin, which clearly suggests that classes like Set and Iterator can be called Mixin classes.

    In other languages

    • Ruby: Clearly does not require multiple inheritance for mixin, as mentioned in major reference books such as Programming Ruby and The Ruby programming Language

    • C++: A virtual method that is set =0 is a pure virtual method.

      Definition 1 coincides with the definition of an abstract class (a class that has a pure virtual method). That class cannot be instantiated.

      Definition 2 is possible with virtual inheritance: Multiple Inheritance from two derived classes

    0 讨论(0)
  • 2020-11-22 01:02

    I think previous responses defined very well what MixIns are. However, in order to better understand them, it might be useful to compare MixIns with Abstract Classes and Interfaces from the code/implementation perspective:

    1. Abstract Class

    • Class that needs to contain one or more abstract methods

    • Abstract Class can contain state (instance variables) and non-abstract methods

    2. Interface

    • Interface contains abstract methods only (no non-abstract methods and no internal state)

    3. MixIns

    • MixIns (like Interfaces) do not contain internal state (instance variables)
    • MixIns contain one or more non-abstract methods (they can contain non-abstract methods unlike interfaces)

    In e.g. Python these are just conventions, because all of the above are defined as classes. However, the common feature of both Abstract Classes, Interfaces and MixIns is that they should not exist on their own, i.e. should not be instantiated.

    0 讨论(0)
  • 2020-11-22 01:05

    A mixin is a special kind of multiple inheritance. There are two main situations where mixins are used:

    1. You want to provide a lot of optional features for a class.
    2. You want to use one particular feature in a lot of different classes.

    For an example of number one, consider werkzeug's request and response system. I can make a plain old request object by saying:

    from werkzeug import BaseRequest
    
    class Request(BaseRequest):
        pass
    

    If I want to add accept header support, I would make that

    from werkzeug import BaseRequest, AcceptMixin
    
    class Request(AcceptMixin, BaseRequest):
        pass
    

    If I wanted to make a request object that supports accept headers, etags, authentication, and user agent support, I could do this:

    from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
    
    class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
        pass
    

    The difference is subtle, but in the above examples, the mixin classes weren't made to stand on their own. In more traditional multiple inheritance, the AuthenticationMixin (for example) would probably be something more like Authenticator. That is, the class would probably be designed to stand on its own.

    0 讨论(0)
  • 2020-11-22 01:07

    Mixins is a concept in Programming in which the class provides functionalities but it is not meant to be used for instantiation. Main purpose of Mixins is to provide functionalities which are standalone and it would be best if the mixins itself do not have inheritance with other mixins and also avoid state. In languages such as Ruby, there is some direct language support but for Python, there isn't. However, you could used multi-class inheritance to execute the functionality provided in Python.

    I watched this video http://www.youtube.com/watch?v=v_uKI2NOLEM to understand the basics of mixins. It is quite useful for a beginner to understand the basics of mixins and how they work and the problems you might face in implementing them.

    Wikipedia is still the best: http://en.wikipedia.org/wiki/Mixin

    0 讨论(0)
  • 2020-11-22 01:07

    I read that you have a c# background. So a good starting point might be a mixin implementation for .NET.

    You might want to check out the codeplex project at http://remix.codeplex.com/

    Watch the lang.net Symposium link to get an overview. There is still more to come on documentation on codeplex page.

    regards Stefan

    0 讨论(0)
提交回复
热议问题