2016-04-12 7 views
2

継承のレイヤーが複数あり、特定の変数が存在することがわかっている場合は、その変数がどこに由来するかをトレースする方法がありますか?各ファイルとクラスを調べることによって後方にナビゲートする必要はありません。おそらくそれを行う関数のいくつかの並べ替えを呼び出すか? 例:どのように継承された変数がPythonでどこから来るのかを見つけることができますか?

parent.py

class parent(object): 
    def __init__(self): 
     findMe = "Here I am!" 

child.py

from parent import parent 
class child(parent): 
    pass 

grandson.py

from child import child 
class grandson(child): 
    def printVar(self): 
     print self.findMe 

はfindMe変数は、関数呼び出しとどこから来たのか探してみてください。

+0

を...そしてあなたは 'おそらくfunction'のいくつかの並べ替えを呼び出すと言うとき、あなたはその関数がいずれかの継承階層をナビゲートしたくありません、右? – Gerrat

答えて

5

「変数」はインスタンス変数である場合 - あなたが__init__方法のチェーンの任意の時点であれば、そう:

def __init__(self): 
    self.findMe = "Here I am!" 

それはその時点からインスタンス変数である、とすることはできませんため、すべてのエフェクトは、他のインスタンス変数とは区別されます。 (特別な__setattr__メソッドを持つクラスのような仕組みを入れないと、属性の変更を追跡し、コードセットのどの部分を属性に設定し直すか - この回答の最後の例を参照)

また

class parent(object): 
    def __init__(self): 
     findMe = "Here I am!" 

findMe

は、そのメソッドへのローカル変数として定義され、 __init__が終了した後も、存在しない、あなたの例であることに注意。

今、あなたの変数は継承チェーン上のどこかにクラス属性として設定されている場合:

class parent(object): 
    findMe = False 

class childone(parent): 
    ... 

findMeはMRO(メソッドでは、各クラスの__dict__をイントロスペクトによって定義されたクラスを見つけることが可能です解決順序)チェーン。もちろん、MROチェーン内のすべてのクラスをイントロスペクトすることなくこれを行う方法はありません。以下の例のように定義された属性を追跡する場合は例外ですが、MRO自体のイントロスペクションはPythonの:もう一度

def __init__(self): 
    super().__init__() 
    ... 
    findme_definer = [cls for cls in self.__class__.__mro__ if "findMe" in cls.__dict__][0] 

- 継承ツリー内のすべての定義された属性を追跡し、各属性が定義されている場所を取得するために辞書を使用することになり、あなたの継承チェーンにメタクラスを持つことも可能であろう。同じメタクラスはすべて__init__(またはすべてのメソッド)を自動的に装飾し、上に挙げたようにインスタンス属性を作成時に追跡できるように特別な__setitem__を設定することもできます。

これは実行することができますが、少し複雑で、維持するのが難しく、おそらくあなたの問題に間違ったアプローチをしているというシグナルです。

クラス属性だけを記録するメタクラスは単純に(Python3構文 - まだPython 2を使用している場合はクラス本体に__metaclass__属性を定義する)ことができます。7):今すぐ

class MetaBase(type): 
    definitions = {} 
    def __init__(cls, name, bases, dct): 
     for attr in dct.keys(): 
      cls.__class__.definitions[attr] = cls 

class parent(metaclass=MetaBase): 
    findMe = 5 
    def __init__(self): 
     print(self.__class__.definitions["findMe"]) 

、人は働くことができ、各クラスの各メソッドを包む、currentclassの属性を定義しただけで「ライブ」追跡メカニズムスーパークラスのどの検索したい場合 - それは多くのトリッキーです。

私はこれを作ったことがありますが、これはあまり必要がない場合でも、クラスクラスのクラス属性をクラスdefinitionsとインスタンス_definitions辞書に記録していますメソッドは、特定のインスタンス属性を最後に設定している可能性があります(これは純粋なPython3であり、Python2が使用する "unbound method"のためにPython2に移植するのは難しく、Python3では単純な関数)

from threading import current_thread 
from functools import wraps 
from types import MethodType 
from collections import defaultdict 

def method_decorator(func, cls): 
    @wraps(func) 
    def wrapper(self, *args, **kw): 
     self.__class__.__class__.current_running_class[current_thread()].append(cls) 
     result = MethodType(func, self)(*args, **kw) 
     self.__class__.__class__.current_running_class[current_thread()].pop() 
     return result 
    return wrapper 

class MetaBase(type): 
    definitions = {} 
    current_running_class = defaultdict(list) 
    def __init__(cls, name, bases, dct): 
     for attrname, attr in dct.items(): 
      cls.__class__.definitions[attr] = cls 
      if callable(attr) and attrname != "__setattr__": 
       setattr(cls, attrname, method_decorator(attr, cls)) 

class Base(object, metaclass=MetaBase): 
    def __setattr__(self, attr, value): 
     if not hasattr(self, "_definitions"): 
      super().__setattr__("_definitions", {}) 
     self._definitions[attr] = self.__class__.current_running_class[current_thread()][-1] 
     return super().__setattr__(attr,value) 

例上記のコードのクラス:

class Parent(Base): 
    def __init__(self): 
     super().__init__() 
     self.findMe = 10 

class Child1(Parent): 
    def __init__(self): 
     super().__init__() 
     self.findMe1 = 20 

class Child2(Parent): 
    def __init__(self): 
     super().__init__() 
     self.findMe2 = 30 

class GrandChild(Child1, Child2): 
    def __init__(self): 
     super().__init__() 
    def findall(self): 
     for attr in "findMe findMe1 findMe2".split(): 
      print("Attr '{}' defined in class '{}' ".format(attr, self._definitions[attr].__name__)) 

とコンソールに1は、この結果を取得します:

In [87]: g = GrandChild() 

In [88]: g.findall() 
Attr 'findMe' defined in class 'Parent' 
Attr 'findMe1' defined in class 'Child1' 
Attr 'findMe2' defined in class 'Child2' 
+0

Jsbueno、詳細な説明をありがとうございます。この返信は私の質問に完全に答えました。私はself.findMeを入れることを意味しましたが、私がそれを提出したとき、それを削除しました。ちょうど私が探していたもの。再度、感謝します! – Krypton36

関連する問題