确定函数是否包含特定方法

我想编写一些代码来检查学生的提交内容,以确保给定的功能包括np.random.choice

例如:

import numpy as np

def checkme(z,x=[1,2,3],y=4):
  tmp = np.random.choice(x,size=y)
  if z:
    print("z")
  return(list(tmp))

我看到我可以使用类似的呼叫

tmp = inspect.signature(checkme)
for param in tmp.parameters.values():
  print(param.name,",param.default)

要确定参数和值,这很好,但是我想更进一步,并确保该函数的主体包括特定的函数或方法。因此,在上面,我想确保学生的代码包含np.random.choice

如何访问函数主体以“检查”并确定这是对还是错?

qq568957159 回答:确定函数是否包含特定方法

您可以使用包装器临时替换要检查的方法,该包装器将通过全局变量(或您选择的其他选项)让您知道是否调用了该方法。我认为这是唯一真正的解决方案,因为使用字符串匹配进行检查和检查像我建议的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_METHODCALL_FUNCTION_EXCALL_FUNCTION_KWCALL_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

大家都在问