İşlev adını bu işlevin içinden (geri izleme kullanmadan) belirleme


479

Python'da, tracebackmodülü kullanmadan, bir işlevin adını o işlev içinden belirlemenin bir yolu var mı?

Diyelim ki bir işlev çubuğuna sahip bir modülüm var. Yürütürken foo.bar(), çubuğun çubuğunun adını bilmesi için bir yol var mı? Ya da daha iyisi, foo.baradı?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?

6
Kabul edilen cevabın neden Andreas Young'dan (ya da nasıl yapılacağını gösteren herhangi birinden) olmadığını anlamıyorum. Bunun yerine, kabul edilen cevap "bunu yapamazsın", ki bu yanlış görünüyor; OP'nin tek gereksinimi kullanmıyordu traceback. Cevapların ve yorumların zamanları bile bunu desteklemiyor gibi görünüyor.
sancho.s ReinstateMonicaCellio

Yanıtlar:


198

Python'un işleve veya işlevin kendi adındaki adına erişme özelliği yoktur. Bu edilmiştir önerilmiştir fakat reddedilmiştir. Yığınla kendiniz oynamak istemiyorsanız , bağlama göre "bar"veya kullanmalısınız bar.__name__.

Verilen ret bildirimi:

Bu PEP reddedildi. Nasıl uygulanacağı veya kesin anlambilimin uç durumlarda ne olması gerektiği açık değildir ve yeterli önemli kullanım durumu yoktur. cevap en iyi ihtimalle ılık.


17
inspect.currentframe()böyle bir yol.
Yuval

41
@ CamHart'ın yaklaşımını @ Yuval'ın yaklaşımıyla birleştirmek, @ RoshOxymoron'un cevabındaki "gizli" ve potansiyel olarak kullanımdan kaldırılan yöntemlerden ve @ neuro / @ AndreasJung'un cevabı için yığına sayısal indekslemeden kaçınır:print(inspect.currentframe().f_code.co_name)
Ocak

2
neden reddedildiğini özetlemek mümkün mü?
Charlie Parker

1
neden seçilen cevap bu? Soru, geçerli işleve veya modülün kendisine erişmekle ilgili değil, sadece ad. Ve yığın izleme / hata ayıklama özellikleri zaten bu bilgilere sahiptir.
nurettin

409
import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3]) #will give the caller of foos name, if something called foo

47
Bu harika çünkü [1][3]arayanın adını da alabilirsiniz.
Kos

57
Ayrıca kullanabilirsiniz: print(inspect.currentframe().f_code.co_name)ya arayanın adını almak için: print(inspect.currentframe().f_back.f_code.co_name). Tüm yığın çerçevelerinin listesini olduğu gibi inspect.stack()almadığınız için daha hızlı olması gerektiğini düşünüyorum .
Michael

7
inspect.currentframe().f_back.f_code.co_namedekore edilmiş bir yöntemle inspect.stack()[0][3]çalışmıyor ...
Dustin Wyatt

4
Lütfen dikkat: inspect.stack () ağır performans yüküne neden olabilir, bu nedenle az kullanın! Kol kutumda (nedense) tamamlanması 240 ms sürdü
gardarh

Bana Python özyineleme makinesinde mevcut olan bir şeyin bunu daha verimli yapmak için kullanılabileceği
Tom Russell

160

Aynı sonucu almanın birkaç yolu vardır:

from __future__ import print_function
import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

Not inspect.stackçağrılar yavaş alternatiflerden daha binlerce kez şunlardır:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

5
inspect.currentframe()yürütme süresi ve özel üyelerin kullanımı arasında iyi bir dengede görünüyor
FabienAndre

1
@mbdevpl Numaralarım 1.25ms, 1.24ms, 0.5us, 0.16us normal (pitonik olmayan) saniye saniye (win7x64, python3.5.1)
Antony Hatchkins

Hiç kimse @ mbdevpl deli olduğunu düşünmüyor :) - Herhangi bir anlam ifade etmediği için 3. çalışmanın çıktısı için bir düzenleme gönderdim. Hiçbir sonuç olmalıydı eğer fikri 0.100 usecya 0.199 usecama her iki şekilde - daha hızlı (Antony Hatchkins seçenek 4 den seçenek 3 üç kat daha hızlı bulduk gerçi) seçeneğiyle 4 ile karşılaştırılabilir seçenekleri 1 ve 2, daha yüzlerce kez.
dwanderson

Kullandığım sys._getframe().f_code.co_nameüzerinde inspect.currentframe().f_code.co_nameZaten ithal var çünkü sysmodülü. Bu makul bir karar mı? (hızların oldukça benzer göründüğü göz önüne alındığında)
PatrickT

