Làm cách nào tôi có thể lấy mã nguồn của hàm Python?


406

Giả sử tôi có một hàm Python như được định nghĩa dưới đây:

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

Tôi có thể lấy tên của hàm bằng cách sử dụng foo.func_name. Làm thế nào tôi có thể lập trình lấy mã nguồn của nó, như tôi đã gõ ở trên?



1
Lưu ý, trong Python 3, bạn có thể lấy tên hàm bằng cách sử dụngfoo.__name__
MikeyE

Bạn có thể nhận được rất nhiều thứ khác là tốt.
not2qubit

Câu trả lời:


541

Nếu chức năng là từ một tệp nguồn có sẵn trên hệ thống tệp, thì inspect.getsource(foo)có thể hữu ích:

Nếu foođược định nghĩa là:

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

Sau đó:

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

Trả về:

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

Nhưng tôi tin rằng nếu hàm được biên dịch từ một chuỗi, luồng hoặc được nhập từ một tệp được biên dịch, thì bạn không thể truy xuất mã nguồn của nó.


2
Trả về một tuple; tuple [0] là danh sách các chuỗi đại diện cho các dòng mã nguồn và tuple [1] là số dòng trong bối cảnh thực thi nơi nó được chạy. Trong IPython; đây là số dòng trong ô không phải là sổ ghi chép
The Red Pea

12
Câu trả lời này không đề cập rõ ràng đến nó, nhưng notify.getsource (foo) trả về nguồn trong một chuỗi thay vì một tuple trong đó tuple [0] là danh sách các dòng. getsource sẽ hữu ích hơn nếu bạn cần để peek trong repl
Whaley

Nó không hoạt động với ví dụ chức năng len. Tôi có thể tìm mã nguồn cho lenhàm ở đâu?
sồiland114

1
hoặcinspect.getsourcelines(foo)
Sławomir Lenart

1
@ Oaklander113 tests.getsource không hoạt động với các hàm dựng sẵn như hầu hết các chức năng từ thư viện chuẩn. Bạn có thể kiểm tra mã nguồn cho cpython tại trang web của họ
Nicolas Abril

183

Các kiểm tra mô-đun có phương pháp để lấy mã nguồn từ đối tượng python. Dường như nó chỉ hoạt động nếu nguồn được đặt trong một tập tin mặc dù. Nếu bạn có điều đó tôi đoán bạn sẽ không cần lấy nguồn từ đối tượng.


3
Có, nó dường như chỉ hoạt động cho các đối tượng được xác định trong một tệp. Không dành cho những người được xác định trong phiên dịch.
sastanin

3
thật ngạc nhiên, nó cũng hoạt động trong sổ ghi chép của Ipython / Jupyter
Tiến sĩ Goulu

1
Tôi đã thử sử dụng kiểm tra trong một python 3.5.3thông dịch viên. import inspect+ inspect.getsource(foo)làm việc tốt.
André C. Andersen

@ AndréChristofferAndersen Vâng nhưng nó không hoạt động đối với các chức năng được xác định trong trình thông dịch
ai đó

86

dis là bạn của bạn nếu mã nguồn không có sẵn:

>>> 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
Ném một TypeError cho các nội trang.
Noumenon

8
@Noumenon vì chúng thường không có mã nguồn bằng Python, chúng được viết bằng C
schlamar

83

Nếu bạn đang sử dụng IPython, thì bạn cần gõ "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
Rất hữu ích trong sổ ghi chép IPython và Jupyter nếu / khi bạn vô tình xóa nhiều hơn một ô có chứa các chức năng bạn vừa dành cả ngày để tạo và kiểm tra ....
AGS

Đối với ai, người đã mất cả lớp: bạn có thể khôi phục phương thức đó bằng phương thức : dir(MyClass), sau đó MyClass.__init__??, v.v.
Valerij

61

