仅导入python文件中的函数

时间:2015-05-08 21:40:44

标签: python

我有以下格式的许多Python文件(submission1.py,submission2.py,...,submissionN.py),

#submission1.py
def fun():
   print('some fancy function')

fun()

我想写一个测试人员来测试这些提交。 (它们实际上是我正在评分的作业。)我有fun()的测试人员,可以测试功能本身。但是,我的问题是,当我导入submission.py时,它会运行fun(),因为它在文件末尾调用它。

我知道,使用if __name__ == "__main__":是处理此问题的正确方法,但是,由于我们没有教授它,我们提交的内容没有。

所以,我的问题是,有没有办法只从fun()文件中导入submission.py而不运行其余的python文件?

3 个答案:

答案 0 :(得分:5)

对于只包含函数的简单脚本,以下内容将起作用:

submission1.py

def fun(x):
   print(x)

fun("foo")


def fun2(x):
   print(x)


fun2("bar")

print("debug print")

您可以删除FunctionDef节点然后重新编译的所有栏:

import ast
import types

with open("submission1.py") as f:
   p = ast.parse(f.read())

for node in p.body[:]:
    if not isinstance(node, ast.FunctionDef):
        p.body.remove(node)



module = types.ModuleType("mod")
code = compile(p, "mod.py", 'exec')
sys.modules["mod"] = module
exec(code,  module.__dict__)

import mod

mod.fun("calling fun")
mod.fun2("calling fun2")

输出:

calling fun
calling fun2

模块主体包含两个Expr和一个Print节点,我们在循环中删除它只保留FunctionDef。

[<_ast.FunctionDef object at 0x7fa33357f610>, <_ast.Expr object at 0x7fa330298a90>, 
<_ast.FunctionDef object at 0x7fa330298b90>, <_ast.Expr object at 0x7fa330298cd0>,
 <_ast.Print object at 0x7fa330298dd0>]

所以循环体后只包含函数:

[<_ast.FunctionDef object at 0x7f49a786a610>, <_ast.FunctionDef object at 0x7f49a4583b90>]

这也将捕获使用print调用函数的位置,如果学生从IDE中调用函数,其中函数具有返回语句很可能,也是为了保持任何导入,有任何你可以保持ast.Import的和ast.ImportFrom的:

submission.py:

from math import *
import datetime

def fun(x):
    print(x)


fun("foo")


def fun2(x):
    return x

def get_date():
    print(pi)
    return datetime.datetime.now()
fun2("bar")

print("debug print")

print(fun2("hello world"))

print(get_date())

编译然后导入:

for node in p.body[:]:
    if not isinstance(node, (ast.FunctionDef,ast.Import, ast.ImportFrom)):
        p.body.remove(node)
.....

import mod

mod.fun("calling fun")
print(mod.fun2("calling fun2"))
print(mod.get_date())

输出:

calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329

最后,如果您声明需要使用某些变量,则可以使用ast.Assign保留它们:

submission.py:

from math import *
import datetime

AREA = 25
WIDTH = 35

def fun(x):
    print(x)


fun("foo")


def fun2(x):
    return x

def get_date():
    print(pi)
    return datetime.datetime.now()
fun2("bar")

print("debug print")

print(fun2("hello world"))

print(get_date()

添加ast.Assign:

for node in p.body[:]:
    if not isinstance(node, (ast.FunctionDef,
        ast.Import, ast.ImportFrom,ast.Assign)):
        p.body.remove(node)
....

输出:

calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35

所以这一切都取决于你的模块的结构以及它们应该包含哪些内容。如果只有字面上的函数,那么第一个例子就会做你想要的。如果还有其他部分需要保留,只需将它们添加到isinstance检查中即可。

所有抽象语法定义的列表都在Parser/Python.asdl下的cpython源代码中。

答案 1 :(得分:2)

您可以使用sys.settrace()来捕获函数定义。

每当定义fun()时,您将其保存在某处,并将存根放入要导入的模块中,以免它被执行。

假设fun()只被定义一次,这段代码应该可以解决问题:

import sys

fun = None

def stub(*args, **kwargs):
    pass

def wait_for_fun(frame, event, arg):
    global fun

    if frame.f_code.co_filename == '/path/to/module.py':
        if 'fun' in frame.f_globals:
            # The function has just been defined. Save it.
            fun = frame.f_globals['fun']
            # And replace it with our stub.
            frame.f_globals['fun'] = stub

            # Stop tracing the module execution.
            return None

    return wait_for_fun

sys.settrace(wait_for_fun)
import my_module

# Now fun() is available and we can test it.
fun(1, 2, 3)
# We can also put it again inside the module.
# This is important if other functions in the module need it.
my_module.fun = fun

这段代码可以通过多种方式得到改进,但它确实起到了作用。

答案 2 :(得分:-2)

也许只是想从submitt.py导入fun()函数,请尝试

来自提交导入的乐趣

要执行fun功能,您必须包含fun模块

submission.fun()

或者如果您想在调用fun()函数时更轻松,请尝试一下 从提交导入乐趣为乐趣

乐趣()