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

回答:


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では、これはノートブック全体ではなく、セル内の行番号です
The Red Pea

12
この回答では明示的に言及していませんが、inspect.getsource(foo)はソースをタプルではなく単一の文字列で返します。ここで、tuple [0]は行のリストです。getsourceは、replをのぞく必要がある場合にさらに便利です
クジラ

たとえば関数では機能しませんlenlen関数のソースコードはどこにありますか?
oaklander114 2016

1
またはinspect.getsourcelines(foo)
SławomirLenart

1
@ oaklander113 inspect.getsourceは、標準ライブラリのほとんどの関数のような組み込み関数では機能しません。cpythonのソースコードは、ウェブサイトまたはGithub
Nicolas Abril

183

モジュールは、検査 Pythonオブジェクトからソースコードを取得するためのメソッドを有します。ソースがファイルにある場合にのみ機能するようです。それがあれば、オブジェクトからソースを取得する必要はないと思います。


3
はい、ファイルで定義されたオブジェクトに対してのみ機能するようです。インタプリタで定義されたものではありません。
サスタニン2009年

3
驚いたことに、これはIpython / Jupyterノートブックでも機能します
博士、

1
python 3.5.3インタプリタでinspectを使ってみました。import inspect+ inspect.getsource(foo)うまくいきました。
アンドレ・C.アンデルセン

@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をスローします。
Noumenon

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__??というように。
Valerij

61

これinspectは良い答えだと私は概ね同意しますが、インタープリターで定義されたオブジェクトのソースコードを取得できないことには同意しません。dill.source.getsourcefrom を使用すると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

>>> 

1
@ Ant6n:まあ、それは卑劣です。 dill.source.getsource関数、クラス、ラムダなどのインタープリターの履歴を検査します。execに渡される文字列の内容は検査しません。
マイクマッカーンズ2014年

これは非常に興味深いようです。それを使用することが可能であるdill:この質問に答えるためにstackoverflow.com/questions/13827543/...
ArtOfWarfare

@ArtOfWarfare:部分的にはい。 およびのdill.sourceような関数がgetnameありimportablegetsource特定のオブジェクトのソースコード(またはオブジェクトを生成するインポート可能ファイル)を取得することに焦点を当てています。ソースintないなどの単純なものの場合、ソースは期待どおりに機能しません(つまり、「a = 10」の場合は「10」を返します)。
Mike McKerns、2015

しかし、これはグローバルのための作業を行います。 >>> a = 10; print( [key for key, val in globals().items() if val is a][0] )
マイクMcKerns

@MikeMcKerns:私はを使用せずにその質問に答えるために最善を尽くしましたdillが、私の答えは少し望まれます(つまり、同じ値に複数の名前がある場合、どちらが使用されたかを理解できません。式を渡すと、その式が何であったかを言うことはできません。名前と同じように評価される式を渡すと、代わりにその名前が与えられます。)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

編集:@ 0shで指摘されているように、この例ipythonはプレーンではなく機能しpythonます。ただし、ソースファイルからコードをインポートする場合はどちらでも問題ありません。


2
インタプリタはfooをバイトコードにコンパイルしてソースコードを破棄し、実行しようとするとOSErrorが発生するため、これは機能しませんgetsource(foo)
Milo Wielondek、2015

バニラパイソンインタプリタに関する限り@ 0sh良い点。ただし、上記のコード例はIPythonを使用する場合に機能します。
TomDotTom 2015年

11

inspectモジュールを使用して、その完全なソースコードを取得できます。モジュールgetsource()からそのためのメソッドを使用する必要がありinspectます。例えば:

import inspect

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

print(inspect.getsource(get_my_code))

以下のリンクで他のオプションを確認できます。 Pythonコードを取得する


7

この投稿はこの他の投稿の重複としてマークされているため、OPはラムダについてではありませんが、ここでは「ラムダ」のケースについて回答します。

したがって、独自の行で定義されていないラムダ関数の場合:marko.ristinの回答に加えて、この回答で提案されているように、mini-lambdaを使用するか、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ノードを行番号とそのコンテキストで照合することです。

デコレータに引数として渡すラムダ関数を解析する必要があったため、契約による設計ライブラリicontractでそれを正確に行う必要がありました。ここに貼り付けるにはコードが多すぎるため、この関数の実装を確認しください。


4

関数を自分で厳密に定義していて、それが比較的短い定義である場合、依存関係のない解決策は、関数を文字列で定義し、式のeval()を関数に割り当てることです。

例えば

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

次に、オプションで元のコードを関数に添付します。

func.source = funcstring

2
eval()の使用は、ある種のインタラクティブなPythonインタープリターを作成しているのでない限り、本当に、本当に悪いと思います。Evalは、重大なセキュリティ問題を引き起こします。文字列リテラルのみを評価するポリシーを採用しても、構文の強調表示から、評価されたメンバーを含むクラスの適切なリフレクションに至るまで、さまざまな役立つ動作を失うことになります。
Mark E. Haase

2
賛成票。@mehaase:セキュリティは明らかに問題ではありません。他のコメントはかなり関連性がありますが、構文の強調表示の欠如は、IDEの障害とpythonが同型言語ではないという事実の組み合わせです。
ninjagecko 2012

7
@ninjagecko一般の人々にアドバイスをするとき、セキュリティは常に問題です。ほとんどの読者は質問をググるのでここに来ています。多くの人がこの答えをそのままコピーするつもりはないと思います。代わりに、彼らは彼らが学んだ概念を取り入れ、それを自分の問題に適用しようとしています。
Mark E. Haase


0

私は信じていますが、ソースファイルを持っていない場合は、正確なコード行を取得することはできませんので、変数名は、PYC / PYD /ピョファイルに格納されていないこと。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.