Як я можу отримати вихідний код функції Python?


406

Припустимо, у мене функція Python, як визначено нижче:

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

Я можу отримати назву функції за допомогою foo.func_name. Як я можу програмно отримати його вихідний код, як я вводив вище?



1
Зауважте, в Python 3 ви можете отримати ім'я функції, використовуючиfoo.__name__
MikeyE

Ви також можете отримати багато іншого .
not2qubit

Відповіді:


541

Якщо функція є з вихідного файлу, доступного у файловій системі, то, 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
Повертається кортеж; tuple [0] - це список рядків, що представляють рядки вихідного коду, а tuple [1] - номер рядка в контексті виконання, де він був запущений. В IPython; це номер рядка в комірці, а не загальний зошит
Red Pea

12
Ця відповідь прямо не згадує її, але inspect.getsource (foo) повертає джерело в одній строці замість кортежу, де tuple [0] - це список рядків. getource стане кориснішим, якщо вам потрібно зазирнути у відповідь
whaley

вона не працює, наприклад, з функцією len. Де я можу знайти вихідний код lenфункції?
oaklander114

1
абоinspect.getsourcelines(foo)
Славомір Ленарт

1
@ oaklander113 inspect.getsource не працює із вбудованими модулями, як і більшість функцій зі стандартної бібліотеки. Ви можете перевірити вихідний код для cpython на їхньому веб-сайті або у їх Github
Nicolas Abril

183

Модуль перевірки має методи для отримання вихідного коду з об'єктів python. Здається, він працює лише в тому випадку, якщо джерело знаходиться у файлі. Якби у вас це було, я думаю, вам не потрібно було б отримувати джерело від об'єкта.


3
Так, це, здається, працює лише для об'єктів, визначених у файлі. Не для тих, що визначені в перекладачі.
зустріч

3
на моє здивування, він працює і в зошитах Ipython / Jupyter
Dr. Goulu

1
Я спробував використати інспектор у python 3.5.3перекладачі. import inspect+ inspect.getsource(foo)добре працював.
Андре К. Андерсен

@ AndréChristofferAndersen Так, але це не повинно працювати для функцій, визначених у перекладачі
хтось

86

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

1
Кидає TypeError для вбудованих.
Номенон

8
@Noumenon, оскільки вони зазвичай не мають вихідного коду в Python, вони написані на C
schlamar

83

Якщо ви використовуєте 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

9
Дуже корисно для ноутбука IPython та Jupyter, якщо / коли ви випадково видалите більше однієї комірки, що містить функції, які ви тільки що провели день, створюючи та тестуючи ....
AGS

Кому, хто втратив увесь клас: ви можете відновити його методом методом:, dir(MyClass)тоді MyClass.__init__??і так далі.
Валерій

61

Хоча я, як правило, погоджуюся, що inspectце хороша відповідь, я не погоджуюся, що ви не можете отримати вихідний код об'єктів, визначений в інтерпретаторі. Якщо ви користуєтесь dill.source.getsourceз dill, ви можете отримати джерело функцій та лямбда, навіть якщо вони визначені інтерактивно. Він також може отримати код для зв'язаних чи незв'язаних методів та функцій класу, визначених у curries ... однак, ви, можливо, не зможете скласти цей код без коду об'єкта, що додається.

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

>>> 

1
@ Ant6n: ну це просто підлий. dill.source.getsourceперевіряє історію інтерпретатора на предмет функцій, класів, лямбда і т.д. - він не перевіряє вміст рядків, переданих у exec.
Майк Маккернс

Це здається дуже цікавим. Чи можна використовувати dillдля відповіді на це питання: stackoverflow.com/questions/13827543 / ...
ArtOfWarfare

