您可以使用包装器临时替换要检查的方法,该包装器将通过全局变量(或您选择的其他选项)让您知道是否调用了该方法。我认为这是唯一真正的解决方案,因为使用字符串匹配进行检查和检查像我建议的in my other answer那样的解散代码都容易出错,并且不可避免地会遗漏边缘情况。
这是一个例子:
class Checker:
def check(self,func,funcargs):
real_np_random_choice = np.random.choice
self.called = False
def wrapper(*args,**kwargs):
self.called = True
return real_np_random_choice(*args,**kwargs)
np.random.choice = wrapper
func(*funcargs)
np.random.choice = real_np_random_choice
return self.called
Checker().check(checkme,(3,[1,2,3],4)) # -> True
我在这里使用一个类仅仅是因为我需要以某种方式将结果从wrapper
中取出。当然也可以使用全局变量来完成。
检查给定模块的给定方法是否被调用的更通用解决方案是:
class Checker:
def __init__(self,module,method):
self.module = module
self.method = method
def check(self,funcargs):
real_method = getattr(self.module,self.method)
self.called = False
def wrapper(*args,**kwargs):
self.called = True
return real_method(*args,**kwargs)
setattr(self.module,self.method,wrapper)
func(*funcargs)
setattr(self.module,real_method)
return self.called
c = Checker(np.random,'choice')
print(c.check(checkme,4)))
,
您可以执行以下操作:
import inspect
f = 'XXX' # Your student's submission is stored in XXX.py
assert('np.random.choice' in inspect.getsource(__import__(f).checkme))
您可以检查函数是否已被调用,而不是检查源代码。您可以应用装饰器来执行此检查:
import numpy as np
# Create your decorator
def iscalled_decorator(func):
def wrapper(*args,**kwargs):
global iscalled
iscalled = True
return func(*args,**kwargs)
return wrapper
# Decorate np.random.choice
np.random.choice = iscalled_decorator(np.random.choice)
# Import your student's function
f = 'XXX'
checkme = __import__(f).checkme
# Set a flag iscalled and call the function
iscalled = False
checkme(3,4)
# Check if the flag is True
assert(iscalled)
,
假设您想拥有手边的功能(即使是经过编译的pyc
形式),并且通过字符串搜索操作(我想您是不是 )可能已经考虑过了),那么您可以使用the dis
module。
形式为y = np.random.choice(x)
的调用将被编译为以下内容(dis.dis()
的输出):
8 0 LOAD_GLOBAL 0 (np)
2 LOAD_ATTR 1 (random)
4 LOAD_METHOD 2 (choice)
6 LOAD_FAST 1 (x)
8 CALL_METHOD 1
10 STORE_FAST 2 (y)
假设您的学生使用的是全局import numpy as np
,则这些说明及其论据的顺序应始终相同。根据方法的加载方式,第三个LOAD_METHOD
可能会变成LOAD_ATTR
。
实际呼叫更难检测,根据其执行方式,可能变为CALL_METHOD
,CALL_FUNCTION_EX
,CALL_FUNCTION_KW
或CALL_FUNCTION
。在上面显而易见的情况下,要检查要调用的函数实际上是否是您想要的那个函数,也不是那么简单。检查仍然可以进行实际的调用,但是需要跟踪Python堆栈并真正解析指令和它们的参数,如果您想深入研究它们,可以检查the documentation。
我将只检查np.random.choice
是否实际上已加载到选中的函数中。您可以使用以下代码进行操作:
import dis
def zip3(g):
try:
a,b,c = next(g),next(g),next(g)
while 1:
yield a,c
a,c = b,c,next(g)
except StopIteration:
pass
def check(func):
for a,c in zip3(dis.get_instructions(func)):
if a.opname == 'LOAD_GLOBAL' and a.argval == 'np':
if b.opname == 'LOAD_ATTR' and b.argval == 'random':
if c.opname in ('LOAD_ATTR','LOAD_METHOD') and c.argval == 'choice':
return True
return False
check(checkme) # -> True
注意:操作码可以取决于Python版本,但是我假设您将在同一Python版本下运行所有测试,因此您可以将匹配项调整为满足您的需求(使用dis.dis()
进行检查)。当然,使用这种方法,您将无法捕获更多a = np; b = a.random; b.choice(x)
或import numpy as whatever
这样复杂的东西,但是无论如何对于字符串匹配也是如此。
,
您可以使用另一种检查方法inspect.getsource
,它将以字符串的形式获取函数的源代码。
import inspect
import numpy as np
def checkme(z,x=[1,y=4):
tmp = np.random.choice(x,size=y)
if z:
print("z")
return(list(tmp))
code = inspect.getsource(checkme)
lines = code.split("\n")
for line in lines:
print(line,"np.random.choice" in line)
输出:
# def checkme(z,y=4): False
# tmp = np.random.choice(x,size=y) True
# if z: False
# print("z") False
# return(list(tmp)) False
# False
我拆分了代码字符串,以检查该方法在哪一行上准确调用
此方法的问题在于别名
例如,如果您的学生是以其他形式输入的numpy,则
import numpy as mynp
import numpy
import numpy as npy
或者当然是注释代码:
# np.random.choice
等
here有关类检查的一些详细信息。
本文链接:https://www.f2er.com/1339673.html