Làm thế nào để có được một tên hàm như một chuỗi?


741

Trong Python, làm cách nào để lấy tên hàm dưới dạng chuỗi mà không gọi hàm?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

nên đầu ra "my_function".

Là chức năng như vậy có sẵn trong Python? Nếu không, bất kỳ ý tưởng về cách thực hiện get_function_name_as_string, trong Python?


Câu trả lời:


945
my_function.__name__

Sử dụng __name__là phương pháp ưa thích vì nó áp dụng thống nhất. Không giống như func_name, nó cũng hoạt động trên các chức năng tích hợp sẵn:

>>> import time
>>> time.time.func_name
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
AttributeError: 'builtin_function_or_method' object has no attribute 'func_name'
>>> time.time.__name__ 
'time'

Ngoài ra, dấu gạch dưới kép cho người đọc biết đây là một thuộc tính đặc biệt. Là một phần thưởng, các lớp và mô-đun cũng có một __name__thuộc tính, vì vậy bạn chỉ cần nhớ một tên đặc biệt.


420
Bởi vì trong một số trường hợp, bạn lấy một số đối tượng hàm làm đối số cho hàm của mình và bạn cần hiển thị / lưu trữ / thao tác tên của hàm đó. Có lẽ bạn đang tạo tài liệu, văn bản trợ giúp, lịch sử hành động, v.v. Vì vậy, không phải lúc nào bạn cũng mã hóa tên hàm.
mbargiel

2
@mgargiel: Điều tôi muốn nói là: giả sử bạn có một lớp định nghĩa 100 phương thức, trong đó tất cả các phương thức chỉ là các hàm bao, gọi một phương thức riêng, truyền tên của chính trình bao bọc. Một cái gì đó như thế này : def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Bạn có một sự lựa chọn khác, đó là : def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Vì vậy, câu trả lời từ Albert Vonpupp có vẻ tốt hơn đối với tôi.
Richard Gomes

19
@RichardGomes Một câu trả lời thích hợp để lấy tên trong chính hàm đó, câu trả lời khác là thích hợp để lấy nó từ tham chiếu hàm. Câu hỏi của OP như được chỉ ra sau này.
Russell Borogove

22
@RichardGomes Thật ra tôi đến câu hỏi này để tìm giải pháp cho vấn đề này. Vấn đề tôi đang cố gắng giải quyết là tạo ra một trang trí có thể được sử dụng để ghi lại tất cả các cuộc gọi của tôi.
ali-hussain

23
Các hàm @RichardGomes là các đối tượng hạng nhất, có thể có nhiều hơn tên bị ràng buộc với chúng. Nó không nhất thiết phải như vậy f.__name__ == 'f'.
wim

298

Để lấy tên của hàm hoặc phương thức hiện tại từ bên trong nó, hãy xem xét:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframe cũng hoạt động thay vì inspect.currentframe mặc dù sau đó tránh truy cập một chức năng riêng tư.

Để có được tên của chức năng gọi thay thế, hãy xem xét f_backnhư trong inspect.currentframe().f_back.f_code.co_name.


Nếu cũng sử dụng mypy, nó có thể phàn nàn rằng:

lỗi: Mục "Không" của "Tùy chọn [FrameType]" không có thuộc tính "f_code"

Để khắc phục lỗi trên, hãy xem xét:

import inspect
import types
from typing import cast

this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name

45
+1: Đây là câu trả lời tôi muốn xem. Các câu trả lời khác cho rằng người gọi đã biết tên hàm, điều này là vô nghĩa trong bối cảnh của câu hỏi này.
Richard Gomes

110
Richard: không, không. BẠN đang giả sử rằng bạn đang gọi tên hoặc func_name trên hàm của bạn trực tiếp trong cùng phạm vi được xác định, điều này thường không xảy ra. Hãy nhớ rằng các hàm là các đối tượng - chúng có thể được truyền xung quanh dưới dạng đối số cho các hàm khác, được lưu trữ trong danh sách / ký tự để tìm kiếm hoặc thực hiện sau này, v.v.
mbargiel

