Python: 显示对象的实现源

Python: Displaying an object's implementation source

我的任务有点不寻常且出乎意料地令人费解 - 显示特定 class 方法实现的源代码。

In [1]: class Demonstration:
   ...:     def cost():
   ...:         return 42
   ...:     

In [2]: class OtherDemo:
   ...:     def cost():
   ...:         return 43
   ...:  

在此示例中,我想在给定 class.

的适当缩进处找到文本 def cost(): 和以下行

inspectdis 这样的模块几乎就是我要找的,但我想显示 python 代码,例如在回溯期间显示的代码;不是字节码。

我也不反对解析源文件 - 是否有文本编辑器可用于自动完成或缩进计算的库,可以在 class 中找到特定方法?

听起来 inspect 库正是您所需要的,特别是函数 getsourcelines:

In [1]: def foo(x):
   ...:     x += 3
   ...:     x += 4
   ...:     return x
   ...: 

In [2]: import inspect

In [3]: inspect.getsourcelines(foo)
Out[3]: (['def foo(x):\n', '    x += 3\n', '    x += 4\n', '    return x\n'], 1)

In [4]: source_code = _

In [6]: print(''.join(source_code[0]))
def foo(x):
    x += 3
    x += 4
    return x

来自文档:

Return a list of source lines 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 the lines corresponding to the object and the line number indicates where in the original source file the first line of code was found. An IOError is raised if the source code cannot be retrieved.

在 Python 中,由于能够动态修改 任何内容 ,映射回源定义可能非常棘手。毕竟,定义可以即时创建。

这是一个稍微简单的例子。动态定义甚至比这更棘手,如果实现发生在预编译模块中,则尤其棘手。

def make_random_function(coin='Heads'):
    if coin == 'Heads':
        def foo(self, a):
            print a
    elif coin == 'Tails':
        def foo(self, a, b):
            return a + b
    else:
        def foo(self, *args, **kwargs):
            raise ValueError('Invalid coin used to create function.')

    foo.__name__ = "dynamic_foo"
    foo.__doc__ = "Good luck buddy."
    return foo


import random
val = random.random()
if val > 0.51:
    coin = 'Heads'
elif val < 0.49:
    coin = 'Tails'
else:
    coin = 'Trick'

function = make_random_function(coin)
MyType = type("MyType", (object,), {function.__name__:function})

m = MyType()

当我 运行 然后调用 m.dynamic_foo() 我看到了这个:

In [313]: coin
Out[313]: 'Trick'

In [314]: val
Out[314]: 0.5099718112195031

In [315]: m.dynamic_foo()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-315-70b3caeb205b> in <module>()
----> 1 m.dynamic_foo()

<ipython-input-310-475ea0810d8d> in foo(*args, **kwargs)
      8     else:
      9         def foo(*args, **kwargs):
---> 10             raise ValueError('Invalid coin used to create function.')
     11 
     12     foo.__name__ = "dynamic_foo"

ValueError: Invalid coin used to create function.

In [316]: m
Out[316]: <__main__.MyType at 0x7f37e70b3ad0>

即使我使用 inspect.getsourcelines(m.dynamic_foo) 也有点误导:

In [319]: inspect.getsourcelines(m.dynamic_foo)
Out[319]: 
([u'        def foo(self, *args, **kwargs):\n',
  u"            raise ValueError('Invalid coin used to create function.')\n"],
 9)

注意函数的源代码如何显示它的名称是 "foo"(而不是 "dynamic_foo")并且它不是 class 方法或 MyType 的实例方法或任何东西。这在技术上是正确的,因为它是实际的源代码行,但它不一定是某些人可能期望看到的,因为它是一个定义,其存在方式与它如何动态注入 class定义。

这是此类动态函数创建和动态 class 操作的简单示例。这变得越复杂,将检查源代码行作为理解函数实现的合理方法的可靠性就越低。