ऐसा लगता है कि यह एक उत्तर की शुरुआत है। मुझे इसे काम करने के लिए पाइथन 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]")
मैं अभी भी यह पता लगाने की कोशिश कर रहा हूं कि क्यों। बाबी नहीं मिला है, लेकिन मुझे लगता है कि इस बिंदु पर भी पोस्टिंग के लायक है, क्योंकि जब मैंने शुरू किया था तब से यह सही ट्रैक पर बहुत अधिक था।
मैंने अभी तक एक तैयार समाधान नहीं देखा है। शायद dir के बजाय gc.get_referents आपको थोड़ा आगे हो जाता है। – Pankrat
यह क्रुफ्ट (__eq__, इत्यादि) पर कटौती करता है हालांकि कीमत पर जो वस्तु के प्रकार से परिवर्तन लौटाया गया है, तो यह शायद एक तेज़ समाधान होगा, लेकिन आसान नहीं होगा। यह भी रिकर्सन का समर्थन नहीं करता है। धन्यवाद! –
क्या आपने 'pickle.Pickler' क्लास को उपclassing पर विचार किया है? स्रोत '.../Lib/pickle.py' में शामिल है। ऐसा करने से आपको बहुत सारे कोड का पुन: उपयोग करने की अनुमति मिलनी चाहिए और 'पिकले एरर' का वर्णन करने के लिए आपको जो कुछ भी वर्णन है - साथ ही साथ अच्छी तरह से स्थापित पिकलिंग-प्रोटोकॉल पाइथन का पिग-बैक ऑफ पहले से ही हो रहा है। – martineau