8

以下代码引发异常:

import inspect

def work():
    my_function_code = """def print_hello():
                              print('Hi!')
                       """
    exec(my_function_code, globals())
    inspect.getsource(print_hello)

上面的代码抛出异常 IOError。如果我在不使用 exec 的情况下声明函数(如下所示),我可以获得它的源代码。

import inspect

def work():
    def print_hello():
        print('Hi!')
    inspect.getsource(print_hello)

我有充分的理由做这样的事情。

有解决方法吗?有可能做这样的事情吗?如果不是,为什么?

4

3 回答 3

6

在阅读@jsbueno 的答案后,我刚刚查看了inspect.py文件,这就是我发现的:

def findsource(object):
    """Return the entire source file and starting line number for an object.

    The argument may be a module, class, method, function, traceback, frame,
    or code object.  The source code is returned as a list of all the lines
    in the file and the line number indexes a line in that list.  An **IOError
    is raised if the source code cannot be retrieved.**"""
    try:
        file = open(getsourcefile(object))  
    except (TypeError, IOError):
        raise IOError, 'could not get source code'
    lines = file.readlines()               #reads the file
    file.close()

它清楚地表明它试图打开源文件然后读取其内容,这就是为什么在exec.

于 2012-08-22T12:16:18.047 回答
4

这甚至是不可能的。python 获取它正在运行的任何代码的源代码的方法是将源代码文件加载到磁盘上。它通过查看__file__代码模块上的属性来定位该文件。

用于通过“exec”或“compiled”生成代码对象的字符串不会被这些调用产生的对象保留。

如果您__file__在生成的代码的全局字典上设置一个变量,然后在调用inspect.getsource.

于 2012-08-22T11:54:00.723 回答
0

这个解决方案当时并不存在,但从 Python 3.4 开始就存在了!

您现在可以修补 linecache.getlines 以使 inspect.getsource() 与来自 exec() 的代码一起工作。当您查看错误堆栈时,它会在 inspect.py 中的 findsource() 处停止。当你查看findsource() 的代码时,你会看到一个提示:

# Allow filenames in form of "<something>" to pass through.
# `doctest` monkeypatches `linecache` module to enable
# inspection, so let `linecache.getlines` to be called.

然后,如果您查看此测试功能,您将了解它的含义。您可以临时更改核心 Python 函数之一以满足您的目的。

无论如何,这是从 Python 3.4 开始的解决方案:

import linecache
import inspect

def exec_getsource(code):
    getlines = linecache.getlines
    def monkey_patch(filename, module_globals=None):
        if filename == '<string>':
            return code.splitlines(keepends=True)
        else:
            return getlines(filename, module_globals)
    linecache.getlines = monkey_patch
    
    try:
        exec(code)
        #you can now use inspect.getsource() on the result of exec() here
        
    finally:
        linecache.getlines = getlines
于 2021-10-21T21:29:11.173 回答