2014-12-01 73 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