Mặc dù tôi thường đồng ý rằng đó inspectlà một câu trả lời hay, tôi không đồng ý rằng bạn không thể lấy mã nguồn của các đối tượng được xác định trong trình thông dịch. Nếu bạn sử dụng dill.source.getsourcetừ dill, bạn có thể lấy nguồn hàm và lambdas, ngay cả khi chúng được xác định tương tác. Nó cũng có thể lấy mã từ các phương thức và hàm lớp bị ràng buộc hoặc không ràng buộc được xác định trong các dòng chữ ... tuy nhiên, bạn có thể không thể biên dịch mã đó mà không có mã của đối tượng kèm theo.

>>> 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: tốt, đó chỉ là lén lút. dill.source.getsourcekiểm tra lịch sử của trình thông dịch cho các chức năng, các lớp, lambdas, v.v. - nó không kiểm tra nội dung của các chuỗi được truyền cho exec.
Mike McKerns

Điều này có vẻ rất thú vị. Có thể sử dụng dillđể trả lời câu hỏi này không: stackoverflow.com/questions/13827543/ trên
ArtOfWarfare

@ArtOfWarfare: một phần, vâng. dill.sourcecó các chức năng như getnameimportablegetsourcetập trung vào việc lấy mã nguồn (hoặc có thể nhập có thể mang lại đối tượng) cho bất kỳ đối tượng nào. Đối với những thứ đơn giản như một intkhông nguồn, vì vậy nó không làm việc như mong đợi (tức là cho 'a = 10' nó trở về '10').
Mike McKerns

Điều này không hoạt động cho toàn cầu tuy nhiên: >>> a = 10; print( [key for key, val in globals().items() if val is a][0] )
Mike McKerns

@MikeMcKerns: Tôi đã cố hết sức để trả lời câu hỏi đó mà không sử dụng dill, nhưng câu trả lời của tôi để lại một chút mong muốn (IE, nếu bạn có nhiều tên cho cùng một giá trị, nó không thể tìm ra cái nào được sử dụng. Nếu bạn vượt qua trong một biểu thức, nó không thể nói biểu thức đó là gì. Heck, nếu bạn chuyển một biểu thức đánh giá giống như một tên, nó sẽ đưa ra tên đó thay thế.) Có thể dillgiải quyết bất kỳ thiếu sót nào trong câu trả lời của tôi tại đây: stackoverflow.com/a/28634996/901641
ArtOfWarfare

21

Để mở rộng câu trả lời của 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: Như được chỉ ra bởi @ 0sh ví dụ này hoạt động bằng cách sử dụng ipythonnhưng không đơn giản python. Nó sẽ ổn trong cả hai, tuy nhiên, khi nhập mã từ các tệp nguồn.


2
Điều này sẽ không hoạt động, vì trình thông dịch sẽ biên dịch foo thành mã byte và loại bỏ mã nguồn, nâng cao OSError nếu bạn thử chạy getsource(foo).
Milo Wielondek

@ 0sh điểm tốt khi có liên quan đến thông dịch viên trăn vanilla. Tuy nhiên, ví dụ mã trên hoạt động khi sử dụng IPython.
TomDotTom

11

Bạn có thể sử dụng inspectmô-đun để có được mã nguồn đầy đủ cho điều đó. Bạn phải sử dụng getsource()phương pháp cho điều đó từ inspectmô-đun. Ví dụ:

import inspect

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

print(inspect.getsource(get_my_code))

Bạn có thể kiểm tra thêm các tùy chọn trên liên kết dưới đây. lấy mã trăn của bạn


7

Vì bài đăng này được đánh dấu là bản sao của bài đăng khác này , tôi trả lời ở đây cho trường hợp "lambda", mặc dù OP không nói về lambdas.

