web-dev-qa-db-ja.com

Python関数のソースコードはどうやって入手できますか?

以下に定義するようなPython関数があるとします。

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

foo.func_nameを使って関数の名前を取得できます。上記で入力したように、プログラムでそのソースコードを取得する方法を教えてください。

336
david

関数がファイルシステムで利用可能なソースファイルからのものである場合、 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                

しかし、関数が文字列、ストリーム、またはコンパイル済みファイルからインポートされたものである場合、そのソースコードを取得することはできません。

465
Rafał Dowgird

inspectモジュール はpythonオブジェクトからソースコードを取得するためのメソッドを持っています。一見、ソースがファイル内にある場合にのみ機能します。あなたがそれを持っていたら私はあなたがオブジェクトからソースを取得する必要はないだろうと思います。

176
runeh

ソースコードが利用できない場合、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
79
schlamar

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

私は一般的にinspectが良い答えであることに同意しますが、私はあなたがインタプリタで定義されたオブジェクトのソースコードを手に入れることができないことに反対します。 dill からdill.source.getsourceを使用すると、対話的に定義されていても、関数とラムダのソースを取得できます。それはまたカレーで定義された束縛されたあるいは束縛されていないクラスメソッドや関数からのコードを取得することができます。

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

>>> 
55
Mike McKerns

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は使用しません。ただし、ソースファイルからコードをインポートする場合は、どちらでも問題ありません。

21
TomDotTom

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

import inspect

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

print(inspect.getsource(get_my_code))

あなたはそれを下のリンクでより多くのオプションをチェックアウトすることができます。 あなたのPythonコードを取得してください

10
Krutarth Gor

要約する :

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

あなた自身が厳密に関数を定義していて、それが比較的短い定義であるならば、依存性のない解決策は文字列で関数を定義して、そしてあなたの関数に式のeval()を割り当てることでしょう。

例えば。

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

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

func.source = funcstring
3
cherplunk

受け入れられた答えはラムダが別の行に与えられている場合にのみ機能することに注意してください。関数の引数として渡して、ラムダのコードをオブジェクトとして取得したい場合は、inspectで行全体が表示されるため、問題は少し複雑になります。

たとえば、ファイルtest.pyを考えます。

import inspect

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

if __== "__main__":
    main()

それを実行することはあなたに与えます(意図を気にしてください!):

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

ラムダのソースコードを取得するには、(f.__code__.co_filenameを使用して)ソースファイル全体を再解析し、行番号とそのコンテキストでlambda ASTノードを一致させるのが最善の策です。

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

2
marko.ristin

この記事は この他の記事 の複製としてマークされているので、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-lambdadocumentation をご覧ください。ところで私は作者です;)

1
smarie

I 信じる変数名はpyc/pyd/pyoファイルに保存されないので、ソースファイルがなければ正確なコード行を取得することはできません。

0
Abgan

IPythonを使用している場合、もう1つの選択肢は、関数名の後に??を使用してそのソースコードを確認することです。これが一例です。

[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
0
Aziz Alto