python中hasattr的倒数

Inverse of hasattr in Python

如果一个物体具有特定属性,则使用查询,但给出一个属性,则有一种方法来确定(所有)物体的位置。

假设我的代码是一个字符串(或一种分类法)的名称,我想引用classname.attribute但我没有这个分类法。

这是我心中的一个解决办法

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"的所有子类——因此,要找到的类不需要位于finder函数所在的模块的命名空间中。

但是,如果您的方法在您正在搜索的一组类中是唯一的,那么您可能只需要组装所有方法的映射并使用它来调用它们。

让我们假设您的所有类都是从一个名为"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) }

你跟mapper['attrname']()打电话给他们

这避免了在每个方法调用时进行线性搜索,因此会更好。

-编辑-

__subclassess__只是找到一个类的直接子类,而不是继承树——所以它在"现实生活"中不起作用——也许它在OP掌握的特定情况下是有用的。如果需要在继承树中找到东西,那么也需要在每个子类中重复出现。

至于老式的类:当然,这行不通——这是它们在新代码中默认被破坏的动机之一。

至于非类属性:不管怎样,它们只能在检查实例时找到——所以必须考虑另一个方法——这里看起来并不是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"))