Как я могу получить исходный код функции Python?

Предположим, у меня есть функция Python, как определено ниже:

def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a

Я могу получить имя функции, используя foo.func_name, Как я могу программно получить его исходный код, как я напечатал выше?

16 ответов

Если функция из исходного файла, доступного в файловой системе, то inspect.getsource(foo) может быть полезным:

Если foo определяется как:

def foo(arg1,arg2):         
    #do something with args 
    a = arg1 + arg2         
    return a  

Затем:

import inspect
lines = inspect.getsource(foo)
print(lines)

Возвращает:

def foo(arg1,arg2):         
    #do something with args 
    a = arg1 + arg2         
    return a                

Но я считаю, что если функция скомпилирована из строки, потока или импортирована из скомпилированного файла, то вы не сможете получить ее исходный код.

Модуль inspect имеет методы для извлечения исходного кода из объектов Python. По-видимому, это работает только в том случае, если источник находится в файле. Если бы у вас было это, я думаю, вам не нужно было бы получать источник от объекта.

Если вы используете IPython, то вам нужно набрать "foo??"

In [19]: foo??
Signature: foo(arg1, arg2)
Source:
def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a

File:      ~/Desktop/<ipython-input-18-3174e3126506>
Type:      function

dis Ваш друг, если исходный код недоступен:

>>> import dis
>>> def foo(arg1,arg2):
...     #do something with args
...     a = arg1 + arg2
...     return a
...
>>> dis.dis(foo)
  3           0 LOAD_FAST                0 (arg1)
              3 LOAD_FAST                1 (arg2)
              6 BINARY_ADD
              7 STORE_FAST               2 (a)

  4          10 LOAD_FAST                2 (a)
             13 RETURN_VALUE

Хотя я бы вообще согласился, что inspect хороший ответ, я бы не согласился с тем, что вы не можете получить исходный код объектов, определенных в интерпретаторе. Если вы используете dill.source.getsource от dill Вы можете получить источник функций и лямбд, даже если они определены в интерактивном режиме. Он также может получить код для связанных или несвязанных методов и функций класса, определенных в карри... однако вы не сможете скомпилировать этот код без кода вмещающего объекта.

>>> from dill.source import getsource
>>> 
>>> def add(x,y):
...   return x+y
... 
>>> squared = lambda x:x**2
>>> 
>>> print getsource(add)
def add(x,y):
  return x+y

>>> print getsource(squared)
squared = lambda x:x**2

>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x*x+x
... 
>>> f = Foo()
>>> 
>>> print getsource(f.bar)
def bar(self, x):
    return x*x+x

>>> 

Чтобы расширить ответ Руны:

>>> def foo(a):
...    x = 2
...    return x + a

>>> import inspect

>>> inspect.getsource(foo)
u'def foo(a):\n    x = 2\n    return x + a\n'

print inspect.getsource(foo)
def foo(a):
   x = 2
   return x + a

РЕДАКТИРОВАТЬ: Как указано @0sh, этот пример работает с использованием ipython но не ясно python, Однако при импорте кода из исходных файлов все должно быть хорошо.

Поскольку этот пост помечен как дубликат этого другого поста, я отвечу здесь за случай "лямбда", хотя ОП не касается лямбд.

Таким образом, для лямбда-функций, которые не определены в своих собственных строках: в дополнение к ответу marko.ristin вы можете использовать мини-лямбду или SymPy, как предложено в этом ответе.

  • mini-lambda легче и поддерживает любые операции, но работает только для одной переменной
  • SymPy тяжелее, но гораздо больше оснащено математическими / вычислительными операциями. В частности, это может упростить ваши выражения. Он также поддерживает несколько переменных в одном выражении.

Вот как вы можете сделать это, используя mini-lambda:

from mini_lambda import x, is_mini_lambda_expr
import inspect

def get_source_code_str(f):
    if is_mini_lambda_expr(f):
        return f.to_string()
    else:
        return inspect.getsource(f)

# test it

def foo(arg1, arg2):
    # do something with args
    a = arg1 + arg2
    return a

print(get_source_code_str(foo))
print(get_source_code_str(x ** 2))

Это правильно дает

def foo(arg1, arg2):
    # do something with args
    a = arg1 + arg2
    return a

x ** 2

Увидеть mini-lambda документация для деталей. Я, кстати, автор;)

Ты можешь использовать inspect модуль, чтобы получить полный исходный код для этого. Вы должны использовать getsource() метод для этого из inspect модуль. Например:

import inspect

def get_my_code():
    x = "abcd"
    return x