47

@Andreas Jung'un gösterdiği yaklaşımı kullanarak tanımlandığı adı alabilirsiniz , ancak bu işlevin çağrıldığı ad olmayabilir:

import inspect

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

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

Bu ayrımın sizin için önemli olup olmadığı söyleyemem.


3
İle aynı durum .func_name. Python'daki sınıf ve işlev adlarının bir şey olduğunu ve bunlara atıfta bulunan değişkenlerin başka bir şey olduğunu hatırlamakta fayda var.
Kos

Bazen Foo2()yazdırmak isteyebilirsiniz Foo. Örneğin: Foo2 = function_dict['Foo']; Foo2(). Bu durumda, Foo2 belki de bir komut satırı ayrıştırıcısı için bir fonksiyon göstergesidir.
Harvey

Bunun ne tür bir hız etkisi var?
Robert C. Barth

2
Ne ile ilgili hız ima? Bu bilgileri gerçek zamanlı olarak zor bir durumda veya başka bir yerde bulundurmanız gereken bir durum var mı?
bgporter

44
functionNameAsString = sys._getframe().f_code.co_name

Çok benzer bir şey istedim çünkü kodumda birkaç yere giden bir günlük dizesine işlev adını koymak istedim. Muhtemelen bunu yapmanın en iyi yolu değil, ama şu anki fonksiyonun adını almanın bir yolu.


2
Tamamen çalışan, sadece sys kullanarak, daha fazla modül yüklemenize gerek yok, ancak hatırlanması çok kolay değil: V
m3nda 20

@ erm3nda Cevabımı görün.
nerdfever.com

1
@ nerdfever.com Benim sorunum bir işlev yaratmak üzere değil, bu işlevin içine ne koyacağımı hatırlamamak. Hatırlamak kolay değil bu yüzden her zaman tekrar oluşturmak için bazı not görmek gerekir. fÇerçeve ve cokod için akılda tutmaya çalışacağım . Ben bu kadar çok kullanmıyorum daha iyi eğer ben sadece bazı snippet içinde kaydetmek :-)
m3nda

24

Bu kullanışlı yardımcı programı yakın tutuyorum:

import inspect
myself = lambda: inspect.stack()[1][3]

Kullanımı:

myself()

