仅从python文件导入函数

仅从python文件导入函数,python,Python,我有很多Python文件(submission1.py,submission2.py,…,submissionN.py),格式如下: #submission1.py def fun(): print('some fancy function') fun() 我想写一个测试人员来测试这些提交。(它们实际上是我正在评分的家庭作业。)。我有一个fun()测试仪,可以测试函数本身。然而,我的问题是,当我导入submission.py时,它运行fun(),因为它在文件末尾调用它 我知道,使用如果

我有很多Python文件(submission1.py,submission2.py,…,submissionN.py),格式如下:

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

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

我知道,使用
如果uuuu name_uuuu==“uuuu main_uuuuuu”:
是处理这个问题的正确方法,但是,我们的意见书没有它,因为我们没有教授它

因此,我的问题是,是否有任何方法可以只从
submission.py
文件导入
fun()
,而不运行python文件的其余部分?

您可以用来捕获函数定义

只要定义了
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

这段代码可以在许多方面得到改进,但它确实起到了作用。

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

submission1.py:

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

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
calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329
calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35
模块主体包含两个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>]
编译然后导入:

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
calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329
calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35
最后,如果您声明了一些需要使用的变量,可以使用ast.Assign保留它们:

for node in p.body[:]:
    if not isinstance(node, (ast.FunctionDef,
        ast.Import, ast.ImportFrom,ast.Assign)):
        p.body.remove(node)
....
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())
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
calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329
calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35
因此,这实际上完全取决于您的模块是如何构造的,它们应该包含什么,以及您删除了什么。如果实际上只有函数,那么第一个示例将执行您想要的操作。如果需要保留其他部件,只需将其添加到iInstance检查中即可


所有抽象语法定义的列表都在cpython源代码的下。

如果您只想从submission.py导入fun()函数,请尝试

从提交导入乐趣

要执行fun功能,必须包含fun模块 提交:fun()

或者,如果您想让调用fun()函数变得更简单,可以尝试一下 从提交导入乐趣作为乐趣


FUN()

否。但是您可以检查脚本的输出。我想您可以很容易地使用ast.parse修改代码以删除函数调用。如果出于任何原因您不想使用
,如果uuuu name\uuuu==“uuu main\uuuu”:
,那么您不应该在脚本末尾调用
FUN
。@SergeBallesta,问题是我现在有很多这种格式的提交文件。我不想把它们全部检查一遍,然后手动添加
if uuuuu name\uuuuuu==“\uuuuuu main\uuuuuu”:
。我不想自己编写一个脚本来删除给定的Python文件中的最后一行,因为文件中有多个函数,一些函数调用位于两个函数的中间。@ CeleBeaIT是正确的,但是对于未来的学期…你真的能为原始问题中的最小示例编写导入代码吗?@celebisait:是的,给我几分钟时间,这里太晚了:)你怎么能在submission1.py中有导入语句,它将被执行?@celebisait:而不是删除所有不是
FunctionDef
s的节点,您可以删除所有属于
Expr
的节点,该节点的值是
调用
。任务基本上是“仅保留函数”-因此我认为最好只保留FunctionDefs,因为它更详细。无论如何,这是一个非常简洁的答案。@PadraicCunningham,:-)。@Sait,非常欢迎你,想象中的互联网声誉很好,但得到这样的反馈会更有价值,而且值得。首先,如果你作为
表单提交导入,导入乐趣
,然后您只能直接使用
fun()
访问
fun
函数。不适用于
submission.fun()
。其次,这并不能回答这个问题,因为它没有解释如何在不执行提交模块中的某些语句的情况下导入提交模块。