2

I have a class Message and a class MessageCollection which obviously depends on Message. I'd like to do this:

m1 = Message()
m2 = Message()

collection = m1 + m2
isinstance(collection, MessageCollection) # True

The problem is that I have to overload __add__ operator in Message class and create a new MessageCollection instance there:

class Message:

    def __add__(self, msg_or_collection):
        if isinstance(msg_or_collection, Message):
            return MessageCollection([self, msg_or_collection])
        elif isinstance(msg_or_collection, MessageCollection)
            return msg_or_collection.append(self)
        raise TypeError("can't add %s to Message" % type(msg_or_collection))

thus creating an ugly circular dependency. Is there any way to avoid it? Maybe my design is wrong and there are other approaches to this?

EDIT:

I already have overloaded MessageCollection's __add__operator indeed, so I can do

collection1 = MessageCollection()
collection2 = collection1 + m1 + m2.

I just want to make it a bit nicer...

EDIT 2:

I eventually removed the dependency and left the Message class without the __add__ overloading.

However.... I've been thinking about it and to me it makes perfect sense to have the syntax collection_of_objects = object1 + object2. It expresses the notion of "I had an apple, then I bought another one, so now I have a collection of two apples". Maybe we should think of an object being a special case of a collection where the number of objects is 1? In this case, the object should inherit from (or be decorated by) a "listable" class...

anyway, thanks for your answers folks! I'll leave the question open to see if it generates further debate ;)

4
  • 1
    So you want m1 + m2 to create a MessageCollection, but you don't want Message to depend on MessageCollection? Impossible. Commented Aug 24, 2014 at 12:48
  • 1
    I don't think having m1 + m2 returning a MessageCollection is nicer. Because it will tightly couple MessageCollection to 'Message' preventing you from using other types of containers in the future or even from ever extending MessageCollection. Having __add__ on MessageCollection is the correct way to go Commented Aug 24, 2014 at 13:00
  • I don't see why overloading __add__ would prevent me from using other containers Commented Aug 24, 2014 at 13:02
  • 2
    I mean that once you have MessageQueue and MessageHeap then suddenly it is not as clear why m1 + m2 should return a MessageCollection and not a MessageQueue.. Commented Aug 24, 2014 at 18:02

2 Answers 2

2

I would avoid using a MessageCollection and try to use a regular python list. They are very robust and in my opinion 99% of the time better then trying to build a new collection type.

The idea that collections are generic and do not assume anything about their content while the content doesn't assume anything about its container is a really powerful abstraction. IMHO You should really avoid messing with that unless there is a very compelling reason to.

If you really must need a MessageCollection class. Then put the __add__ operator on it. It means you build the MessageCollection empty and can add to it (extend it) using +. This is similar to the list syntax.

Sign up to request clarification or add additional context in comments.

3 Comments

Because MessageCollection has more attributes and methods specific to my logic. Message can still go inside any other container... I just want to make my code more readable by allowing collection = m1 + m2 + m3 syntax.
If there is one thing that will kill readability faster than operator overloading, I've not seen it.
@msw I understand your point, but once you learn how operators act on that particular object, it's clear that code becomes more compact and neat. For example, Celery uses | operator to chain tasks. You can still use the chain method, but using | makes it shorter and more readable, especially when you want to concatenate operations.
0

Stop writing classes. Really. This is a common anti-pattern in Python which bites many who are well learned in Java, and C++ to a lesser degree. Part of the reason for this is nothing in Java happens outside a containing class. When you have a class with no state and you are not stuck in Java, forget the class.

In Java and C++, it is even more important to create collection classes because neither language has lists, sets, sequences, dictionaries, and such. In Java/C++ any aggregate types you have to write a class for, load a library or instantiate a template. This increases the frequency of wanting to write a class: If I want a vector of Message * then I'm going to have to make one as you did:

collection1 = MessageCollection()
m1 = Message()
m2 = Message()
collection2 = collection1 + m1 + m2

class Message:
    def __add__(self, msg_or_collection):
        if isinstance(msg_or_collection, Message):
            return MessageCollection([self, msg_or_collection])
        elif isinstance(msg_or_collection, MessageCollection)
        return msg_or_collection.append(self)
    raise TypeError("can't add %s to Message" % type(msg_or_collection))

But there are a few things which could be much better. Code that you don't write has no defects in it, worse still every time you use an isinstance you've increased code coupling so that MessageCollection and Message become entwined and inseparable. So we'll drop Message.add this will remove the + you are so keen on. But we can wave that away since you specify no behavior for message collection aside from being used to chain up Messages we can already do that easily

m1 = Message()
m2 = Message() 
messages = [m1, m2]

We've lost a class method with a polymorphic return type based on the polymorphic input type. We now know the time complexity of messages.append(m3) because we are well familiar with lists, and all the slicing, iteration, manipulation and changes we'd want to make on messages is already available because messages is-a list; MessageCollection might be. And if having to use messages.append(m3) really bums you out, you still have

messages += m3

built in.

1 Comment

Maybe it's not clear from my question: MessageCollection is NOT simply a list. It has its own methods and properties, so I NEED a class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.