@ArtOfWarfare: частково, так. dill.sourceмає такі функції, як getnameі importableта, getsourceякі зосереджуються на отриманні вихідного коду (або імпорту, який дає об'єкт) для будь-якого даного об'єкта. Для простих речей, таких як a int, немає джерела, тому воно не працює так, як очікувалося (тобто для 'a = 10' воно повертає '10').
Майк Маккернс

Однак це працює і для глобальних: >>> a = 10; print( [key for key, val in globals().items() if val is a][0] )
Майк Маккернс

@MikeMcKerns: Я зробив усе можливе, щоб відповісти на це питання без використання dill, але моя відповідь залишає бажати кращого (IE, якщо у вас є кілька імен для одного і того ж значення, він не може з'ясувати, яке було використано. Якщо ви передайте вираз, він не може сказати, що це за вираз. Гек, якщо ви передасте вираз, який оцінює те саме, що ім'я, він дасть це ім'я замість цього.) Можна dillвирішити будь-який з цих недоліків моєї відповіді тут: stackoverflow.com/a/28634996/901641
ArtOfWarfare

21

Щоб розгорнути відповідь на 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. Однак це повинно бути добре в обох випадках, коли імпортується код з вихідних файлів.


2
Це не спрацює, оскільки інтерпретатор буде компілювати foo у байт-код і викидати вихідний код, піднімаючи OSError, якщо ви спробуєте запустити getsource(foo).
Міло Вілондек

@ 0sh хороший момент, що стосується перекладача ванільного пітона. Однак наведений вище приклад коду працює при використанні IPython.
TomDotTom

11

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

import inspect

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

print(inspect.getsource(get_my_code))

Ви можете ознайомитись з іншими параметрами за посиланням нижче. отримати свій код python


7

Оскільки ця посада позначена як дублікат цієї іншої публікації , я відповідаю тут за випадок "лямбда", хоча ОП не стосується лямбдів.

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


5

Зауважте, що прийняті відповіді працюють лише в тому випадку, якщо лямбда надається в окремому рядку. Якщо ви передаєте це як аргумент функції і хочете отримати код лямбда як об'єкт, проблема стає трохи хитрою, оскільки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 лямбда за номером рядка та його контекстом.

Ми повинні були зробити це саме в нашій іконтрактній програмі бібліотеки за контрактом, оскільки нам довелося проаналізувати функції лямбда, які ми передаємо як аргументи декораторам. Тут занадто багато коду для вставки, тому подивіться на реалізацію цієї функції .


4

Якщо ви чітко визначаєте функцію самостійно, і це відносно коротке визначення, рішенням без залежностей було б визначити функцію в рядку та призначити вираз eval () для вашої функції.

Напр

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

потім необов'язково приєднати до функції вихідний код:

func.source = funcstring

2
Використання eval () вражає мене як справді, НАДАЛЬНО поганим, якщо ви не пишете якогось інтерактивного інтерпретатора Python. Eval відкриває кардинальні проблеми із безпекою. Якщо ви приймаєте політику лише лінійних літералів eval'ing, ви все одно втрачаєте різноманітні корисні поведінки, починаючи від виділення синтаксису і закінчуючи належним відображенням класів, що містять членів eval'ed.
Марк Е. Хааз

2
Оголошення. @mehaase: безпека, очевидно, тут не проблема. Ваші інші коментарі, хоч і є досить актуальними, хоча я б сказав, відсутність виділення синтаксису - це поєднання помилок IDE і того факту, що python не є гомоніконімічною мовою.
ninjagecko

7
@ninjagecko Безпека - це завжди проблема, коли ви даєте поради широкій громадськості. Більшість читачів приїжджають сюди, тому що вони гуляють питання. Я не думаю, що багато людей збираються копіювати цю відповідь дослівно; натомість вони збираються взяти вивчену концепцію та застосувати її до власної проблеми.
Марк Е. Хааз


0

Я вважаю, що імена змінних не зберігаються у файлах pyc / pyd / pyo, тому ви не можете отримати точні рядки коду, якщо у вас немає вихідних файлів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.