Inverse of hasattr in Python
如果一个物体具有特定属性,则使用查询,但给出一个属性,则有一种方法来确定(所有)物体的位置。
假设我的代码是一个字符串(或一种分类法)的名称,我想引用
这是我心中的一个解决办法
1 2 3 4 5 6 7 | def finder(attr): for obj in globals(): try: if globals()[obj].__dict__[attr]: return(globals()[obj]) except: ... |
惯例:
ZZU1
我确信这不是最好的办法(甚至是最好的办法)。请有人提供更好的方式。
它总是"可能的"。这是否可取是另一个历史。
一种快速而肮脏的方法是对所有类进行线性迭代,并检查是否有定义您拥有的属性的方法。当然,这会受到冲突的影响,并且会产生具有这样一个命名属性的第一个类。如果它存在于多个文件中,则由您决定要哪个文件:
1 2 3 4 5 | def finder(attr): for cls in object.__subclasses__(): if hasattr(cls, attr): return cls raise ValueError |
不是在"globals"中搜索,而是搜索"object"的所有子类——因此,要找到的类不需要位于
但是,如果您的方法在您正在搜索的一组类中是唯一的,那么您可能只需要组装所有方法的映射并使用它来调用它们。
让我们假设您的所有类都是从一个名为"base"的类中直接获取的:
1 2 | mapper = {attr_name:getattr(cls, attr_name) for cls in base.__subclasses__() for attr_name, obj in cls.__dict__.items() if isinstance(obj, classmethod) } |
你跟
这避免了在每个方法调用时进行线性搜索,因此会更好。
-编辑-
至于老式的类:当然,这行不通——这是它们在新代码中默认被破坏的动机之一。
至于非类属性:不管怎样,它们只能在检查实例时找到——所以必须考虑另一个方法——这里看起来并不是O.P.关心的问题。
这可能有助于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import gc def checker(checkee, maxdepth = 3): def onlyDict(ls): return filter(lambda x: isinstance(x, dict), ls) collection = [] toBeInspected = {} tBI = toBeInspected gc.collect() for dic in onlyDict(gc.get_referrers(checkee)): for item, value in dic.iteritems(): if value is checkee: collection.append(item) elif item !="checker": tBI[item] = value def _auxChecker(checkee, path, collection, checked, current, depth): if current in checked: return checked.append(current) gc.collect() for dic in onlyDict(gc.get_referents(current)): for item, value in dic.iteritems(): currentPath = path +"." + item if value is checkee: collection.append(currentPath) else: try: _auxChecker(checkee, currentPath, collection, checked, value, depth + 1) if depth < maxdepth else None except TypeError: continue checked = [] for item, value in tBI.iteritems(): _auxChecker(checkee, item, collection, checked, value, 1) return collection |
如何使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | referrer = [] class Foo: pass noo = Foo() bar = noo import xml import libxml2 import sys import os op = os.path xml.foo = bar foobar = noo for x in checker(foobar, 5): try: y= eval(x) referrer.append(x) except: continue del x, y |
ps:对于对被检查者本身的递归或嵌套引用,不会进一步检查被检查者的属性。
这应该在所有情况下都有效,但仍然需要大量测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import inspect import sys def finder(attr, classes=None): result = [] if classes is None: # get all accessible classes classes = [obj for name, obj in inspect.getmembers( sys.modules[__name__])] for a_class in classes: if inspect.isclass(a_class): if hasattr(a_class, attr): result.append(a_class) else: # we check for instance attributes if hasattr(a_class(), attr): result.append(a_class) try: result += finder(attr, a_class.__subclasses__()) except: # old style classes (that don't inherit from object) do not # have __subclasses; not the best solution though pass return list(set(result)) # workaround duplicates def main(attr): print finder(attr) return 0 if __name__ =="__main__": sys.exit(main("some_attr")) |