1
Burada önerilen alternatif ile bu nasıl yapılır? "kendim = lambda: sys._getframe (). f_code.co_name" çalışmıyor (çıktı "<lambda>"; Sanırım sonuç daha sonra çağrı zamanında değil tanım zamanında belirlendi. Hmm.
NYCeyes

2
@ prismalytics.io: Kendimi (kendimi ()) arar ve sadece değerini (kendimi) kullanmazsanız, aradığınızı alırsınız.
ijw

NYCeyes haklıydı, adı lambda içinde çözüldü ve sonuç <lambda> oldu. Sys._getframe () ve inspect.currentframe () yöntemleri doğrudan adını almak istediğiniz işlevin içinde yürütülmelidir ZORUNLU. İnspect.stack () yöntemi çalışır, çünkü 1 dizinini belirtebilirsiniz, inspect.stack () [0] [3] yapmak da <lambda> değerini verir.
19:16

20

Bunu inspectyapmanın en iyi yolu sanırım . Örneğin:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

17

İşlev adını yazacak bir sarıcı buldum

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

Bu yazdırılacak

my_funky_name

TASLAK


1
Bir dekoratör noob olarak, my_funky_name bağlamında func .__ name__'ye erişmenin bir yolu olup olmadığını merak ediyorum (böylece değerini alabilir ve my_funky_name içinde kullanabilirim)
cowbert

Bunu my_funky_name işlevi içinde yapmanın yolu my_funky_name.__name__. Func .__ name__ işlevine yeni bir parametre olarak geçebilirsiniz. func (* argümanlar, ** kwargs, my_name = func .__ name__). Dekoratörünüzün adını fonksiyonunuzun içinden almak için, bunun inspect kullanılmasını gerektireceğini düşünüyorum. Ama çalışan fonksiyonumda fonksiyonumu kontrol eden fonksiyonun adını almak ... güzel bir
memenin

15

Bu aslında sorunun diğer cevaplarından türetilmiştir.

İşte benim almam:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

Bu sürümün inspect.stack () kullanımı üzerindeki olası avantajı, binlerce kat daha hızlı olması gerektiğidir [bkz. Alex Melihoff'un sys._getframe () kullanımına karşı gönderi ve zamanlamalarına karşılık inspect.stack () kullanılması].


12

print(inspect.stack()[0].function) çok işe yarıyor gibi görünüyor (Python 3.5).


11

İşte geleceğe dönük bir yaklaşım.

@ CamHart's ve @ Yuval'ın önerilerini @ RoshOxymoron'un kabul edilen cevabı ile birleştirmekten kaçınma avantajı vardır:

  • _hidden ve potansiyel olarak kullanımdan kaldırılmış yöntemler
  • Yığına endeksleme (gelecekteki pitonlarda yeniden sıralanabilir)

Bu yüzden gelecekteki python sürümleriyle (2.7.3 ve 3.3.2'de test edilmiş) güzel oynadığını düşünüyorum:

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

11
import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

Ölçek:

a = A()
a.test_class_func_name()
test_func_name()

Çıktı:

test_class_func_name
test_func_name

11

İnsanların neden karmaşık hale getirdiğinden emin değilim:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))

belki bunun dışında sys modülünün özel işlevini kullandığınız için. Genel olarak kötü bir uygulama olarak kabul edilir, değil mi?
tikej

@tikej, kullanım burada belirtilen en iyi uygulamayı kabul eder: docs.quantifiedcode.com/python-anti-patterns/correctness/…
karthik r

10
import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

IDE'de kod çıktıları

merhaba ben foo, baba

merhaba, ben barım, baba foo

merhaba, ben barım, baba


8

Bir dekoratör kullanabilirsiniz:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

Bu posterin sorusunu nasıl cevaplıyor? Bunu, işlev adının işlev içinden nasıl bilindiğini gösteren bir örnek içerecek şekilde genişletebilir misiniz?
parvus

@parvus: cevabım olduğu gibi OP sorusuna bir cevap gösteren bir örnek
Douglas Denhartog

Tamam, işlevim rastgele kullanıcının OP işlevidir. Bunu dekoratörler hakkındaki anlayışımdan yoksun bırak. Nerede @? Bu, argümanlarını uyarlamak istemediğiniz işlevler için nasıl çalışır? Çözümünüzü nasıl anlarım: işlev adını bilmek istediğimde, @get_function_name ile eklemem ve zaten başka bir amaç için orada olmadığını umarak ad bağımsız değişkenini eklemem gerekiyor. Muhtemelen bir şey özlüyorum, bunun için üzgünüm.
parvus

Bir yorum içinde kendi python kursu başlatmadan: 1. fonksiyonları nesnelerdir; 2. işleve bir ad özniteliği ekleyebilir, adı yazdırabilir / kaydedebilir veya dekoratörün içindeki "ad" ile istediğiniz sayıda şey yapabilirsiniz; 3. dekoratörler birden fazla şekilde eklenebilir (örneğin @ veya benim örneğimde); 4. dekoratörler @ sarar kullanabilir ve / veya sınıfların kendileri olabilir; 5. Devam edebilirdim, ama mutlu programlama!
Douglas Denhartog

Bu sadece __name__bir işlevin özelliğine ulaşmak için kıvrık bir yol gibi görünüyor . Kullanım, almaya çalıştığınız şeyi bilmenizi gerektirir, bu da işlevlerin anında tanımlanmadığı basit durumlarda benim için çok yararlı görünmüyor.
Avi

3

Birden fazla miras senaryosu içinde güvenlik ile süper çağırmak için kullanılan kendi yaklaşımımı yapıyorum (tüm kodu koydum)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

örnek kullanımı:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

test etmek:

a = C()
a.test()

çıktı:

called from C
called from A
called from B

Her @with_name dekore edilmiş yöntemin içinde, geçerli işlev adı olarak self .__ fname__ dosyasına erişebilirsiniz.


3

Geçenlerde bu fonksiyonun bağlamından bir fonksiyonun öğretisine erişmek için yukarıdaki cevapları kullanmaya çalıştım, ancak yukarıdaki sorular sadece isim dizesini döndürdüğü için işe yaramadı.

Neyse ki basit bir çözüm buldum. Eğer benim gibi, işlev adının dizesine eval () uygulayabileceğiniz adı temsil eden dizeyi almak yerine işleve başvurmak istersiniz.

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)

3

Yığın öğelerine güvenmemeyi öneririm. Birisi kodunuzu farklı bağlamlarda kullanırsa (örneğin python yorumlayıcısı) yığınız dizini değiştirecek ve bozacaktır ([0] [3]).

Size böyle bir şey öneriyorum:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')

0

Bir dekoratörle bunu başarmak oldukça kolaydır.

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')
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.