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

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

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

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

Ответ 1

Если функция принадлежит исходному файлу, доступному в файловой системе, то для inspect.getsource(foo) может потребоваться 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                

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

Ответ 2

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

Ответ 3

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

Ответ 4

Если вы используете 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

Ответ 5

Хотя я обычно соглашаюсь с тем, что 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

>>> 

Ответ 6

Чтобы развернуть ответ runeh:

>>> 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

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

Ответ 7

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

import inspect

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

print(inspect.getsource(get_my_code))

Вы можете проверить дополнительные параметры в приведенной ниже ссылке. получить код python

Ответ 8

:

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

Ответ 9

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

например.

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

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

func.source = funcstring

Ответ 10

Имейте в виду, что принятые ответы работают только в том случае, если лямбда указана на отдельной строке. Если вы передадите его в качестве аргумента функции и хотите получить код лямбда в качестве объекта, проблема будет немного сложной, так как 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 с номером строки и ее контекстом.

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

Ответ 11

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

Ответ 12

Если вы используете 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

Ответ 13

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

Таким образом, для лямбда-функций, которые не определены в своих собственных строках: в дополнение к ответу 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 документацию для деталей. Я, кстати, автор;)