Dize olarak işlev adı nasıl alınır?


740

Python'da, işlevi çağırmadan bir işlev adını dize olarak nasıl alabilirim?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

çıktı vermelidir "my_function".

Python'da böyle bir işlev var mı? Değilse, get_function_name_as_stringPython'da nasıl uygulanacağına dair herhangi bir fikir var mı?


Yanıtlar:


944
my_function.__name__

__name__Düzgün uygulandığı için kullanmak tercih edilen yöntemdir. Bunun aksine func_name, yerleşik işlevler üzerinde de çalışır:

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

Ayrıca çift alt çizgi okuyucuya bunun özel bir özellik olduğunu gösterir. Bonus olarak, sınıfların ve modüllerin de bir __name__özelliği vardır, bu nedenle sadece bir özel adı hatırlarsınız.


420
Çünkü bazı durumlarda, işlevinize bir argüman olarak bazı işlev nesnelerini alırsınız ve bu işlevin adını görüntülemeniz / depolamanız / işlemeniz gerekir. Belki de dokümantasyon, yardım metni, eylemler geçmişi vb. Üretiyorsunuz. Yani hayır, fonksiyon adını her zaman kodlamıyorsunuz.
mbargiel

2
@mgargiel: Söylemek istediğim şudur: varsayalım 100 yöntemini tanımlayan bir sınıfınız var, burada tüm yöntemler sadece sarmalayıcılardır, burada sarmalayıcı adını taşıyan özel bir yöntemi çağırır. Böyle bir şey: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). Sen başka bir seçenek vardır: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). Yani, Albert Vonpupp'un cevabı bana daha iyi görünüyor.
Richard Gomes

19
@RichardGomes Bir cevap, fonksiyonun kendi içinde isim almak için, diğeri ise fonksiyon referansından almak için uygundur. OP'nin yazılı olarak sorusu ikincisini gösterir.
Russell Borogove

22
@RichardGomes Aslında bu soruya bu soruna bir çözüm aramaya geldim. Adreslemeye çalıştığım sorun, tüm çağrılarımı kaydetmek için kullanılabilecek bir dekoratör oluşturmak.
ali-hussain

23
@RichardGomes işlevleri birinci sınıf nesnelerdir, bunlara bağlı addan daha fazlası olabilir. Her zaman böyle değildir f.__name__ == 'f'.
wim

298

Geçerli işlevin veya yöntemin adını içinden almak için şunları göz önünde bulundurun:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframeinspect.currentframeikincisi özel bir işleve erişmekten kaçınmasına rağmen yerine çalışır .

Bunun yerine arama işlevinin adını almak için f_backolduğu gibi düşünün inspect.currentframe().f_back.f_code.co_name.


Ayrıca kullanıyorsanız mypy, şikayet edebilir:

hata: "İsteğe bağlı [FrameType]" öğesinin "Yok" öğesinin "f_code" özelliği yok

Yukarıdaki hatayı bastırmak için şunları göz önünde bulundurun:

import inspect
import types
from typing import cast

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

45
+1: Bu, görmek istediğim cevap. Diğer cevaplar, arayan kişinin bu soru bağlamında saçma olan işlev adını zaten bildiğini varsayar.
Richard Gomes

110
Richard: Hayır değil. Fonksiyonunuzda doğrudan tanımlandığı kapsamda isim veya işlev_adı çağırdığınızı varsayıyorsunuz , ki bu çoğu zaman böyle değildir. İşlevler nesnelerdir unutmayın - Sonraki aramalar veya yürütme, vs. onlar listelerinde depolanan diğer fonksiyonlara argüman olarak etrafında geçirilebilir / dicts
mbargiel

11
@paulus_almighty, yığın kareleri kazmak beni soyut olarak vurmuyor! Aslında, soyutun tam tersi. Dokümanlardaki uygulama ayrıntı notuna bakın . Python'un tüm uygulamaları içermeyecektir sys._getframe- doğrudan CPython'un içlerine bağlanır.
senderle

6
Bu yalnızca işlevin içinde çalışır, ancak soru işlevin çağrılmaması gerektiğini belirtir.
user2357112 Monica

5
İşlevinizi daha kullanışlı ve hatırlanması kolay bir şeye sarmak istediğinizde u çerçeve 1'i almak zorundasınız sys._getframe(1).f_code.co_name, böylece get_func_name()çağrılan işlevin istenen adını almayı bekleyebilirsiniz.
m3nda

44
my_function.func_name

Fonksiyonların diğer eğlenceli özellikleri de vardır. Listelemek dir(func_name)için yazın. func_name.func_code.co_codedize olarak saklanan derlenmiş işlevdir.

