これが答えの始まりのようです。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, 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 が見つからない理由を理解しようとしていますが、私が始めたときよりもはるかに正しい軌道に乗っているので、この時点でも投稿する価値があると思います.