2016-04-12 1 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 определяется по интроспекции каждого класса __dict__ в (метод MRO порядок разрешения). Конечно, нет никакого способа, и нет смысла, делая это без интроспекции всех классов в цепочке MRO, за исключением того, что если отслеживать атрибуты как определенные, как в приведенном ниже примере, но и самоанализ сам MRO является oneliner в Python:

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

Опять же - можно было бы иметь метакласс к вашей наследственной цепи, которая будет следить за все определенные атрибуты в дереве наследования, а также использовать словарь для извлечения, где определяются каждый атрибут. Тот же метакласс также может автоматически украсить все __init__ (или все методы) и установить специальный __setitem__, чтобы он мог отслеживать атрибуты экземпляра по мере их создания, как указано выше.

Это можно сделать, немного сложно, было бы сложно поддерживать, и, вероятно, это сигнал, по которому вы неправильно подходите к своей проблеме.

Таким образом, метаклас для записи только атрибутов класса может быть просто (синтаксис python3 - определить атрибут __metaclass__ для тела класса, если вы все еще используете Python 2.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 из-за «несвязанного метода», который использует 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__)) 

А на консоли один получит этот результат:

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