I'm working on a python project where I need to use about 20 different classes implementing a list of functionalities such as : "download", "parse", "update", etc.
Several functionalities can easily be factorized by using a superclass since the required code is the same for all of them.
But sometimes, particularly for the "parse" method, I have 10 classes that must implement the same algorithm and 10 others that need a specific algorithm.
Based on what I know about python, this behavior can be easily factorized with the use of mixins.
But here is the problem even if the "parse" algorithm is the same, I need to apply a tag to parsed entries, and this tag is specific for each class. I wonder if this is a proper way to use a class attribute that will be used only by the mixin to achieve this goal.
This chunk of code give an example of how attributes are used :
class MyMixin():
def parse(self):
print(self.tag)
...
class MyClass(MyMixin):
tag = 'mytag'
I already saw it in some frameworks (http://www.django-rest-framework.org/api-guide/generic-views/), but I'm interested to know what's the opinion of the community.
==========================
EDIT
To summarize with a concrete example, should I write this :
class MyMixin():
def do_something(self):
print(self.tag)
class MyClass(MyMixin):
tag = 'mytag'
if __name__ == '__main__':
c = MyClass()
c.do_something()
or that :
class MyMixin():
def do_something(self, tag):
print(tag)
class MyClass(MyMixin):
tag = 'mytag'
if __name__ == '__main__':
c = MyClass()
c.do_something(c.tag)
You use mixins to implement composition in other classes. This way you can delegate functionality to a mixin and reuse the mixin in other classes. Therefore with the use of python, you have two rules for mixins:
These two rules set a mixin apart from inheritance.
So to answer you're question, yes you may use parent attributes, if you make sure the parent has these attributes.
A small example (from: https://mail.python.org/pipermail/tutor/2008-May/062005.html):
class Base(object):
"""Base class for mixer classes. All mixin classes
require the classes they are mixed in with to be
instances of this class (or a subclass)."""
def __init__(self,b):
self.b = b # Mixin classes assume this attribute will be present
class MixinBPlusOne(object):
"""A mixin class that implements the print_b_plus_one
method."""
def __init__(self):
print 'MixinBPlusOne initialising'
def print_b_plus_one(self):
print self.b+1
class MixinBMinusOne(object):
"""A mixin class that implements the print_b_minus_one
method."""
def __init__(self):
print 'MixinBMinusOne initialising'
def print_b_minus_one(self):
print self.b-1
class Mixer(Base,MixinBPlusOne,MixinBMinusOne):
"""A mixer class (class that inherits some mixins), this
will work because it also inherits Base, which the
mixins expect."""
def __init__(self,b):
# I feel like I should be using super here because
# I'm using new-style classes and multiple
# inheritance, but the argument list of
# Base.__init__ differs from those of the Mixin
# classes, and this seems to work anyway.
Base.__init__(self,b)
MixinBPlusOne.__init__(self)
MixinBMinusOne.__init__(self)
class BrokenMixer(MixinBPlusOne,MixinBMinusOne):
"""This will not work because it does not inherit Base,
which the mixins expect it to do."""
pass
m = Mixer(9)
m.print_b_plus_one()
m.print_b_minus_one()
m = BrokenMixer(9)
m.print_b_plus_one() # It'll crash here.
Also the django restframework example is very good: django rest framework mixins
You can get some extra safety with the help of the abc
module:
from abc import abstractproperty, ABCMeta
class Parser(object):
__metaclass__ = ABCMeta
@abstractproperty
def tag(self):
pass
class Concrete(Parser):
@property
def tag(self):
return 'concrete'
print Concrete().tag # => prints 'concrete'
class Incomplete(Parser):
pass
# Raises error:
# TypeError: Can't instantiate abstract class Incomplete with abstract methods tag
Incomplete()
(the code for Python 3 may be slightly different)
This way the error is caught nice and early instead of when the attribute is accessed.
In addition PyCharm warns that the class is incomplete at the definition. Other static analysis tools can probably also pick this up.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With