Python fonksiyonunun kaynak kodunu nasıl alabilirim?


406

Aşağıda tanımlandığı gibi bir Python fonksiyonum olduğunu varsayalım:

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

Kullanarak işlevin adını alabilirim foo.func_name. Yukarıda yazdığım gibi program aracılığıyla kaynak kodunu nasıl alabilirim?



1
Not, Python 3'te fonksiyon adını alabilirsinizfoo.__name__
MikeyE

Bir çok şey daha alabilirsiniz .
not2qubit

Yanıtlar:


541

İşlev, dosya sisteminde bulunan bir kaynak dosyadan geliyorsa, inspect.getsource(foo)yardımcı olabilir:

Şu fooşekilde tanımlanırsa:

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

Sonra:

import inspect
lines = inspect.getsource(foo)
print(lines)

İadeler:

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

Ancak, işlev bir dizeden derlenmiş, akışlı veya derlenmiş bir dosyadan içe aktarılmışsa, kaynak kodunu alamayacağınıza inanıyorum.


2
Bir demet döndürür ; tuple [0], kaynak kod satırlarını temsil eden dizelerin listesidir ve tuple [1], çalıştırıldığı yerde yürütme bağlamındaki satır numarasıdır. IPython'da; bu not defterinde hücre içindeki satır numarası değil
Kırmızı Bezelye

12
Bu yanıt açık bir şekilde bahsetmez, ancak inspect.getsource (foo) kaynağı, tuple [0] 'ın satırların bir listesi olduğu bir demet yerine tek bir dizede döndürür. Repal bakmak gerekiyorsa getsource daha yararlı olacaktır
whaley

işlev ile çalışmaz len. lenİşlevin kaynak kodunu nerede bulabilirim ?
oaklander114

1
veyainspect.getsourcelines(foo)
Sławomir Lenart

1
@ oaklander113 inspect.getsource, standart kitaplıktaki işlevlerin çoğu gibi yerleşik öğelerle çalışmaz. En CPython için kaynak kodunu kontrol edebilirsiniz kendi web sitesinde veya onların Github
Nicolas Abril

183

Modül kontrol piton nesnelerden kaynak kodunu almak için yöntemler vardır. Görünüşe göre sadece kaynak bir dosyada bulunuyorsa çalışır. Eğer buna sahip olsaydın, sanırım kaynağı nesneden almana gerek kalmazdı.


3
Evet, yalnızca bir dosyada tanımlanan nesneler için çalışıyor gibi görünüyor. Tercümanda tanımlananlar için değil.
sastanin

3
benim için sürpriz, Ipython / Jupyter defterlerinde de çalışıyor
Dr.

1
Bir python 3.5.3tercümanda teftiş kullanmayı denedim . import inspect+ iyi inspect.getsource(foo)çalıştı.
André C. Andersen

@ AndréChristofferAndersen Evet, ancak yorumlayıcıda tanımlanan işlevler için çalışmamalıdır
biri

86

dis kaynak kodu yoksa arkadaşınızdır:

>>> 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
Yerleşikler için bir TypeError atar.
Noumenon

8
@Noumenon, genellikle Python'da kaynak kodu bulunmadığından, C
schlamar

83

IPython kullanıyorsanız, "foo ??" yazmanız gerekir

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 ve Jupyter not defterinde çok yararlı, kazara fonksiyonlar içeren birden fazla hücreyi yanlışlıkla silerseniz / sildiğinizde, gün oluşturup test ederek geçirdiniz ....
AGS

Bütün sınıf kaybetti kime, için: yöntemle bunu yöntemini geri yükleyebilirsiniz: dir(MyClass)ardından MyClass.__init__??vb.
Valerij

61

Genelde inspectbunun iyi bir cevap olduğunu kabul etsem de, tercümanda tanımlanan nesnelerin kaynak kodunu alamayacağınıza katılmıyorum. Eğer kullanırsanız dill.source.getsourcedan dill, onlar interaktif tanımlanmış olsa dahi, işlevleri ve lambdas kaynağını elde edebilirsiniz. Ayrıca, kodları curries'de tanımlanan bağlı veya bağlı olmayan sınıf yöntemlerinden ve işlevlerinden de alabilir ... ancak, bu kodu çevreleyen nesnenin kodu olmadan derleyemeyebilirsiniz.

>>> 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: bu sadece sinsi. dill.source.getsourcetercümanın geçmişini işlevler, sınıflar, lambdalar, vb. açısından inceler - exec'e aktarılan dizelerin içeriğini denetlemez.
Mike McKerns

Bu çok ilginç görünüyor. dillBu soruya cevap vermek için kullanmak mümkün mü : stackoverflow.com/questions/13827543/…
ArtOfWarfare

@ArtOfWarfare: kısmen, evet. ve herhangi bir nesne için kaynak kodu (veya nesneyi veren bir içe aktarılabilir) almaya odaklanan ve dill.sourcegibi işlevlere sahiptir . Bir gibi basit şeyler için orada hiçbir beklendiği gibi işi değil bu yüzden (yani 'a = 10' için '10' döndürür), kaynak. getnameimportablegetsourceint
Mike McKerns

Ancak bu küreseller için işe yarıyor: >>> a = 10; print( [key for key, val in globals().items() if val is a][0] )
Mike McKerns

