What is a mixin, and why are they useful?

后端 未结 16 2117
無奈伤痛
無奈伤痛 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 01:09

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

    A mixin is a limited form of multiple inheritance. In some languages the mechanism for adding a mixin to a class is slightly different (in terms of syntax) from that of inheritance.

    In the context of Python especially, a mixin is a parent class that provides functionality to subclasses but is not intended to be instantiated itself.

    What might cause you to say, "that's just multiple inheritance, not really a mixin" is if the class that might be confused for a mixin can actually be instantiated and used - so indeed it is a semantic, and very real, difference.

    Example of Multiple Inheritance

    This example, from the documentation, is an OrderedCounter:

    class OrderedCounter(Counter, OrderedDict):
         'Counter that remembers the order elements are first encountered'
    
         def __repr__(self):
             return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
    
         def __reduce__(self):
             return self.__class__, (OrderedDict(self),)
    

    It subclasses both the Counter and the OrderedDict from the collections module.

    Both Counter and OrderedDict are intended to be instantiated and used on their own. However, by subclassing them both, we can have a counter that is ordered and reuses the code in each object.

    This is a powerful way to reuse code, but it can also be problematic. If it turns out there's a bug in one of the objects, fixing it without care could create a bug in the subclass.

    Example of a Mixin

    Mixins are usually promoted as the way to get code reuse without potential coupling issues that cooperative multiple inheritance, like the OrderedCounter, could have. When you use mixins, you use functionality that isn't as tightly coupled to the data.

    Unlike the example above, a mixin is not intended to be used on its own. It provides new or different functionality.

    For example, the standard library has a couple of mixins in the socketserver library.

    Forking and threading versions of each type of server can be created using these mix-in classes. For instance, ThreadingUDPServer is created as follows:

    class ThreadingUDPServer(ThreadingMixIn, UDPServer):
        pass
    

    The mix-in class comes first, since it overrides a method defined in UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.

    In this case, the mixin methods override the methods in the UDPServer object definition to allow for concurrency.

    The overridden method appears to be process_request and it also provides another method, process_request_thread. Here it is from the source code:

    class ThreadingMixIn:
            """Mix-in class to handle each request in a new thread."""
    
            # Decides how threads will act upon termination of the
            # main process
            daemon_threads = False
    
            def process_request_thread(self, request, client_address):
                """Same as in BaseServer but as a thread.
                In addition, exception handling is done here.
                """
                try:
                    self.finish_request(request, client_address)
                except Exception:
                    self.handle_error(request, client_address)
                finally:
                    self.shutdown_request(request)
    
            def process_request(self, request, client_address):
                """Start a new thread to process the request."""
                t = threading.Thread(target = self.process_request_thread,
                                     args = (request, client_address))
                t.daemon = self.daemon_threads
                t.start()
    

    A Contrived Example

    This is a mixin that is mostly for demonstration purposes - most objects will evolve beyond the usefulness of this repr:

    class SimpleInitReprMixin(object):
        """mixin, don't instantiate - useful for classes instantiable
        by keyword arguments to their __init__ method.
        """
        __slots__ = () # allow subclasses to use __slots__ to prevent __dict__
        def __repr__(self):
            kwarg_strings = []
            d = getattr(self, '__dict__', None)
            if d is not None:
                for k, v in d.items():
                    kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
            slots = getattr(self, '__slots__', None)
            if slots is not None:
                for k in slots:
                    v = getattr(self, k, None)
                    kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
            return '{name}({kwargs})'.format(
              name=type(self).__name__,
              kwargs=', '.join(kwarg_strings)
              )
    

    and usage would be:

    class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
        __slots__ = 'foo',
        def __init__(self, foo=None):
            self.foo = foo
            super(Foo, self).__init__()
    

    And usage:

    >>> f1 = Foo('bar')
    >>> f2 = Foo()
    >>> f1
    Foo(foo='bar')
    >>> f2
    Foo(foo=None)
    
    0 讨论(0)
  • 2020-11-22 01:11

    OP mentioned that he/she never heard of mixin in C++, perhaps that is because they are called Curiously Recurring Template Pattern (CRTP) in C++. Also, @Ciro Santilli mentioned that mixin is implemented via abstract base class in C++. While abstract base class can be used to implement mixin, it is an overkill as the functionality of virtual function at run-time can be achieved using template at compile time without the overhead of virtual table lookup at run-time.

    The CRTP pattern is described in detail here

    I have converted the python example in @Ciro Santilli's answer into C++ using template class below:

        #include <iostream>
        #include <assert.h>
    
        template <class T>
        class ComparableMixin {
        public:
            bool operator !=(ComparableMixin &other) {
                return ~(*static_cast<T*>(this) == static_cast<T&>(other));
            }
            bool operator <(ComparableMixin &other) {
                return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
            }
            bool operator >(ComparableMixin &other) {
                return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
            }
            bool operator >=(ComparableMixin &other) {
                return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
            }
            protected:
                ComparableMixin() {}
        };
    
        class Integer: public ComparableMixin<Integer> {
        public:
         Integer(int i) {
             this->i = i;
         }
         int i;
         bool operator <=(Integer &other) {
             return (this->i <= other.i);
         }
         bool operator ==(Integer &other) {
             return (this->i == other.i);
         }
        };
    
    int main() {
    
        Integer i(0) ;
        Integer j(1) ;
        //ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
        assert (i < j );
        assert (i != j);
        assert (j >  i);
        assert (j >= i);
    
        return 0;
    }
    

    EDIT: Added protected constructor in ComparableMixin so that it can only be inherited and not instantiated. Updated the example to show how protected constructor will cause compilation error when an object of ComparableMixin is created.

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

    I'd advise against mix-ins in new Python code, if you can find any other way around it (such as composition-instead-of-inheritance, or just monkey-patching methods into your own classes) that isn't much more effort.

    In old-style classes you could use mix-ins as a way of grabbing a few methods from another class. But in the new-style world everything, even the mix-in, inherits from object. That means that any use of multiple inheritance naturally introduces MRO issues.

    There are ways to make multiple-inheritance MRO work in Python, most notably the super() function, but it means you have to do your whole class hierarchy using super(), and it's considerably more difficult to understand the flow of control.

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

    It's not a Python example but in the D programing language the term mixin is used to refer to a construct used much the same way; adding a pile of stuff to a class.

    In D (which by the way doesn't do MI) this is done by inserting a template (think syntactically aware and safe macros and you will be close) into a scope. This allows for a single line of code in a class, struct, function, module or whatever to expand to any number of declarations.

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

    I think there have been some good explanations here but I wanted to provide another perspective.

    In Scala, you can do mixins as has been described here but what is very interesting is that the mixins are actually 'fused' together to create a new kind of class to inherit from. In essence, you do not inherit from multiple classes/mixins, but rather, generate a new kind of class with all the properties of the mixin to inherit from. This makes sense since Scala is based on the JVM where multiple-inheritance is not currently supported (as of Java 8). This mixin class type, by the way, is a special type called a Trait in Scala.

    It's hinted at in the way a class is defined: class NewClass extends FirstMixin with SecondMixin with ThirdMixin ...

    I'm not sure if the CPython interpreter does the same (mixin class-composition) but I wouldn't be surprised. Also, coming from a C++ background, I would not call an ABC or 'interface' equivalent to a mixin -- it's a similar concept but divergent in use and implementation.

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

    Perhaps a couple of examples will help.

    If you're building a class and you want it to act like a dictionary, you can define all the various __ __ methods necessary. But that's a bit of a pain. As an alternative, you can just define a few, and inherit (in addition to any other inheritance) from UserDict.DictMixin (moved to collections.DictMixin in py3k). This will have the effect of automatically defining all the rest of the dictionary api.

    A second example: the GUI toolkit wxPython allows you to make list controls with multiple columns (like, say, the file display in Windows Explorer). By default, these lists are fairly basic. You can add additional functionality, such as the ability to sort the list by a particular column by clicking on the column header, by inheriting from ListCtrl and adding appropriate mixins.

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