import dis
dis.dis(my_function)

kodu neredeyse insan tarafından okunabilir biçimde görüntüler. :)


4
F .__ name__ ve f.func_name arasındaki fark nedir?
Federico A.Ramponi

14
Sam: isimler özel, __names özel, kavramsal bir fark var.
Matthew Trevor

11
Birisi Matta'nın önceki cevabından şaşkınsa, yorum sistemi bazı çift alt çizgileri kalın kod olarak yorumladı. Backtick ile kaçtı, mesaj okumalıdır: __names__özel, __namesözel.
gwideman

12
Aslında, doğru _namesözel olduğunu düşünüyorum (önce tek alt çizgi, sadece bir kongre), __names__özel (önce ve sonra çift alt çizgi). Önceden çift alt çizginin resmi veya konvansiyon olarak herhangi bir anlamı olup olmadığından emin değilim.
MestreLion

8
func_nameartık python3'te mevcut değil, bu yüzden func.__name__uyumluluk istiyorsanız kullanmanız gerekir
Daenyth

34

Bu işlev arayanın işlev adını döndürür.

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

Albert Vonpupp'un dostça bir sarıcı ile yanıtı gibi.


1
[2] dizininde "<modül>" vardı, ancak aşağıdakiler işe yaradı: traceback.extract_stack (Yok, 2) [0] [- 1]
emmagras

3
benim için bu işe yaramaz, ama bu işe yarıyor: traceback.extract_stack () [- 1] [2]
mike01010

Bu, ilk dizini 1 olarak değiştirirseniz, siz yorum yapmadan önce hata ayıklamayı öğrenmelisiniz ... traceback.extract_stack (Yok, 2) [1] [2]
Jcc.Sanabria 19:18

29

Sınıf yöntemleriyle de ilgileniyorsanız, Python 3.3+ __qualname__ek olarak __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

Bir fonksiyon dekoratörü kullanmayı seviyorum. Bir sınıf ekledim. GLog'un standart bir python günlüğü olduğunu varsayalım:

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

şimdi fonksiyonunuzla tek yapmanız gereken onu süslemek ve işte

@func_timer_decorator
def my_func():

15

sys._getframe()Python'un tüm uygulamalarında kullanılabileceği garanti edilmez ( bkz. ref ), tracebackaynı şeyi yapmak için modülü kullanabilirsiniz , örn.

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

   return funcName

Bir çağrı stack[-1]geçerli işlem ayrıntılarını döndürür.


Üzgünüz, eğer sys._getframe()tanımsızsa, o traceback.extract_stackzaman da çalışmaz. İkincisi, birincisinin işlevselliğinin kaba bir üst kümesini sağlar; birini diğeri olmadan görmeyi bekleyemezsiniz. Ve aslında, IronPython 2.7'de extract_stack()her zaman geri döner []. -1
SingleNegationElimination

15
import inspect

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

nerede

  • çağıran () [0]

  • stack () [3] yöntemin dize adı


1
Sade ve basit. yöntemin dize adı stack()[0]her zaman arayan olacaktır [3].
kontur

14

@ Demyn'in cevabının bir uzantısı olarak , geçerli işlevin adını ve geçerli işlevin bağımsız değişkenlerini yazdıran bazı yardımcı işlevler oluşturdum:

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

Sadece işlevin adını almak istiyorum burada bunun için basit bir kod. Diyelim ki bu işlevler tanımlı

def function1():
    print "function1"

def function2():
    print "function2"

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

çıktı fonksiyon1 olacak

Şimdi bu işlevlerin bir listede olduğunu varsayalım

a = [function1 , function2 , funciton3]

fonksiyonların adını almak için

for i in a:
    print i.__name__

çıktı olacak

işlev1
işlev2
işlev3


5

Dekoratörler kullanan birkaç cevap gördüm, ancak birkaçının biraz ayrıntılı olduğunu hissettim. Burada, giriş ve çıkış değerlerinin yanı sıra işlev adlarını günlüğe kaydetmek için kullandığım bir şey var. Burada bir günlük dosyası oluşturmak yerine bilgileri yazdırmak için uyarladım ve OP'ye özel örneğe uygulamak için uyarladım.

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

Çıktı:

{'function_name': 'my_function'}

1
Bu konuda diğerlerinin aksine sevdiğim şey, sadece bir debugkez işlevi ekleyerek, sadece ihtiyacınız olan veya işlev adını yazdırmak istediğiniz herhangi bir işleve dekoratörü ekleyerek işlevselliği ekleyebilmenizdir. En kolay ve en uyarlanabilir gibi görünüyor.
SpareTimeCoder
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.