可攜帶且強健?
號有兩種基本方法:
- 動態:模型的東西,計算呼叫的方法,然後調用方法。
- 靜態:在代碼中查找方法調用,而不調用它。
嘲諷至少是潛在的破壞性,它的脆弱,可能會很複雜。但最大的問題是它只能沿着被調用的路徑找到方法調用。例如,假設您這樣寫:
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