11
@paulus_almighty, đào sâu vào các khung stack không đánh tôi là trừu tượng! Trong thực tế, nó là loại trái ngược với trừu tượng. Xem ghi chú chi tiết thực hiện trong các tài liệu. Không phải tất cả các triển khai của Python sẽ bao gồm sys._getframe- nó được kết nối trực tiếp với các phần bên trong của CPython.
gửi

6
Điều này chỉ hoạt động bên trong hàm, nhưng câu hỏi chỉ rõ rằng hàm không nên được gọi.
user2357112 hỗ trợ Monica

5
Trong trường hợp bạn muốn bọc hàm của mình thành thứ gì đó dễ sử dụng hơn và dễ nhớ, bạn phải truy xuất khung 1 như thế sys._getframe(1).f_code.co_name, vì vậy bạn có thể xác định một hàm như get_func_name()và mong muốn có được tên mong muốn của hàm đã gọi.
m3nda

44
my_function.func_name

Ngoài ra còn có các tính chất thú vị khác của chức năng. Nhập dir(func_name)để liệt kê chúng. func_name.func_code.co_codelà hàm biên dịch, được lưu trữ dưới dạng chuỗi.

import dis
dis.dis(my_function)

sẽ hiển thị mã ở định dạng gần như có thể đọc được. :)


4
Sự khác biệt giữa f .__ name__ và f.func_name là gì?
Federico A. Ramponi

14
Sam: tên là riêng tư, __names là đặc biệt, có một sự khác biệt về khái niệm.
Matthew Trevor

11
Trong trường hợp ai đó bối rối trước câu trả lời trước của Matthew, hệ thống bình luận đã giải thích một số dấu gạch dưới kép là mã cho chữ in đậm. Thoát với backtick, thông điệp nên đọc: __names__là riêng tư, __nameslà đặc biệt.
gwideman

12
Trên thực tế, tôi nghĩ rằng chính xác là _namesriêng tư (dấu gạch dưới đơn trước, chỉ là quy ước), __names__là đặc biệt (dấu gạch dưới kép trước và sau). Không chắc chắn nếu gạch dưới gấp đôi trước có bất kỳ ý nghĩa, chính thức hoặc như một quy ước.
MestreLion

8
func_namekhông còn tồn tại nữa trong python3, do đó bạn cần sử dụng func.__name__nếu muốn tương thích
Daenyth

34

Hàm này sẽ trả về tên hàm của trình gọi.

def func_name():
    import traceback
    return traceback.extract_stack(None, 2)[0][2]

Nó giống như câu trả lời của Albert Vonpupp với một vỏ bọc thân thiện.


1
Tôi đã có "<mô-đun>" tại chỉ mục [2], nhưng sau đây đã hoạt động: tracBack.extract_stack (Không có, 2) [0] [- 1]
emmagras

3
đối với tôi điều này không hoạt động, nhưng điều này không: tracBack.extract_stack () [- 1] [2]
mike01010

Công trình này nếu thay đổi chỉ số đầu tiên là 1, bạn folks nên học cách debug trước khi cho ý kiến ... traceback.extract_stack (Không, 2) [1] [2]
Jcc.Sanabria

29

Nếu bạn đang quan tâm đến phương pháp lớp quá, Python 3.3+ có __qualname__ngoài __name__.

def my_function():
    pass

class MyClass(object):
    def method(self):
        pass

print(my_function.__name__)         # gives "my_function"
print(MyClass.method.__name__)      # gives "method"

print(my_function.__qualname__)     # gives "my_function"
print(MyClass.method.__qualname__)  # gives "MyClass.method"

20

Tôi thích sử dụng một chức năng trang trí. Tôi đã thêm một lớp, cũng nhân với thời gian chức năng. Giả sử gLog là một logger python tiêu chuẩn:

class EnterExitLog():
    def __init__(self, funcName):
        self.funcName = funcName

    def __enter__(self):
        gLog.debug('Started: %s' % self.funcName)
        self.init_time = datetime.datetime.now()
        return self

    def __exit__(self, type, value, tb):
        gLog.debug('Finished: %s in: %s seconds' % (self.funcName, datetime.datetime.now() - self.init_time))

