【问题标题】:Creating array of unique objects in Python在 Python 中创建唯一对象数组
【发布时间】:2016-02-16 13:16:40
【问题描述】:

假设我有一个程序可以用线和点创建一些方案。 所有的线都由两点决定。有这些类:

class Coordinates(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y


class Point(object):
    def __init__(self, coordinates):
        self.coordinates = coordinates

class Line(object):
    def __init__(self, coordinates_1, coordinates_2):
        self.coordinates_1 = coordinates_1
        self.coordinates_2 = coordinates_2

方案采用线列表并创建唯一点列表。

class Circuit(object):
    def __init__(self, element_list):
        self.line_list = element_list
        self.point_collection = set()
        self.point_collection = self.generate_points()

    def generate_points(self):
        for line in self.line_list:
            coordinates_pair = [line.coordinates_1, line.coordinates_2]
            for coordinates in coordinates_pair:
                self.point_collection.add(Point(coordinates))
        return self.point_collection

哪些变体能够创建独特对象的列表或集合?如何在不使用集合和排序的情况下仅使用循环和条件来做到这一点?以及如何更简单?

UPD。我附加的代码无法正常工作。我尝试在 Point 类中添加 hasheq 方法:

class Point(object):
def __init__(self, coordinates):
    self.coordinates = coordinates

def __hash__(self):
    return 0

def __eq__(self, other):
    return True

然后我尝试用一​​些线条制作一个方案:

element_list=[]
element_list.append(Line(Coordinates(0,0), Coordinates(10,0)))
element_list.append(Line(Coordinates(10,0), Coordinates(10,20)))

circuit = Circuit(element_list)

print(circuit.point_collection)

这里的两条线等于四个点,其中两个点的坐标相同。因此,代码必须打印三个对象,但它只打印一个:

{<__main__.Point object at 0x0083E050>}

【问题讨论】:

  • 使用集合并不等同于排序,因为集合对象是不同的可散列对象的无序集合。你能解释一下为什么你不想使用一套吗?这样我们就会知道您在寻找什么。
  • @arekolek 你能解释一下为什么你不想使用一套吗? - 我猜是 OP 的教授/老师说不要。
  • @Ian:那么,我的问题会变成“为什么老师不希望他们使用一套?”。
  • 问题是这段代码不起作用。它只是返回它得到的所有点。我想知道如何正确地制作它。另外,我尝试创建一个递归解决方案,但它让我有点困惑。因此这种方式对我来说也很有趣。
  • 关于集合不等同于排序的事实。我不是说集合然后我说排序,但我知道创建唯一列表然后主列表排序的算法,然后很容易找到唯一项目。效果很好

标签: python arrays algorithm list collections


【解决方案1】:

简答:

您需要在 Point 类中实现 __hash__()__eq__() 方法。 有关想法,请参阅此answer showing a correct and good way to implement __hash__()

长答案:

The documentation 说:

集合对象是不同的可散列对象的无序集合。常见用途包括 (...) 从序列中删除重复项 (...)

还有hashable means:

如果一个对象的哈希值在其生命周期内永远不会改变(它需要一个__hash__() 方法),并且可以与其他对象进行比较(它需要一个__eq__() 方法),那么它就是可哈希的。比较相等的可散列对象必须具有相同的散列值。

默认情况下,作为用户定义类实例的对象是可散列的;它们都比较不相等(除了它们自己),它们的哈希值来自它们的id()

这解释了为什么您的代码不删除重复点。

考虑这种使Foo 的所有实例不同且Bar 的所有实例相等的实现:

class Foo:
  pass


class Bar:
  def __hash__(self):
    return 0

  def __eq__(self, other):
    return True

现在运行:

>>> set([Foo(), Foo()])
{<__main__.Foo at 0x7fb140791da0>, <__main__.Foo at 0x7fb140791f60>}

>>> set([Bar(), Bar()])
{<__main__.Bar at 0x7fb1407c5780>}

在您的情况下,__eq__ 应该在两个坐标相等时返回 True,而 __hash__ 应该返回坐标对的哈希值。请参阅前面提到的答案,了解一个很好的方法。

一些备注:

从设计的角度来看,您的 Point 类当前具有 no reason to exist,因为它只是 Coordinates 的包装器,并且不提供其他功能。您应该只使用其中之一,例如:

class Point(object):
  def __init__(self, x, y):
    self.x = x
    self.y = y

为什么不直接打电话给coordinates_1coordinates_2 ab

class Line(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

此外,您的generate_points 可以以更 Python 的方式实现:

def generate_points(self):
  return set(p for l in self.line_list for p in (l.a, l.b))

最后,为了更容易调试,你可以在你的类中consider implementing __repr__ and __str__ methods

【讨论】:

  • 谢谢,但是当我按照您的建议添加 hasheq 方法时,无论如何我只能得到一个集合中的一个项目。我试图在您的 Bar 类中实现实例变量。例如set([Bar(5), Bar(6)]) 只给了我一个对象。
  • @hopheylalaley 您能否编辑您的问题以包括您对__hash____eq__ 的实现?
  • 完成。非常感谢关于代码设计的建议。不过,我打算区分 Coordinates 和 Point 类。我想制作一个程序来实现图形的图形化。点在这里表示图的节点或顶点,它应该具有查找相邻节点的方法。
  • @hopheylalaley 我已链接到一个答案,该答案向您展示了如何正确实施它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-17
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
相关资源
最近更新 更多