@MikeMcKerns: Kullanmadan bu soruyu cevaplamak için elimden geleni yaptım dill, ancak cevabım biraz arzulanan bir şey bırakıyor (IE, aynı değer için birden fazla adınız varsa, hangisinin kullanıldığını anlayamazsanız. bir ifadeye geçmek, bu ifadenin ne olduğunu söyleyemez. Heck, bir adla aynı olarak değerlendirilen bir ifadeyi iletirseniz, bunun yerine bu adı verir.) dillCevabımdaki eksikliklerin herhangi birini çözebilir burada: stackoverflow.com/a/28634996/901641
ArtOfWarfare

21

Runeh cevabını genişletmek için:

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

DÜZENLEME: @ 0sh ile işaret edildiği gibi, bu örnek ipythondüz fakat değil kullanılarak çalışır python. Bununla birlikte, kaynak dosyalardan kod alınırken her ikisinde de iyi olmalıdır.


2
Tercüman bayt kodlamak ve kaynak kodunu atmak için foo'yu derleyeceği için çalışmayacaktır getsource(foo).
Milo Wielondek

@ 0sh vanilya python yorumlayıcı açısından iyi bir nokta. Ancak yukarıdaki kod örneği IPython kullanırken çalışır.
TomDotTom

11

Bunun inspectiçin tam kaynak kodunu almak üzere modülü kullanabilirsiniz . Bunun getsource()için inspectmodülden bir yöntem kullanmalısınız . Örneğin:

import inspect

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

print(inspect.getsource(get_my_code))

Aşağıdaki bağlantıdan daha fazla seçeneğe göz atabilirsiniz. python kodunu al


7

Bu yazı diğer yazının kopyası olarak işaretlendiğinden , OP lambdaslarla ilgili olmasa da, burada "lambda" davası için cevap veriyorum.

Bu nedenle, kendi satırlarında tanımlanmayan lambda işlevleri için: marko.ristin'in cevabına ek olarak , mini lambda kullanmak veya bu cevapta önerildiği gibi SymPy kullanmak isteyebilirsiniz .

  • mini-lambda daha hafiftir ve her türlü işlemi destekler, ancak yalnızca tek bir değişken için çalışır
  • SymPydaha ağırdır ancak matematik / matematik işlemleri ile çok daha donatılmıştır. Özellikle ifadelerinizi basitleştirebilir. Aynı ifadede birkaç değişkeni de destekler.

Bunu kullanarak nasıl yapabileceğiniz aşağıda açıklanmıştır 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))

Doğru şekilde verir

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

x ** 2

Ayrıntılar için mini-lambda belgelere bakın. Bu arada yazarım;)


5

Kabul edilen cevapların sadece lambda ayrı bir satırda verildiğinde işe yaradığını unutmayın. Bir işleve argüman olarak iletirseniz ve lambda kodunu nesne olarak almak isterseniz, sorun inspectsize biraz hileli olur çünkü size tüm satırı verecektir.

Örneğin, bir dosyayı göz önünde bulundurun test.py:

import inspect

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

if __name__ == "__main__":
    main()

Bunu yapmak size (karar verdiğinizi unutmayın) verir:

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

Lambda'nın kaynak kodunu almak için, en iyi bahis, bence, tüm kaynak dosyayı yeniden kullanarak (kullanarak f.__code__.co_filename) ve lambda AST düğümünü satır numarası ve bağlamıyla eşleştirmektir.

Tasarım-kütüphaneli ikon ikonumuzda , dekoratörlere argüman olarak aktardığımız lambda fonksiyonlarını ayrıştırmamız gerektiğinden tam olarak yapmalıydık . Buraya yapıştırmak için çok fazla kod var, bu yüzden bu işlevin uygulanmasına bir göz atın .


4

İşlevi tam olarak kendiniz tanımlıyorsanız ve bu nispeten kısa bir tanımsa, bağımlılık içermeyen bir çözüm, işlevi bir dizede tanımlamak ve ifadenin eval () işlevine işlevinizi atamak olacaktır.

Örneğin

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

daha sonra isteğe bağlı olarak orijinal kodu işleve eklemek için:

func.source = funcstring

2
Eval () kullanımı, bir tür interaktif Python tercümanı yazmıyorsanız, gerçekten, gerçekten kötü olduğumu gösteriyor. Eval ciddi güvenlik problemleri yaratır. Yalnızca dize değişmez değerlerini değerlendirme ilkesini benimserseniz, sözdizimi vurgulamasından değerlendirilen üyeleri içeren sınıfların doğru yansımasına kadar çeşitli yararlı davranışları kaybedersiniz.
Mark E. Haase

2
Upvoting. @mehaase: Güvenlik burada bir sorun değil. Diğer yorumlarınız oldukça alakalı olsa da, sözdizimi vurgulama eksikliğinin IDE'nin arızasının ve python'un homoik bir dil olmadığı gerçeğinin bir kombinasyonu olduğunu söyleyebilirim.
ninjagecko

7
@ninjagecko Genel halka tavsiye verirken güvenlik her zaman bir konudur. Çoğu okuyucu buraya geliyor çünkü soru soruyorlar. Pek çok insanın bu yanıtı aynen kopyalayacağını sanmıyorum; bunun yerine, öğrendikleri kavramı alıp kendi sorunlarına uygulayacaklardır.
Mark E. Haase

4

özetlemek :

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

0

Ben inanıyorum kaynak dosyaları yoksa sen tam kod satırlarını alamaz so değişken adları, pyc / PYD / pyo dosyalarında saklanan olmadığını.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.