可携带且强健?
号有两种基本方法:
- 动态:模型的东西,计算呼叫的方法,然后调用方法。
- 静态:在代码中查找方法调用,而不调用它。
嘲讽至少是潜在的破坏性,它的脆弱,可能会很复杂。但最大的问题是它只能沿着被调用的路径找到方法调用。例如,假设您这样写:
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
岂不方法的总数始终是相同的?例如,这里总是3. – Kevin 2014-12-01 20:59:19
不是没有丑陋,脆弱的黑客,没有。你为什么觉得你需要这样做? – BrenBarn 2014-12-01 21:02:19