Vì vậy, đối với các hàm lambda không được xác định trong dòng riêng của chúng: ngoài câu trả lời của marko.ristin , bạn có thể muốn sử dụng mini-lambda hoặc sử dụng SymPy như được đề xuất trong câu trả lời này .

  • mini-lambda nhẹ hơn và hỗ trợ bất kỳ loại hoạt động nào, nhưng chỉ hoạt động cho một biến duy nhất
  • SymPynặng hơn nhưng được trang bị nhiều hơn với các phép toán / phép tính. Đặc biệt nó có thể đơn giản hóa các biểu thức của bạn. Nó cũng hỗ trợ một số biến trong cùng một biểu thức.

Đây là cách bạn có thể làm điều đó bằng cách sử dụng 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))

Nó mang lại chính xác

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

x ** 2

Xem mini-lambda tài liệu để biết chi tiết. Nhân tiện, tôi là tác giả;)


5

Xin lưu ý rằng các câu trả lời được chấp nhận chỉ hoạt động nếu lambda được đưa ra trên một dòng riêng biệt. Nếu bạn chuyển nó dưới dạng đối số cho hàm và muốn lấy mã của lambda làm đối tượng, thì vấn đề sẽ hơi phức tạp vì inspectsẽ cung cấp cho bạn toàn bộ dòng.

Ví dụ: xem xét một tệp test.py:

import inspect

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

if __name__ == "__main__":
    main()

Thực hiện nó mang lại cho bạn (nhớ sự cố gắng!):

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

Để lấy mã nguồn của lambda, theo tôi, cách tốt nhất của bạn là phân tích lại toàn bộ tệp nguồn (bằng cách sử dụng f.__code__.co_filename) và khớp với nút lambda AST theo số dòng và ngữ cảnh của nó.

Chúng tôi đã phải làm một cách chính xác rằng trong thư viện thiết kế theo hợp đồng của chúng tôi icontract kể từ khi chúng tôi phải phân tích các chức năng lambda chúng tôi vượt qua trong như các đối số để trang trí. Đó là quá nhiều mã để dán ở đây, vì vậy hãy xem việc thực hiện chức năng này .


4

Nếu bạn tự xác định hàm một cách nghiêm ngặt và đó là một định nghĩa tương đối ngắn, một giải pháp không phụ thuộc sẽ là xác định hàm trong một chuỗi và gán eval () của biểu thức cho hàm của bạn.

Ví dụ

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

sau đó tùy chọn đính kèm mã gốc vào hàm:

func.source = funcstring

2
Việc sử dụng eval () khiến tôi thực sự rất xấu, trừ khi bạn đang viết một loại trình thông dịch Python tương tác. Eval mở ra các vấn đề an ninh quyết liệt. Nếu bạn áp dụng chính sách chỉ đánh giá chuỗi ký tự, bạn vẫn bỏ qua nhiều hành vi hữu ích khác nhau, từ đánh dấu cú pháp đến phản ánh đúng các lớp có chứa các thành viên bị đánh giá.
Đánh dấu E. Haase

2
Nâng cao. @mehaase: bảo mật rõ ràng không phải là vấn đề ở đây. Các ý kiến ​​khác của bạn mặc dù khá phù hợp, mặc dù tôi nói thiếu đánh dấu cú pháp là sự kết hợp giữa lỗi của IDE và thực tế là python không phải là ngôn ngữ đồng âm.
ninjagecko

7
@ninjagecko Bảo mật luôn là vấn đề khi bạn đưa ra lời khuyên cho công chúng. Hầu hết các độc giả đang đến đây bởi vì họ là những câu hỏi googling. Tôi không nghĩ nhiều người sẽ sao chép câu trả lời này nguyên văn; thay vào đó, họ sẽ lấy khái niệm họ đã học và áp dụng nó vào vấn đề của chính họ.
Đánh dấu E. Haase

4

tóm tắt:

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

0

Tôi tin rằng tên biến không được lưu trong tệp pyc / pyd / pyo, vì vậy bạn không thể truy xuất các dòng mã chính xác nếu bạn không có tệp nguồn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.