2012-09-26 14 views
5

私は複雑なPythonデータ構造を持っています(重要な場合は大きなmusic21 Scoreオブジェクトです)。オブジェクト構造の深いところの弱点。以前はスタックトレースやPythonデバッガでこのような問題をデバッグしましたが、それは常に大きな問題です。オブジェクトのすべての属性に対してdir()を再帰的に実行し、リスト、タプル、辞書などに隠されたオブジェクトを見つけ出し、特定の値(ラムダ関数など)に一致するオブジェクトを返します。大きな問題は再帰的な参照です。したがって、ある種のメモ機能(copy.deepcopyの使用など)が必要です。私が試した:再帰的にdir()特定の型または特定の値の値を見つけるためのPythonオブジェクト

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 

私はおそらくこれに穴を差し込む続けることができます( ITER は、私は、例えば、dictsのためにしたいと思うものではありません)が、私はそれに多くの時間を投げる前に、誰かがあれば疑問に思います簡単な答えを知っています。それは非常に便利な一般的なツールになる可能性があります。

+1

私はまだ既成の解決策を見ていません。おそらく、dirの代わりにgc.get_referentsがあなたを少し遠ざけるでしょう。 – Pankrat

+0

返されるものの形式はオブジェクトの型によって変わるのですが、クラフト(__eq__など)は切り捨てられますが、より高速な解決策になるでしょうが、単純ではありません。それも再帰をサポートしていないようです。ありがとう! –

+1

'pickle.Pickler'クラスをサブクラス化することを検討しましたか?ソースは '.../Lib/pickle.py'に含まれています。そうすることで、たくさんのコードを再利用し、記述したことを行うために 'PickleError'をトラップすることができます。すでに確立されている十分に確立された酸洗プロトコルPythonをピギーバックすることができます。 – martineau

答えて

2

これは、回答の始まりと思われます。私はPython 3.2 inspect.getattr_staticからいくつかの項目をバックポートする必要があったので、新しいオブジェクトを生成し続けていたプロパティを呼び出さないようにしました。

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()) 

とget:

(<__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()) 
このコードは、あなたがこのような何かを実行することができます

#------------------------------------------------------------------------------- 
# 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 

:ここに私が思い付いたコードです

取得:

(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が見つからない理由を理解しようとしていますが、これは私が始めたときよりもはるかに正しいトラックであるため、この時点でも投稿する価値があると思います。