Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #42670 > unrolled thread
| Started by | andrea crotti <andrea.crotti.0@gmail.com> |
|---|---|
| First post | 2013-04-03 15:04 +0100 |
| Last post | 2013-04-03 17:16 +0000 |
| Articles | 4 — 3 participants |
Back to article view | Back to comp.lang.python
Mixin way? andrea crotti <andrea.crotti.0@gmail.com> - 2013-04-03 15:04 +0100
Re: Mixin way? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-04-03 15:47 +0000
Re: Mixin way? andrea crotti <andrea.crotti.0@gmail.com> - 2013-04-03 17:29 +0100
Re: Mixin way? Neil Cerutti <neilc@norwich.edu> - 2013-04-03 17:16 +0000
| From | andrea crotti <andrea.crotti.0@gmail.com> |
|---|---|
| Date | 2013-04-03 15:04 +0100 |
| Subject | Mixin way? |
| Message-ID | <mailman.60.1364997899.3114.python-list@python.org> |
[Multipart message — attachments visible in raw view] — view raw
I have some classes that have shared behaviours, for example in our
scenario an object can be "visited", where something that is visitable
would have some behaviour like
--8<---------------cut here---------------start------------->8---
class Visitable(Mixin):
FIELDS = {
'visits': [],
'unique_visits': 0,
}
def record_view(self, who, when):
self.visits += {'who': who, 'when': when}
self.unique_visits += 1
--8<---------------cut here---------------end--------------->8---
Where the Mixin class simply initialises the attributes:
--8<---------------cut here---------------start------------->8---
class Mixin(object):
def __init__(self, **kwargs):
for key, val in self.FIELDS.items():
setattr(self, key, val)
for key, val in kwargs.items():
if key in self.FIELDS:
setattr(self, key, val)
--8<---------------cut here---------------end--------------->8---
So now I'm not sure how to use it though.
One way would be multiple subclasses
class MyObjectBase(object):
pass
class MyObj(MyObjectBase, Visitable):
pass
for example.
This solution is probably easy, but at the same time disturbing because
MyObjectBase is semantically quite different from Visitable, so
subclassing from both seems wrong..
The other solution (which is what is partially done now) is to use
another class attribute:
class ObjectWithMixin(CouchObject):
MIXINS = [Visitable]
and then do all the smart things needed:
- at object construction time
- when setting attributes and so on..
This solution is more complicated to implement but maybe is more
flexible and more "correct", what do you think?
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-04-03 15:47 +0000 |
| Message-ID | <515c4f07$0$29966$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #42670 |
On Wed, 03 Apr 2013 15:04:51 +0100, andrea crotti wrote:
> I have some classes that have shared behaviours, for example in our
> scenario an object can be "visited", where something that is visitable
> would have some behaviour like
[snip mixins]
By the way, it's a common convention to name mixin classes as "SpamMixin".
> So now I'm not sure how to use it though.
> One way would be multiple subclasses
>
> class MyObjectBase(object):
> pass
>
> class MyObj(MyObjectBase, Visitable):
> pass
>
> for example.
What's the purpose of MyObjectBase? Either your example is too simple, or
it actually has no purpose, and you should simply write:
class MyObj(object, VisitableMixin):
# code goes here
instead of having one extra layer in the inheritance hierarchy.
> This solution is probably easy, but at the same time disturbing because
> MyObjectBase is semantically quite different from Visitable, so
> subclassing from both seems wrong..
Not really. From what you describe, this seems to be exactly the use-case
for mixins. Yes, mixins are by definition somewhat of an abuse of Object-
Oriented concepts. Mixins don't so much represent a "type of thing" as a
convenient bundle of encapsulated behaviour and/or state.
E.g. MyObj represents some sort of "MyObj thing" (or at least it
*should*, if your OO classes are well-planned), but Visitable(Mixin) does
not represent a thing at all.
But here's a way to look at it to justify the concept of mixins. Think of
regular classes and subclasses as representing trees of descent, like
biological groups:
Animal -> Mammal -> Rodent -> Porcupine
Animal -> Monotreme -> Echidna
Then mixins represent convergent evolution of some trait:
Animal -> Mammal -> Rodent + QuillsMixin -> Porcupine
Animal -> Monotreme + QuillsMixin -> Echidna
> The other solution (which is what is partially done now) is to use
> another class attribute:
>
> class ObjectWithMixin(CouchObject):
> MIXINS = [Visitable]
>
> and then do all the smart things needed:
> - at object construction time
> - when setting attributes and so on..
>
> This solution is more complicated to implement but maybe is more
> flexible and more "correct", what do you think?
I think that's worse.
You seem to have *almost* stumbled onto the design pattern known as
"composition" or "delegation", only not quite.
Normal class-based design models a "is-a" relationship. "Lassie is a
Dog", so we would do:
lassie = Dog()
Composition models a "has-a" relationship. For example, both cars and
boats have engines, so we might be tempted to use a mixin:
class Car(EngineMixin):
pass
class Boat(EngineMixin):
pass
which isn't an awful solution. But a conceptually cleaner solution might
be to do this:
class Car:
def __init__(self):
self.engine = Engine()
def go(self):
print "Fasten your seat belt"
self.engine.start()
self.handbrake = False
self.change_gear("drive")
self.accelerate()
class Boat:
def __init__(self):
self.engine = Engine()
def go(self): ...
and then both cars and boats can *delegate* behaviour to the engine
object.
So, if you think of "Visitable" as a gadget that can be strapped onto
your MyObj as a component, then composition is probably a better design.
But if you think of "Visitable" as a mere collection of behaviour and
state, then a mixin is probably a better design.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | andrea crotti <andrea.crotti.0@gmail.com> |
|---|---|
| Date | 2013-04-03 17:29 +0100 |
| Message-ID | <mailman.64.1365006544.3114.python-list@python.org> |
| In reply to | #42680 |
[Multipart message — attachments visible in raw view] — view raw
2013/4/3 Steven D'Aprano <steve+comp.lang.python@pearwood.info>
> [snip]
>
> So, if you think of "Visitable" as a gadget that can be strapped onto
> your MyObj as a component, then composition is probably a better design.
> But if you think of "Visitable" as a mere collection of behaviour and
> state, then a mixin is probably a better design.
>
>
Well I can explain better the situation to make it more clear.
We are using CouchDb and so far it has been (sigh) a brutal manipulation of
dictionaries everywhere, with code duplication and so on.
Now I wanted to encapsulate all the entities in the DB in proper objects,
so I have a CouchObject:
class CouchObject(object) :
"""
Encapsulate an object which has the ability to be saved to a couch
database.
"""
#: list of fields that get filled in automatically if not passed in
AUTO = ['created_datetime', 'doc_type', '_id', '_rev']
#: dictionary with some extra fields with default values if not
# passed in the constructor the default value gets set to the attribute
DEFAULTS = {}
REQUIRED = []
OPTIONAL = []
TO_RESOLVE = []
MIXINS = []
Where every subclass can redefine these attributes to get something
done automatically by the constructor for convenience.
Now however there is a lot of behaviour shared between them, so I want
to encapsulate it out in different places.
I think the MIXINS as I would use it is the normal composition
pattern, the only difference is that the composition is done per class
and not per object (again for lazyness reasons), right?
Probably subclassing might be fine as well, and makes it simpler, but
I don't like too much to do subclass from multiple classes...
[toc] | [prev] | [next] | [standalone]
| From | Neil Cerutti <neilc@norwich.edu> |
|---|---|
| Date | 2013-04-03 17:16 +0000 |
| Message-ID | <as36enFrk8aU1@mid.individual.net> |
| In reply to | #42683 |
On 2013-04-03, andrea crotti <andrea.crotti.0@gmail.com> wrote:
> Well I can explain better the situation to make it more clear.
>
> We are using CouchDb and so far it has been (sigh) a brutal
> manipulation of dictionaries everywhere, with code duplication
> and so on.
>
> Now I wanted to encapsulate all the entities in the DB in
> proper objects, so I have a CouchObject:
>
>
> class CouchObject(object) :
> """
> Encapsulate an object which has the ability to be saved to a couch
> database.
> """
> #: list of fields that get filled in automatically if not passed in
> AUTO = ['created_datetime', 'doc_type', '_id', '_rev']
> #: dictionary with some extra fields with default values if not
> # passed in the constructor the default value gets set to the attribute
> DEFAULTS = {}
> REQUIRED = []
> OPTIONAL = []
> TO_RESOLVE = []
> MIXINS = []
>
> Where every subclass can redefine these attributes to get
> something done automatically by the constructor for
> convenience.
Hopefully someone with experience with them can help you further,
but this seems like a job for a metaclass.
--
Neil Cerutti
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web