如何创建索引某些键位置的字典?

时间:2015-05-27 16:22:26

标签: python dictionary indexing data-structures

我有一个继承dict对象的类。

my_subclassed_dict = SubclassedDictionary({
        "id": {"value1": 144
               "value2": "steve",
               "more" {"id": 114}
        },
        "attributes": "random"
})

SubclassedDictionary初始化上,我希望生成符合特定条件的路径。

假设,如果我要做出这个条件,请为100以上的所有数字编制索引。然后,这可能会访问my_subclassed_dict.get_paths(),然后会返回某种类似于此的结构:

[
    ['id', 'value1'], 
    ['id', 'more', 'id',]
]

简而言之,在实例化时,如何为dict子类生成匹配特定条件的键的路径?

修改

因为有人要求提供示例实现。然而,问题在于它没有处理嵌套的字典。

class SubclassedDictionary(dict):
    paths = []

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)  # use the free update to set keys

    def update(self, *args, **kwargs):
        temp = args[0]
        for key, value in temp.items():
            if isinstance(value, int):
                if value > 100:
                    self.paths.append(key)
        super(SubclassedDictionary, self).update(*args, **kwargs)

dictionary = {
   "value1": 333,
   "v2": 99,
   "v2": 129,
   "v3": 30,
   "nested": {
      "nested_value" 1000
   }
}

new_dict = SubclassedDictionary(dictionary)

print(new_dict.paths) # outputs: ['v2','value1']

如果它确实按预期工作。

print(new_dict.paths) 

输出

[
   ['v2'],
   ['value1'],
   ['nested', 'nested_value']
]

2 个答案:

答案 0 :(得分:2)

根据我的理解,如果关键字的值与某个条件匹配,你需要一个能够在字典中返回字典键的字典。

class SubclassedDictionary(dict):
    def __init__(self, new_dict, condition=None, *args, **kwargs):
        super(SubclassedDictionary, self).__init__(new_dict, *args, **kwargs)
        self.paths = []
        self.get_paths(condition)

    def _get_paths_recursive(self, condition, iterable, parent_path=[]):
        path = []
        for key, value in iterable.iteritems():
            # If we find an iterable, recursively obtain new paths.
            if isinstance(value, (dict, list, set, tuple)):
                # Make sure to remember where we have been (parent_path + [key])
                recursed_path = self._get_paths_recursive(condition, value, parent_path + [key])
                if recursed_path:
                    self.paths.append(parent_path + recursed_path)
            elif condition(value) is True:
                self.paths.append(parent_path + [key])

    def get_paths(self, condition=None):
        # Condition MUST be a function that returns a bool!
        self.paths = []
        if condition is not None:
            return self._get_paths_recursive(condition, self)

def my_condition(value):
    try:
        return int(value) > 100
    except ValueError:
        return False



my_dict = SubclassedDictionary({"id": {"value1": 144,
                                       "value2": "steve",
                                       "more": {"id": 114}},
                                "attributes": "random"},
                               condition=my_condition)

print my_dict.paths  # Returns [['id', 'value1'], ['id', 'more', 'id']]

此实施有一些好处。一个是你可以随时改变你的状况。在你的问题中,听起来这可能是你感兴趣的一个功能。如果你想要一个不同的条件,你可以轻松地编写一个新函数并将其传递给类的构造函数,或者只需用你的get_paths()调用新条件。

在开发递归算法时,您应该考虑3件事。

1) What is my stopping condition?在这种情况下,您的文字条件实际上并不是您的停止条件。当不再有要迭代的元素时,递归停止。

2) Create a non-recursive function这一点很重要,原因有两个(我以后会到达第二个原因)。第一个原因是它是一种安全的方式来封装您不希望消费者使用的功能。在这种情况下,_get_paths_recursive()有额外的参数,如果消费者持有可能会破坏您的路径属性。

3) Do as much error handling before recursion (Second reason behind two functions)第二个函数的另一个好处是你可以进行非递归操作。通常,当您编写递归算法时,您必须在开始递归之前执行某些操作。在这种情况下,我确保condition参数有效(我可以添加更多检查以确保它的函数返回bool,并接受一个参数)。我还重置了路径属性,因此如果多次调用get_paths(),您就不会有大量的路径。

答案 1 :(得分:0)

最小的变化是:

class SubclassedDictionary(dict):

    def __init__(self, *args, **kwargs):
        self.paths = []  # note instance, not class, attribute
        self.update(*args, **kwargs)  # use the free update to set keys

    def update(self, *args, **kwargs):
        temp = args[0]
        for key, value in temp.items():
            if isinstance(value, int):
                if value > 100:
                    self.paths.append([key])  # note adding a list to the list
            # recursively handle nested dictionaries
            elif isinstance(value, dict):
                for path in SubclassedDictionary(value).paths:
                    self.paths.append([key]+path)
        super(SubclassedDictionary, self).update(*args, **kwargs)

这给出了您正在寻找的输出:

>>> SubclassedDictionary(dictionary).paths
[['v2'], ['value1'], ['nested', 'nested_value']]

但是,一个更简洁的方法可能是使paths成为一个方法,并创建嵌套的SubclassedDictionary实例而不是字典,这也允许您在调用时指定规则而不是硬编码。例如:

class SubclassedDictionary(dict):

    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)  # use the free update to set keys

    def update(self, *args, **kwargs):
        temp = args[0]
        for key, value in temp.items():
            if isinstance(value, dict):
                temp[key] = SubclassedDictionary(value)
        super(SubclassedDictionary, self).update(*args, **kwargs)

    def paths(self, rule):
        matching_paths = []
        for key, value in self.items():
            if isinstance(value, SubclassedDictionary):
                for path in value.paths(rule):
                    matching_paths.append([key]+path)
            elif rule(value):
                matching_paths.append([key])
        return matching_paths

在使用中,要获取大于100的所有整数的路径:

>>> SubclassedDictionary(dictionary).paths(lambda val: isinstance(val, int) and val > 100)
[['v2'], ['value1'], ['nested', 'nested_value']]

一个缺点是,每次调用时都会重新创建路径列表。

值得注意的是,您目前没有正确处理kwargs(所以我的代码也没有!);看看例如Overriding dict.update() method in subclass to prevent overwriting dict keys我在那里提供了一个答案,展示了如何实现与基本dict匹配的界面。您当前代码的另一个问题是它不处理随后从字典中删除的密钥;我的第一个片段也没有,但是当第二个片段重建路径列表时,每次它都不是问题。