Python通过某些属性进行堆积,在属性更改后重新进行重新绑定

时间:2016-04-18 21:51:53

标签: python python-3.x python-3.5

我正在尝试使用python 3.5标准库中的heappq模块来创建相同类型对象的优先级队列。我希望能够根据对象的属性进行堆积,然后更改其中一些属性的值,然后根据新值重新堆积。我想知道我是怎么做的。

import heappq
class multiNode:
    def __init__(self, keyValue):
        self.__key = keyValue
    def setKey(self, keyValue):
        self.__key = keyValue
    def getKey(self):
        return self.__key

queue = [multiNode(1), multiNode(2), multiNode(3)]
heapq.heapify(queue) #want to heapify by whatever getKey returns for each node
queue[0].setKey(1000)
heapq.heapify(queue) #re heapify with those new values

2 个答案:

答案 0 :(得分:2)

有多种方法可以使代码正常工作。例如,您可以通过实施某些rich comparison operator methods(也许使用functools.total_ordering来实现其余部分)来使您的商品可以订购:

@functools.total_ordering
class multiNode:
    def __init__(self, keyValue):
        self.__key = keyValue
    def setKey(self, keyValue):
        self.__key = keyValue
    def getKey(self):
        return self.__key
    def __eq__(self, other):
        if not isinstance(other, multiNode):
            return NotImplemented
        return self.__key == other.__key
    def __lt__(self, other):
        if not isinstance(other, multiNode):
            return NotImplemented
        return self.__key < other.__key

这将使您的代码正常工作,但每次更改其中的节点时重新定义队列可能效率不高,尤其是如果队列中有很多节点。更好的方法可能是在队列周围编写一些额外的逻辑,这样就可以在不删除队列条目或违反堆属性的情况下使队列条目无效。然后,当您有一个需要更新的项目时,您只需使其旧条目无效并添加一个具有新优先级的新条目。

这是一个快速而又脏的实现,它使用字典从节点实例映射到[pritority, node]列表。如果节点的优先级更新,则会检查字典,并将列表的node部分设置为None。从节点前面弹出节点时,将忽略无效的条目。

queue = []
queue_register = {}

def add_to_queue(node)
    item = [node.getKey(), node]
    queue.heappush(queue, item)
    queue_register[node] = item

def update_key_in_queue(node, new_key):
    queue_register[node][1] = None # invalidate old item
    node.setKey(new_key)
    add_to_queue(node)

def pop_from_queue():
    node = None
    while node is None:
        _, node = heapq.heappop(queue) # keep popping items until we find one that's valid
    del queue_register[node] # clean up our bookkeeping record
    return node

您可能希望针对重新规划进行测试,以查看哪个程序对队列的实际使用速度更快。

关于您multiNode课程的一些最终说明(与您在问题中提出的问题无关):

你在课堂上做的很多事情并不是非常恐怖的。首先,Python最常见的命名约定是CapitalizedNames用于类,lower_case_names_with_underscores用于其他几乎所有类型,函数,模块的变量。

使用__key的双引号下划线的另一个问题。双重领先(而不是尾随)的unscrores调用Python的名称修改系统。这可能看起来像是一种将变量设为私有的方式,但事实并非如此。它更倾向于帮助防止意外名称冲突,例如当您在代理对象(否则模仿某些其他对象的属性)中设置属性时,或者在mixin类中(可能由具有未知属性的其他类型继承) )。如果您班级外的代码确实想要访问__key课程中的错位属性multiNode,他们仍然可以使用_multiNode__key来完成此操作。要暗示某些内容意图是私有属性,您应该只使用一个下划线_key

这使我对自己的最终问题有所了解,key可能根本不应该是私密的。使用getXsetX方法修改私有实例变量并不是非常Pythonic。记录该属性是该类的公共API的一部分并让其他代码直接访问它是更常见的。如果您稍后决定在查找或修改属性时需要做一些奇特的事情,则可以使用property描述符自动将属性访问转换为对getter和setter函数的调用。其他编程语言通常以getter和setter而不是public属性开头,因为以后没有这种方法可以更改属性API的实现。所以无论如何,我会让你的班级__init__设置为self.key = keyValue并彻底摆脱setKeygetKey

答案 1 :(得分:0)

执行您正在寻找的内容的粗略方法是使用id()和Python内置的id方法。这个方法基本上允许你将堆保存为你创建的对象的dict堆,然后通过在id中访问它们{{1}来更新这些对象。键是键。我在我的本地机器上尝试了这个,它似乎做了你正在寻找的东西:

import heapq
class multiNode:
    def __init__(self, keyValue):
        self.__key = keyValue
    def setKey(self, keyValue):
        self.__key = keyValue
    def getKey(self):
        return self.__key

first_node = multiNode(1)
second_node = multiNode(2)
thrid_node = multiNode(3)
# add more nodes here
q = [id(first_node), id(second_node), id(third_node)]
mutilNode_dict = {
    id(first_node): first_node,
    id(second_node): second_node,
    id(third_node): third_node
}
heapq.heapify(q)
multiNode_dict[q[0]].setKey(1000)
heapq.heapify(q)

heapify()在这里确实做得太多,因为对象的id将被删除,直到它被删除。如果您将新对象添加到堆中并取出对象,则会更有用。