def func_timer_decorator(func):
    def func_wrapper(*args, **kwargs):
        with EnterExitLog(func.__name__):
            return func(*args, **kwargs)

    return func_wrapper

Vì vậy, bây giờ tất cả những gì bạn phải làm với chức năng của bạn là trang trí nó và thì đấy

@func_timer_decorator
def my_func():

15

sys._getframe()không được đảm bảo có sẵn trong tất cả các triển khai của Python ( xem ref ), bạn có thể sử dụng tracebackmô-đun để làm điều tương tự, ví dụ.

import traceback
def who_am_i():
   stack = traceback.extract_stack()
   filename, codeline, funcName, text = stack[-2]

   return funcName

Một cuộc gọi đến stack[-1]sẽ trả về các chi tiết quá trình hiện tại.


Xin lỗi, nếu sys._getframe()không được xác định, thì traceback.extract_stackcũng không thể hoạt động. Cái sau cung cấp một superset thô về chức năng của cái trước; bạn không thể mong đợi nhìn thấy cái này mà không có cái kia. Và trên thực tế, trong IronPython 2.7 extract_stack()luôn trả về []. -1
Độc thân Tăng tốc

15
import inspect

def foo():
   print(inspect.stack()[0][3])

Ở đâu

  • ngăn xếp () [0] người gọi

  • stack () [3] tên chuỗi của phương thức


1
Đơn giản và đơn giản. stack()[0]sẽ luôn là người gọi, [3]tên chuỗi của phương thức.
kontur

14

Là một phần mở rộng của câu trả lời của @ Demyn , tôi đã tạo một số hàm tiện ích in tên đối số của hàm hiện tại và đối số của hàm hiện tại:

import inspect
import logging
import traceback

def get_function_name():
    return traceback.extract_stack(None, 2)[0][2]

def get_function_parameters_and_values():
    frame = inspect.currentframe().f_back
    args, _, _, values = inspect.getargvalues(frame)
    return ([(i, values[i]) for i in args])

def my_func(a, b, c=None):
    logging.info('Running ' + get_function_name() + '(' + str(get_function_parameters_and_values()) +')')
    pass

logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s [%(levelname)s] -> %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)

my_func(1, 3) # 2016-03-25 17:16:06,927 [INFO] -> Running my_func([('a', 1), ('b', 3), ('c', None)])

8

Bạn chỉ muốn lấy tên của hàm ở đây là một mã đơn giản cho điều đó. giả sử bạn có các hàm này được xác định

def function1():
    print "function1"

def function2():
    print "function2"

def function3():
    print "function3"
print function1.__name__

đầu ra sẽ là hàm1

Bây giờ giả sử bạn có các chức năng này trong một danh sách

a = [function1 , function2 , funciton3]

để có được tên của các chức năng

for i in a:
    print i.__name__

đầu ra sẽ là

function1
function2
function3


5

Tôi đã thấy một vài câu trả lời sử dụng các nhà trang trí, mặc dù tôi cảm thấy một vài câu hơi dài dòng. Đây là một cái gì đó tôi sử dụng để ghi tên hàm cũng như các giá trị đầu vào và đầu ra tương ứng của chúng. Tôi đã điều chỉnh nó ở đây để chỉ in thông tin thay vì tạo tệp nhật ký và điều chỉnh nó để áp dụng cho ví dụ cụ thể của OP.

def debug(func=None):
    def wrapper(*args, **kwargs):
        try:
            function_name = func.__func__.__qualname__
        except:
            function_name = func.__qualname__
        return func(*args, **kwargs, function_name=function_name)
    return wrapper

@debug
def my_function(**kwargs):
    print(kwargs)

my_function()

Đầu ra:

{'function_name': 'my_function'}

1
Điều tôi thích về điều này trái ngược với tất cả những thứ khác, là chỉ cần thêm debugchức năng một lần bạn có thể thêm chức năng bằng cách thêm trình trang trí vào bất kỳ chức năng nào bạn cần hoặc muốn in tên hàm. Nó dường như là dễ nhất và dễ thích nghi nhất.
tùngTimeCoder
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.