递归地dir()一个python对象来查找某个类型或某个值的值

Mic*_*ert 6 python recursive-descent recursive-datastructures

我有一个复杂的Python数据结构(如果它很重要,它是一个很大的music21 Score对象),由于在对象结构内部某处存在弱化,它不会发痒.我以前用堆栈跟踪和python调试器调试过这样的问题,但总是很痛苦.是否有一个工具以递归方式对对象的所有属性运行dir(),查找隐藏在列表,元组,dicts等中的对象,并返回与特定值匹配的那些(lambda函数或类似的东西).一个很大的问题是递归引用,因此需要某种备忘录函数(如copy.deepcopy使用).我试过了:

import weakref
def findWeakRef(streamObj, memo=None):
    weakRefList = []
    if memo is None:
        memo = {}
    for x in dir(streamObj):
        xValue = getattr(streamObj, x)
        if id(xValue) in memo:
            continue
        else:
            memo[id(xValue)] = True
        if type(xValue) is weakref.ref:
            weakRefList.append(x, xValue, streamObj)
        if hasattr(xValue, "__iter__"):
            for i in xValue:
                if id(i) in memo:
                    pass
                else:
                    memo[id(i)] = True
                    weakRefList.extend(findWeakRef(i), memo)
        else:
            weakRefList.extend(findWeakRef(xValue), memo)
    return weakRefList
Run Code Online (Sandbox Code Playgroud)

我大概可以继续在此堵孔(在ITER是不是我想要什么类型的字典,例如),但在此之前我把更多的时间到了,如果有人知道一个更简单的回答不知道.它可能是一个非常有用的通用工具.

Sri*_*riK 5

这是一个比较简单的解决方案,但有些幼稚。即只是在属性树下进行深度优先搜索。如果它是一个原始类型,则停止,否则深入树中。它将为您提供调用树和叶子的值。

def recursive_dir(obj, path):
    if ((obj!=None) and (not isinstance(obj, (str,float,int,list,dict,set)))):
        for attr, val in obj.__dict__.iteritems():
            temp_path = path[:]
            temp_path.append(attr)
            recursive_dir(getattr(obj, attr), temp_path)
    else:
        print (path, "--->", obj)
        print("")
recursive_dir(x,[])
Run Code Online (Sandbox Code Playgroud)


Mic*_*ert 4

这似乎是一个答案的开始。我必须从 Python 3.2spect.getattr_static 向后移植一些项目才能使其工作,这样它就不会调用只是不断生成新对象的属性。这是我想出的代码:

\n\n
#-------------------------------------------------------------------------------\n# Name:         treeYield.py\n# Purpose:      traverse a complex datastructure and yield elements\n#               that fit a given criteria\n#\n# Authors:      Michael Scott Cuthbert\n#\n# Copyright:    Copyright \xc2\xa9 2012 Michael Scott Cuthbert\n# License:      CC-BY\n#-------------------------------------------------------------------------------\nimport types\n\nclass TreeYielder(object):\n    def __init__(self, yieldValue = None):\n        \'\'\'\n        `yieldValue` should be a lambda function that\n        returns True/False or a function/method call that\n        will be passed the value of a current attribute\n        \'\'\'        \n        self.currentStack = []\n        self.yieldValue = yieldValue\n        self.stackVals = []\n        t = types\n        self.nonIterables = [t.IntType, t.StringType, t.UnicodeType, t.LongType,\n                             t.FloatType, t.NoneType, t.BooleanType]\n\n    def run(self, obj, memo = None):\n        \'\'\'\n        traverse all attributes of an object looking\n        for subObjects that meet a certain criteria.\n        yield them.\n\n        `memo` is a dictionary to keep track of objects\n        that have already been seen\n\n        The original object is added to the memo and\n        also checked for yieldValue\n        \'\'\'\n        if memo is None:\n            memo = {}\n        self.memo = memo\n        if id(obj) in self.memo:\n            self.memo[id(obj)] += 1\n            return\n        else:\n            self.memo[id(obj)] = 1\n\n        if self.yieldValue(obj) is True:\n            yield obj\n\n\n        ### now check for sub values...\n        self.currentStack.append(obj)\n\n        tObj = type(obj)\n        if tObj in self.nonIterables:\n            pass\n        elif tObj == types.DictType:\n            for keyX in obj:\n                dictTuple = (\'dict\', keyX)\n                self.stackVals.append(dictTuple)\n                x = obj[keyX]\n                for z in self.run(x, memo=memo):\n                    yield z\n                self.stackVals.pop()\n\n        elif tObj in [types.ListType, types.TupleType]:\n            for i,x in enumerate(obj):\n                listTuple = (\'listLike\', i)\n                self.stackVals.append(listTuple)\n                for z in self.run(x, memo=memo):\n                    yield z\n                self.stackVals.pop()\n\n        else: # objects or uncaught types...\n            ### from http://bugs.python.org/file18699/static.py\n            try:\n                instance_dict = object.__getattribute__(obj, "__dict__")\n            except AttributeError:\n                ## probably uncaught static object\n                return\n\n            for x in instance_dict:\n                try:\n                    gotValue = object.__getattribute__(obj, x)\n                except: # ?? property that relies on something else being set.\n                    continue\n                objTuple = (\'getattr\', x)\n                self.stackVals.append(objTuple)\n                try:\n                    for z in self.run(gotValue, memo=memo):\n                        yield z\n                except RuntimeError:\n                    raise Exception("Maximum recursion on:\\n%s" % self.currentLevel())\n                self.stackVals.pop()                \n\n        self.currentStack.pop()\n\n    def currentLevel(self):\n        currentStr = ""\n        for stackType, stackValue in self.stackVals:\n            if stackType == \'dict\':\n                if isinstance(stackValue, str):\n                    currentStr += "[\'" + stackValue + "\']"\n                elif isinstance(stackValue, unicode):\n                    currentStr += "[u\'" + stackValue + "\']"\n                else: # numeric key...\n                    currentStr += "[" + str(stackValue) + "]"\n            elif stackType == \'listLike\':\n                currentStr += "[" + str(stackValue) + "]"\n            elif stackType == \'getattr\':\n                currentStr += ".__getattribute__(\'" + stackValue + "\')"\n            else:\n                raise Exception("Cannot get attribute of type %s" % stackType)\n        return currentStr\n
Run Code Online (Sandbox Code Playgroud)\n\n

此代码可让您运行如下所示的代码:

\n\n
class Mock(object):\n    def __init__(self, mockThing, embedMock = True):\n        self.abby = 30\n        self.mocker = mockThing\n        self.mockList = [mockThing, mockThing, 40]\n        self.embeddedMock = None\n        if embedMock is True:\n            self.embeddedMock = Mock(mockThing, embedMock = False)\n\nmockType = lambda x: x.__class__.__name__ == \'Mock\'\n\nsubList = [100, 60, -2]\nmyList = [5, 20, [5, 12, 17], 30, {\'hello\': 10, \'goodbye\': 22, \'mock\': Mock(subList)}, -20, Mock(subList)]\nmyList.append(myList)\n\nty = TreeYielder(mockType)\nfor val in ty.run(myList):\n    print(val, ty.currentLevel())\n
Run Code Online (Sandbox Code Playgroud)\n\n

并得到:

\n\n
(<__main__.Mock object at 0x01DEBD10>, "[4][\'mock\']")\n(<__main__.Mock object at 0x01DEF370>, "[4][\'mock\'].__getattribute__(\'embeddedMock\')")\n(<__main__.Mock object at 0x01DEF390>, \'[6]\')\n(<__main__.Mock object at 0x01DEF3B0>, "[6].__getattribute__(\'embeddedMock\')")\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者运行:

\n\n
high = lambda x: isinstance(x, (int, float)) and x > 10\nty = TreeYielder(high)\nfor val in ty.run(myList):\n    print(val, ty.currentLevel())\n
Run Code Online (Sandbox Code Playgroud)\n\n

并得到:

\n\n
(20, \'[1]\')\n(12, \'[2][1]\')\n(17, \'[2][2]\')\n(30, \'[3]\')\n(22, "[4][\'goodbye\']")\n(100, "[4][\'mock\'].__getattribute__(\'embeddedMock\').__getattribute__(\'mocker\')[0]")\n(60, "[4][\'mock\'].__getattribute__(\'embeddedMock\').__getattribute__(\'mocker\')[1]")\n(40, "[4][\'mock\'].__getattribute__(\'embeddedMock\').__getattribute__(\'mockList\')[2]")\n
Run Code Online (Sandbox Code Playgroud)\n\n

我仍在试图找出为什么没有找到 .abby,但我认为即使在这一点上它也值得发布,因为它比我开始时更走在正确的轨道上。

\n