print(inspect.getsource(get_my_code))

Вы можете проверить это больше вариантов по ссылке ниже. получить ваш код Python

Пожалуйста, обратите внимание, что принятые ответы работают, только если лямбда дана в отдельной строке. Если вы передадите его в качестве аргумента функции и захотите получить лямбда-код в виде объекта, проблема становится немного сложнее, поскольку inspect даст вам всю линию.

Например, рассмотрим файл test.py:

import inspect

def main():
    x, f = 3, lambda a: a + 1
    print(inspect.getsource(f))

if __name__ == "__main__":
    main()

Выполнение этого дает вам (помните абзац!):

    x, f = 3, lambda a: a + 1

Чтобы получить исходный код лямбды, лучше всего, на мой взгляд, повторно проанализировать весь исходный файл (используя f.__code__.co_filename) и сопоставьте лямбда-узел AST по номеру строки и ее контексту.

Мы должны были сделать именно это в нашей библиотеке конструирования по контракту icontract, поскольку нам пришлось анализировать лямбда-функции, которые мы передаем в качестве аргументов декораторам. Здесь слишком много кода для вставки, поэтому взгляните на реализацию этой функции.

Подвести итоги:

import inspect
print( "".join(inspect.getsourcelines(foo)[0]))

Если вы строго определяете функцию самостоятельно, и это относительно короткое определение, решение без зависимостей будет состоять в том, чтобы определить функцию в строке и присвоить eval() выражения вашей функции.

Например

funcstring = 'lambda x: x> 5'
func = eval(funcstring)

затем при желании присоединить исходный код к функции:

func.source = funcstring

В ответе Рафала Довгирда говорится:

Я считаю, что если функция скомпилирована из строки, потока или импортирована из скомпилированного файла, то вы не сможете получить ее исходный код.

Тем не менее, это возможно , чтобы получить исходный код функции , составленную из строки, при условии , что компиляции кода также добавила запись в linecache.cache диктат:

      import linecache
import inspect

script = '''
def add_nums(a, b):
    return a + b
'''

bytecode = compile(script, 'unique_filename', 'exec')
tmp = {}
eval(bytecode, {}, tmp)
add_nums = tmp["add_nums"]

linecache.cache['unique_filename'] = (
    len(script),
    None,
    script.splitlines(True),
    'unique_filename',
)

print(inspect.getsource(add_nums))

# prints:
# """
# def add_nums(a, b):
#    return a + b
# """

Вот как attrsбиблиотека автоматически создает различные методы для классов, учитывая набор атрибутов, с которыми класс ожидает инициализировать. Смотрите их исходный код здесь . Как поясняется в источнике, эта функция в первую очередь предназначена для того, чтобы отладчики, такие как PDB, могли выполнять пошаговое выполнение кода.

Если все предыдущие отличные методы не помогли, вы все равно застряли (!!), вы можете попытаться завершить работу функции и использоватьtracebackчтобы получить хотя бы часть кода.

      In [1]: import traceback
   ...: def foo(arg1,arg2):
   ...:     #do something with args
   ...:     a = arg1 + arg2
   ...:     return a
   ...: 

In [2]: try:
   ...:     foo(None, None)
   ...: except Exception as e:
   ...:     print(repr(e))
   ...:     print(traceback.format_exc())
   ...: 
TypeError("unsupported operand type(s) for +: 'NoneType' and 'NoneType'")
Traceback (most recent call last):
  File "<ipython-input-2-5e7289999181>", line 2, in <module>
    foo(None, None)
  File "<ipython-input-1-db29b8b8f18d>", line 4, in foo
    a = arg1 + arg2
TypeError: unsupported operand type(s) for +: 'NoneType' and 'NoneType'

Возможно, потребуется проявить творческий подход, и это не сработает, если функция не имеет параметров.

Если вы используете IPython, другой вариант заключается в использовании ?? после имени функции, чтобы увидеть ее исходный код. Вот пример:

[nav] In [1]: def foo(arg1,arg2):
         ...:     #do something with args
         ...:     a = arg1 + arg2
         ...:     return a

[nav] In [2]: foo??
Signature: foo(arg1, arg2)
Docstring: <no docstring>
Source:
def foo(arg1,arg2):
    #do something with args
    a = arg1 + arg2
    return a

Я считаю, что имена переменных не хранятся в файлах pyc/pyd/pyo, поэтому вы не можете получить точные строки кода, если у вас нет исходных файлов.

      from dis import show_code
show_code(XXX)
Другие вопросы по тегам