Bagaimana cara mendapatkan nama fungsi sebagai string?


741

Dengan Python, bagaimana cara mendapatkan nama fungsi sebagai string, tanpa memanggil fungsi?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

harus keluar "my_function".

Apakah fungsi tersebut tersedia dalam Python? Jika tidak, ada ide tentang cara mengimplementasikan get_function_name_as_string, dengan Python?


Jawaban:


945
my_function.__name__

Menggunakan __name__adalah metode yang disukai karena berlaku secara seragam. Tidak seperti func_nameitu, ini juga berfungsi pada fungsi bawaan:

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

Juga garis bawah ganda menunjukkan kepada pembaca ini adalah atribut khusus. Sebagai bonus, kelas dan modul memiliki __name__atribut juga, jadi Anda hanya perlu mengingat satu nama khusus.


420
Karena dalam beberapa kasus, Anda mendapatkan beberapa objek fungsi sebagai argumen untuk fungsi Anda, dan Anda perlu menampilkan / menyimpan / memanipulasi nama fungsi itu. Mungkin Anda membuat dokumentasi, teks bantuan, riwayat tindakan, dll. Jadi tidak, Anda tidak selalu meng-hardcoding nama fungsi.
mbargiel

2
@mgargiel: Apa yang ingin saya katakan adalah: seandainya Anda memiliki kelas yang mendefinisikan 100 metode, di mana semua metode hanya pembungkus, yang memanggil metode pribadi, melewati nama pembungkus itu sendiri. Sesuatu seperti ini: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Anda memiliki pilihan lain, yaitu: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Jadi, jawaban dari Albert Vonpupp terlihat lebih baik bagi saya.
Richard Gomes

19
@RichardGomes Satu jawaban sesuai untuk mendapatkan nama di dalam fungsi itu sendiri, yang lain sesuai untuk mendapatkannya dari referensi fungsi. Pertanyaan OP seperti yang tertulis menunjukkan yang terakhir.
Russell Borogove

22
@RichardGomes Sebenarnya saya sampai pada pertanyaan ini mencari solusi untuk masalah ini. Masalah yang saya coba atasi adalah membuat dekorator yang dapat digunakan untuk mencatat semua panggilan saya.
ali-hussain

23
Fungsi @RichardGomes adalah objek kelas satu, bisa ada lebih dari nama yang terikat padanya. Belum tentu demikian f.__name__ == 'f'.
wim

298

Untuk mendapatkan nama fungsi atau metode saat ini dari dalamnya, pertimbangkan:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframejuga berfungsi alih-alih inspect.currentframemeskipun yang terakhir menghindari mengakses fungsi pribadi.

Untuk mendapatkan nama fungsi panggilan, pertimbangkan f_backsebagai dalam inspect.currentframe().f_back.f_code.co_name.


Jika juga menggunakan mypy, itu bisa mengeluh bahwa:

error: Item "None" of "Opsional [FrameType]" tidak memiliki atribut "f_code"

Untuk menekan kesalahan di atas, pertimbangkan:

import inspect
import types
from typing import cast

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

45
+1: Ini adalah jawaban yang ingin saya lihat. Jawaban lain mengasumsikan bahwa penelepon sudah mengetahui nama fungsi, yang tidak masuk akal dalam konteks pertanyaan ini.
Richard Gomes

110
Richard: tidak. ANDA berasumsi bahwa Anda sedang memanggil nama atau func_name pada fungsi Anda secara langsung dalam cakupan yang sama seperti yang didefinisikan, yang sangat sering terjadi. Ingatlah bahwa fungsi adalah objek - fungsi tersebut dapat diedarkan sebagai argumen untuk fungsi lainnya, disimpan dalam daftar / dikte untuk pencarian atau eksekusi selanjutnya, dll.
mbargiel

11
@ paulus_almighty, menggali ke dalam bingkai tumpukan tidak menurut saya abstrak! Bahkan, ini agak kebalikan dari abstrak. Lihat catatan detail implementasi dalam dokumen. Tidak semua implementasi Python akan menyertakan sys._getframe- itu terhubung langsung ke internal CPython.
pengirim

6
Ini hanya berfungsi di dalam fungsi, tetapi pertanyaan menentukan bahwa fungsi tidak boleh dipanggil.
user2357112 mendukung Monica

5
Jika Anda ingin membungkus fungsi Anda dengan sesuatu yang lebih berguna dan mudah diingat, Anda harus mengambil frame 1 seperti sys._getframe(1).f_code.co_name, sehingga Anda dapat mendefinisikan fungsi seperti get_func_name()dan berharap untuk mendapatkan nama fungsi yang diinginkan yang dipanggil.
m3nda

44
my_function.func_name

Ada juga sifat-sifat fungsi yang menyenangkan lainnya. Ketik dir(func_name)untuk mendaftar mereka. func_name.func_code.co_codeadalah fungsi yang dikompilasi, disimpan sebagai string.

