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