2014-12-01 24 views
-3

我在調用方法列表的類中有一個方法def testing(self)。我需要知道是否有任何方法可以獲取方法中調用的方法總數,然後分別訪問每個方法。查找方法中的方法數量並分別訪問每個方法

def testing(self): 
    self.method1() 
    self.method2() 
    self.method3() 
    return 

def method1(self): 
    """DO WORK""" 
    return 

def method2(self): 
    """DO WORK""" 
    return 

def method3(self): 
    """DO WORK""" 
    return 
+0

豈不方法的總數始終是相同的?例如,這裏總是3. – Kevin 2014-12-01 20:59:19

+3

不是沒有醜陋,脆弱的黑客,沒有。你爲什麼覺得你需要這樣做? – BrenBarn 2014-12-01 21:02:19

回答

1

可攜帶且強健?

號有兩種基本方法:

  • 動態:模型的東西,計算呼叫的方法,然後調用方法。
  • 靜態:在代碼中查找方法調用,而不調用它。

嘲諷至少是潛在的破壞性,它的脆弱,可能會很複雜。但最大的問題是它只能沿着被調用的路徑找到方法調用。例如,假設您這樣寫:

def testing(self): 
    if self.method1(): 
     return self.method2() 
    else: 
     return self.method3(self.method4()) 

很明顯,沒有調用會找到所有四個方法調用。

或者:

def testing(self): 
    return 2/self.method1() 

除非你以某種方式提前知道該method1必須在這種情況下返回一個數字,你會得到一個TypeError這裏。但如果你知道這一點,你可能已經知道method1被稱爲在這種情況下,所以你的問題甚至不存在。

無論如何,因爲嘲諷是夠在概念上很難,而不必處理所有的實施頭痛,要做到這一點的正確方法是使用一個第三方的模塊,如Michele d'Amico's answer建議。但是你可以建立一些快速&髒自己,如果你想了解它是如何工作的:

def mock(method, *args, **kwargs): 
    obj = method.__self__ 
    _old_getattribute = type(obj).__getattribute__ 
    methods = [] 
    def _new_getattribute(self, attr): 
     methods.append(attr) 
    type(obj).__getattribute__ = _new_getattribute 
    try: 
     method(*args, **kwargs) 
    except Exception as e: 
     print 'Ignoring {}: {}'.format(type(e), e) 
    type(obj).__getattribute__ = _old_getattribute 
    return [name for name in methods if callable(getattr(obj, name))] 

methods = mock(self.testing) 

除了上面提到的問題,這在本質上假定這是一個方法調用的過程中擡起頭來對self任何實際存在,並且可調用的任何東西(包括例如staticmethod或擠在實例屬性中的常規函數​​)都是一種方法。你幾乎必須考慮所有這些問題,決定你想要什麼,並實現它。


同時,有多種方法可以查看代碼。您可以解析源代碼(例如,使用inspect.getsource),或者您可以通過testing.__code__.co_code中的字節代碼grub。 (當然,Bytecode是不可移植的。)但幸運的是,stdlib有兩個模塊可以幫助您:dis反彙編字節碼或ast模塊來解析源代碼。 dis的2.x版本有點笨重,它不會給你任何方式來遍歷字節碼並訪問它們的參數等等。你仍然可以通過解析字符串輸出或使用各種第三方模塊來完成。

無論如何,您需要提出一些關於什麼算作「方法調用」的規則,並編寫代碼來識別該模式。嘲笑版本當然也是如此,但這個版本更加明顯。

下面是與ast作爲一個例子,發現直接訪問過self任何東西任何呼叫:

class FindMethods(ast.NodeVisitor): 
    def __init__(self): 
     super(FindMethods, self).__init__() 
     self.methods = [] 
    def visit_Call(self, node): 
     try: 
      if node.func.value.id == 'self': 
       self.methods.append(node.func.attr) 
     except: 
      pass 
     return self.generic_visit(node) 
tree = ast.parse(inspect.getsource(testing)) 
finder = FindMethods() 
finder.visit(tree) 
print finder.methods