【问题标题】:Python - is there a way to efficiently search for all objects with a particular attribute?Python - 有没有办法有效地搜索具有特定属性的所有对象?
【发布时间】:2022-01-16 14:38:14
【问题描述】:

上下文 我有一个脚本,它生成同一个类的对象(这个类被称为State,供参考)。每个对象都包含一些value,对象的目标是存储和移动值。对象的其他属性由嵌套字典确定,这样字典:

dictionary = {
    "foo":[1,2],
    "bar":{
        "baz":[3],
        "qux":[4]
        }
}

将产生 4 个具有以下属性的对象

self.foo = 1, self.bar.baz = 3, self.value = 0
self.foo = 1, self.bar.qux = 4, self.value = 0
self.foo = 2, self.bar.baz = 3, self.value = 0
self.foo = 2, self.bar.qux = 4, self.value = 0

(有关此过程的更多信息:Combination of nested dictionaries with arbitrary lengths in python

问题 我需要找到某种方法来查找具有某个属性或一组属性的所有对象。例如,所有具有self.bar.baz 属性的对象可能需要承担某个value,而self.bar.qux 需要承担其他一些value

一种可能的解决方案 一种解决方案是创建一个单独的对象(来自Environment 类,供参考),该对象负责创建所有实例化对象的列表。

最简单的解决方案是将所有对象附加到一个列表中,然后遍历该列表,检查哪些对象具有一组属性。但是,这很快就会变得低效,尤其是考虑到对象集通常比上面的示例复杂得多。

稍微高级一点的解决方案是让Environment 对象具有所有可能的属性,例如

self.foo
self.bar.baz
self.bar.qux

并且这些属性的值是列表,包含所有具有这些属性的对象。这样就不需要通过列表对象进行过滤,具有一组属性的对象可以通过返回两个或多个列表中的对象集合来进行过滤。

但是,我不确定:

  • 这个代码实际上是什么样子的?
  • 是否有更有效的解决方案?

其他限制 重要的是,State 对象具有的属性不是固定的。也就是说,它可能不是foobar(或bazqux),但可能是完全不同的键。因此,我无法对要搜索的属性进行硬编码。

【问题讨论】:

    标签: python object filter


    【解决方案1】:

    这是一个建议:

    # creating data to use ...
    
    class State:
        """Dummy class for State, the `name` member and the `__repr__` are for visualization purposes"""
        def __init__(self, name):
            self.name = name
    
        def __repr__(self) -> str:
            return self.name
    
    
    # creating fake data (according to the structure you provided, though)
    s1, s2, s3, s4 = State("S1"), State("S2"), State("S3"), State("S4")
    s1.foo = 1; s1.bar = State(""); s1.bar.baz = 3; s1.value = 0
    s2.foo = 1; s2.bar = State(""); s2.bar.qux = 4; s2.value = 0
    s3.foo = 2; s3.bar = State(""); s3.bar.baz = 3; s3.value = 0
    s4.foo = 2; s4.bar = State(""); s4.bar.qux = 4; s4.value = 0
    
    states = [s1, s2, s3, s4]
    
    
    # now solving the problem ...
    
    def recursively_has_attr(obj, attrs: str) -> bool:
        """Iterate over an object members to check whether is has an 'a.b.c' attribute."""
        for attr_name in attrs.split("."):
            if hasattr(obj, attr_name):
                obj = getattr(obj, attr_name)
            else:
                return False
        else:
            return True
    
    
    # we are going to create customized versions of the function `recursively_has_attr`
    # by pre-filling its `attrs` parameters, using `functools.partial`
    from functools import partial
    matching_foo = partial(recursively_has_attr, attrs="foo")
    matching_bar_baz = partial(recursively_has_attr, attrs="bar.baz")
    matching_bar_qux = partial(recursively_has_attr, attrs="bar.qux")
    matching_bar_zod = partial(recursively_has_attr, attrs="bar.zod")
    
    print(f"matching foo : {tuple(filter(matching_foo, states))}")
    print(f"matching bar.baz : {tuple(filter(matching_bar_baz, states))}")
    print(f"matching bar.qux : {tuple(filter(matching_bar_qux, states))}")
    print(f"matching bar.zod : {tuple(filter(matching_bar_zod, states))}")
    
    user_supplied_attrs = input("Enter the attribute you want to search : ")
    filtering_function = partial(recursively_has_attr, attrs=user_supplied_attrs)
    print(f"matching {user_supplied_attrs!r} : {tuple(filter(filtering_function, states))}")
    

    产生:

    matching foo : (S1, S2, S3, S4)
    matching bar.baz : (S1, S3)
    matching bar.qux : (S2, S4)
    matching bar.zod : ()
    Enter the attribute you want to search : bar
    matching 'bar' : (S1, S2, S3, S4)
    

    这行得通,其他方法也可以。

    至于高效,真的是要求吗?
    如果是,efficient 是什么意思?二是内存方面、速度方面、代码行方面、复杂性方面?
    如果你想要非常好的性能,你会考虑使用低级语言吗? Cython 拥有“真正的”数组?

    在你的数据上测试我的方法,如果还不够,请发布一个目标明确的问题:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-20
      • 2016-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-15
      • 1970-01-01
      相关资源
      最近更新 更多