import dis
dis.dis(my_function)

akan menampilkan kode dalam format yang hampir dapat dibaca manusia. :)


4
Apa perbedaan antara f .__ name__ dan f.func_name?
Federico A. Ramponi

14
Sam: nama pribadi, nama __ istimewa, ada perbedaan konseptual.
Matthew Trevor

11
Jika seseorang bingung dengan jawaban sebelumnya oleh Matthew, sistem komentar telah menafsirkan beberapa garis bawah sebagai kode untuk dicetak tebal. Lolos dengan backtick, pesan tersebut harus dibaca: __names__bersifat pribadi, __nameskhusus.
gwideman

12
Sebenarnya, saya pikir yang benar adalah _namespribadi (garis bawah tunggal sebelum, hanya sebuah konvensi), __names__adalah istimewa (garis bawah dua kali sebelum dan sesudah). Tidak yakin apakah menggarisbawahi sebelumnya memiliki makna, secara formal atau sebagai konvensi.
MestreLion

8
func_nametidak ada lagi di python3 sehingga Anda perlu menggunakan func.__name__jika Anda ingin kompatibilitas
Daenyth

34

Fungsi ini akan mengembalikan nama fungsi pemanggil.

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

Ini seperti jawaban Albert Vonpupp dengan bungkus yang ramah.


1
Saya memiliki "<module>" di index [2], tetapi yang berikut berfungsi: traceback.extract_stack (Tidak ada, 2) [0] [- 1]
emmagras

3
bagi saya ini tidak berhasil, tetapi ini tidak: traceback.extract_stack () [- 1] [2]
mike01010

Ini berfungsi jika mengubah indeks pertama ke 1, kalian harus belajar debug sebelum berkomentar ... traceback.extract_stack (Tidak ada, 2) [1] [2]
Jcc.Sanabria

29

Jika Anda tertarik dalam metode kelas juga, Python 3.3+ memiliki __qualname__selain __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

Saya suka menggunakan fungsi dekorator. Saya menambahkan kelas, yang juga kali fungsi waktu. Asumsikan gLog adalah python logger standar:

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

jadi sekarang yang harus Anda lakukan dengan fungsi Anda adalah menghiasnya dan voila

@func_timer_decorator
def my_func():

16
import inspect

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

dimana

  • stack () [0] pemanggil

  • stack () [3] nama string dari metode ini


1
Polos dan sederhana. stack()[0]akan selalu menjadi penelepon, [3]nama string dari metode ini.
kontur

15

Sebagai perpanjangan dari jawaban @ Demyn , saya membuat beberapa fungsi utilitas yang mencetak nama fungsi saat ini dan argumen fungsi saat ini:

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

15

sys._getframe()tidak dijamin tersedia di semua implementasi Python ( lihat ref ), Anda dapat menggunakan tracebackmodul untuk melakukan hal yang sama, misalnya.

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

   return funcName

Panggilan ke stack[-1]akan mengembalikan detail proses saat ini.


Maaf, jika sys._getframe()tidak ditentukan, maka traceback.extract_stackjuga tidak bisa dioperasikan. Yang terakhir memberikan superset kasar dari fungsionalitas yang pertama; Anda tidak dapat berharap melihat satu tanpa yang lain. Dan pada kenyataannya, di IronPython 2.7 extract_stack()selalu kembali []. -1
SingleNegationElimination

8

Anda hanya ingin mendapatkan nama fungsinya di sini adalah kode sederhana untuk itu. katakanlah Anda memiliki fungsi-fungsi ini didefinisikan

def function1():
    print "function1"

def function2():
    print "function2"

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

outputnya adalah function1

Sekarang katakanlah Anda memiliki fungsi-fungsi ini dalam daftar

a = [function1 , function2 , funciton3]

untuk mendapatkan nama fungsi

for i in a:
    print i.__name__

hasilnya akan

function1
function2
function3


5

Saya telah melihat beberapa jawaban yang menggunakan dekorator, meskipun saya merasa beberapa agak bertele-tele. Berikut adalah sesuatu yang saya gunakan untuk nama fungsi logging dan juga nilai input dan output masing-masing. Saya telah mengadaptasinya di sini untuk hanya mencetak info daripada membuat file log dan mengadaptasinya untuk diterapkan pada contoh spesifik 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()

Keluaran:

{'function_name': 'my_function'}

1
Apa yang saya suka tentang ini yang bertentangan dengan yang lain, adalah bahwa dengan hanya menambahkan debugfungsi setelah Anda dapat menambahkan fungsionalitas dengan hanya menambahkan dekorator ke fungsi apa pun yang Anda butuhkan atau ingin mencetak nama fungsi. Tampaknya menjadi yang termudah dan paling mudah beradaptasi.
spareTimeCoder
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.