我有一个复杂的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向后移植一些项目以使其工作,因此它不会调用仅生成新对象的属性.这是我提出的代码:
原文链接:https://www.f2er.com/python/439380.html#-------------------------------------------------------------------------------
# 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,但我认为即使在这一点上它也值得发布,因为它比我开始时的正确轨道要多得多.