在Python中创建唯一对象数组

时间:2015-11-15 10:03:36

标签: python arrays algorithm list collections

假设我有一个程序可以创建一些带有线和点的方案。 所有行都由两点确定。有这些课程:

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类中添加哈希 eq 方法:

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

1 个答案:

答案 0 :(得分:2)

简答:

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

答案很长:

The documentation说:

  

set对象是不同的hashable对象的无序集合。常见用途包括(...)从序列(...)

中删除重复项

hashable means

  

如果对象具有在其生命周期内永远不会更改的哈希值(它需要__hash__()方法),并且可以与其他对象进行比较(它需要__eq__()方法),则该对象是可清除的。比较相等的Hashable对象必须具有相同的哈希值。

     

默认情况下,作为用户定义类实例的对象是可清除的;它们都比较不相等(除了它们自己),它们的哈希值来自它们的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可以更加pythonic的方式实现:

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