在Python中重载operator __add__时避免循环依赖

时间:2014-08-24 12:31:21

标签: python

我有一个Message类和一个MessageCollection类,它显然取决于Message。 我想这样做:

m1 = Message()
m2 = Message()

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

问题是我必须在Message类中重载__add__运算符并在那里创建一个新的MessageCollection实例:

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))
因而产生了丑陋的循环依赖。有什么办法可以避免吗?也许我的设计是错误的,还有其他方法吗?

编辑:

我确实已经超载了MessageCollection' __add__运算符,所以我可以做

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

我只想让它更好......

编辑2:

我最终删除了依赖项并离开了Message类而没有__add__重载。

然而......我一直在考虑它,对我而言,拥有语法collection_of_objects = object1 + object2是完全合理的。它表达了"我有一个苹果的概念,然后我买了另一个,所以现在我有两个苹果的集合"。也许我们应该想到一个对象是一个集合的特殊情况,其中对象的数量是1?在这种情况下,对象应该从一个" listable"继承(或被装饰)。类...

无论如何,谢谢你们的回答!我将问题保持开放,看是否会产生进一步的争论;)

2 个答案:

答案 0 :(得分:2)

我会避免使用MessageCollection并尝试使用常规的python列表。它们非常强大,在我看来99%的时间比尝试构建新的集合类型更好。

集合是通用的,并且在内容不假设其容器的任何内容时不会假设其内容,这是一个非常强大的抽象概念。恕我直言,除非有非常令人信服的理由,否则你应该真的避免搞乱。

如果你真的需要一个MessageCollection类。然后将__add__运算符放在其上。这意味着您将MessageCollection构建为空,并可以使用+添加(扩展它)。这类似于列表语法。

答案 1 :(得分:0)

停止写课程。 Really。这是Python中常见的反模式,它使许多在Java中学到很多的人和C ++的学习程度较低。部分原因是Java在包含类之外发生了什么。当你有一个没有状态的课程时,你不会被Java困住,那就忘了上课。

在Java和C ++中,创建集合类更为重要,因为这两种语言都没有列表,集合,序列,字典等。在Java / C ++中,您必须为任何聚类类型编写类,加载库或实例化模板。这增加了想要写一个类的频率:如果我想要一个Message *的向量,那么我将不得不像你一样做一个:

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))

但有一些事情可能会好得多。您没有编写的代码没有任何缺陷,更糟糕的是每次使用isinstance时,您都会增加代码耦合,以便MessageCollection和Message变得缠绕在一起并且不可分割。因此,我们会放弃Message.add这将删除您非常热衷的+。但是,除了用于链接消息之外,我们可以指出没有消息收集的行为,我们可以轻松地做到这一点

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

我们丢失了一个基于多态输入类型的多态返回类型的类方法。我们现在知道messages.append(m3)的时间复杂度,因为我们对列表非常熟悉,并且我们想要在messages上进行的所有切片,迭代,操作和更改都已可用,因为{{1} } 是-a 列表; MessageCollection可能是。如果必须使用messages真的让你筋疲力尽,那么你仍然有

messages.append(m3)

内置。