加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

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

发布时间:2020-12-16 21:57:40 所属栏目:Python 来源:网络整理
导读:我有一个复杂的Python数据结构(如果它很重要,它是一个很大的music21 Score对象),由于在对象结构内部某处存在弱化,它不会发痒.我以前用堆栈跟踪和python调试器调试过这样的问题,但总是很痛苦.是否有一个工具以递归方式对对象的所有属性运行dir(),查找隐藏在列

我有一个复杂的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

我可能会继续插入这个漏洞(例如,它不是我想要的替代词),但在我投入更多时间之前,想知道是否有人知道更简单的答案.它可能是一个非常有用的通用工具.

最佳答案
这似乎是答案的开始.我不得不从Python 3.2 inspect.getattr_static向后移植一些项目以使其工作,因此它不会调用仅生成新对象的属性.这是我提出的代码:

#-------------------------------------------------------------------------------
# Name:         treeYield.py
# Purpose:      traverse a complex datastructure and yield elements
#               that fit a given criteria
#
# Authors:      Michael Scott Cuthbert
#
# Copyright:    Copyright ? 2012 Michael Scott Cuthbert
# License:      CC-BY
#-------------------------------------------------------------------------------
import types

class TreeYielder(object):
    def __init__(self,yieldValue = None):
        '''
        `yieldValue` should be a lambda function that
        returns True/False or a function/method call that
        will be passed the value of a current attribute
        '''        
        self.currentStack = []
        self.yieldValue = yieldValue
        self.stackVals = []
        t = types
        self.nonIterables = [t.IntType,t.StringType,t.UnicodeType,t.LongType,t.FloatType,t.NoneType,t.BooleanType]

    def run(self,obj,memo = None):
        '''
        traverse all attributes of an object looking
        for subObjects that meet a certain criteria.
        yield them.

        `memo` is a dictionary to keep track of objects
        that have already been seen

        The original object is added to the memo and
        also checked for yieldValue
        '''
        if memo is None:
            memo = {}
        self.memo = memo
        if id(obj) in self.memo:
            self.memo[id(obj)] += 1
            return
        else:
            self.memo[id(obj)] = 1

        if self.yieldValue(obj) is True:
            yield obj


        ### now check for sub values...
        self.currentStack.append(obj)

        tObj = type(obj)
        if tObj in self.nonIterables:
            pass
        elif tObj == types.DictType:
            for keyX in obj:
                dictTuple = ('dict',keyX)
                self.stackVals.append(dictTuple)
                x = obj[keyX]
                for z in self.run(x,memo=memo):
                    yield z
                self.stackVals.pop()

        elif tObj in [types.ListType,types.TupleType]:
            for i,x in enumerate(obj):
                listTuple = ('listLike',i)
                self.stackVals.append(listTuple)
                for z in self.run(x,memo=memo):
                    yield z
                self.stackVals.pop()

        else: # objects or uncaught types...
            ### from http://bugs.python.org/file18699/static.py
            try:
                instance_dict = object.__getattribute__(obj,"__dict__")
            except AttributeError:
                ## probably uncaught static object
                return

            for x in instance_dict:
                try:
                    gotValue = object.__getattribute__(obj,x)
                except: # ?? property that relies on something else being set.
                    continue
                objTuple = ('getattr',x)
                self.stackVals.append(objTuple)
                try:
                    for z in self.run(gotValue,memo=memo):
                        yield z
                except RuntimeError:
                    raise Exception("Maximum recursion on:n%s" % self.currentLevel())
                self.stackVals.pop()                

        self.currentStack.pop()

    def currentLevel(self):
        currentStr = ""
        for stackType,stackValue in self.stackVals:
            if stackType == 'dict':
                if isinstance(stackValue,str):
                    currentStr += "['" + stackValue + "']"
                elif isinstance(stackValue,unicode):
                    currentStr += "[u'" + stackValue + "']"
                else: # numeric key...
                    currentStr += "[" + str(stackValue) + "]"
            elif stackType == 'listLike':
                currentStr += "[" + str(stackValue) + "]"
            elif stackType == 'getattr':
                currentStr += ".__getattribute__('" + stackValue + "')"
            else:
                raise Exception("Cannot get attribute of type %s" % stackType)
        return currentStr

此代码允许您运行以下内容:

class Mock(object):
    def __init__(self,mockThing,embedMock = True):
        self.abby = 30
        self.mocker = mockThing
        self.mockList = [mockThing,40]
        self.embeddedMock = None
        if embedMock is True:
            self.embeddedMock = Mock(mockThing,embedMock = False)

mockType = lambda x: x.__class__.__name__ == 'Mock'

subList = [100,60,-2]
myList = [5,20,[5,12,17],30,{'hello': 10,'goodbye': 22,'mock': Mock(subList)},-20,Mock(subList)]
myList.append(myList)

ty = TreeYielder(mockType)
for val in ty.run(myList):
    print(val,ty.currentLevel())

得到:

(<__main__.Mock object at 0x01DEBD10>,"[4]['mock']")
(<__main__.Mock object at 0x01DEF370>,"[4]['mock'].__getattribute__('embeddedMock')")
(<__main__.Mock object at 0x01DEF390>,'[6]')
(<__main__.Mock object at 0x01DEF3B0>,"[6].__getattribute__('embeddedMock')")

或运行:

high = lambda x: isinstance(x,(int,float)) and x > 10
ty = TreeYielder(high)
for val in ty.run(myList):
    print(val,ty.currentLevel())

得到:

(20,'[1]')
(12,'[2][1]')
(17,'[2][2]')
(30,'[3]')
(22,"[4]['goodbye']")
(100,"[4]['mock'].__getattribute__('embeddedMock').__getattribute__('mocker')[0]")
(60,"[4]['mock'].__getattribute__('embeddedMock').__getattribute__('mocker')[1]")
(40,"[4]['mock'].__getattribute__('embeddedMock').__getattribute__('mockList')[2]")

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读