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?

X10
sumber

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.

pengguna28409
sumber
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
Albert Vonpupp
sumber
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. :)

Markus Jarderot
sumber
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.

Demyn
sumber
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"
lapis
sumber
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():
Radato
sumber
16
import inspect

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

dimana

  • stack () [0] pemanggil

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

Ma Guowei
sumber
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)])
Jim G.
sumber
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.

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

Mohsin Ashraf
sumber
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'}
Kode NL23
sumber
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