วิธีรับชื่อฟังก์ชั่นเป็นสตริง?


740

ใน Python ฉันจะได้ชื่อฟังก์ชันเป็นสตริงโดยไม่ต้องเรียกใช้ฟังก์ชันได้อย่างไร

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

"my_function"เอาท์พุทควร

มีฟังก์ชั่นดังกล่าวใน Python หรือไม่? ถ้าไม่แนวคิดเกี่ยวกับวิธีการใช้งานget_function_name_as_stringใน Python?


คำตอบ:


944
my_function.__name__

การใช้__name__เป็นวิธีที่ต้องการเนื่องจากมีการใช้อย่างสม่ำเสมอ ต่างจากfunc_nameมันทำงานกับฟังก์ชั่นในตัวเช่นกัน:

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

นอกจากนี้ขีดเส้นใต้คู่บ่งบอกถึงผู้อ่านนี้เป็นคุณลักษณะพิเศษ ในฐานะโบนัสคลาสและโมดูลต่างก็มีแอ__name__ททริบิวต์ด้วยดังนั้นคุณจะจำชื่อพิเศษได้เพียงชื่อเดียวเท่านั้น


420
เพราะในบางกรณีคุณจะได้รับฟังก์ชั่นวัตถุบางอย่างเป็นอาร์กิวเมนต์ของฟังก์ชั่นของคุณและคุณจะต้องแสดง / store / จัดการชื่อของฟังก์ชั่นนั้น บางทีคุณกำลังสร้างเอกสารข้อความช่วยเหลือประวัติความเป็นมาของการกระทำ ฯลฯ ดังนั้นไม่คุณไม่ได้ hardcoding ชื่อฟังก์ชันเสมอไป
mbargiel

2
@ mgargiel: สิ่งที่ฉันหมายถึงคือถ้าคุณมีคลาสซึ่งกำหนดวิธีการ 100 วิธีโดยวิธีทั้งหมดเป็นเพียง wrappers ซึ่งเรียกวิธีการส่วนตัวผ่านชื่อเสื้อคลุมตัวเอง บางสิ่งเช่นนี้: def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs). คุณมีทางเลือกอื่นซึ่งก็คือ: def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs). ดังนั้นคำตอบจาก Albert Vonpupp ดูดีกว่าสำหรับฉัน
Richard Gomes

19
@RichardGomes หนึ่งคำตอบมีความเหมาะสมในการรับชื่อภายในฟังก์ชั่นของตัวเองอื่น ๆ ที่เหมาะสมสำหรับการรับมันจากการอ้างอิงฟังก์ชั่น คำถามของ OP ตามที่เขียนไว้แสดงถึงคำถามหลัง
รัสเซล Borogove

22
@RichardGomes จริงๆแล้วฉันมาที่คำถามนี้เพื่อหาทางแก้ไขปัญหานี้ ปัญหาที่ฉันพยายามแก้ไขคือสร้างมัณฑนากรที่สามารถใช้บันทึกการโทรทั้งหมดของฉัน
ali-hussain

23
ฟังก์ชั่น @RichardGomes เป็นวัตถุชั้นหนึ่งอาจมีมากกว่าชื่อที่ผูกไว้กับพวกเขา มันไม่จำเป็นต้องเป็นf.__name__ == 'f'อย่างนั้น
Wim

298

ในการรับฟังก์ชั่นปัจจุบันหรือชื่อของวิธีการจากภายในให้พิจารณา:

import inspect

this_function_name = inspect.currentframe().f_code.co_name

sys._getframeยังสามารถใช้งานได้inspect.currentframeแม้ว่าจะหลีกเลี่ยงการเข้าถึงฟังก์ชั่นส่วนตัว

จะได้รับชื่อฟังก์ชั่นของการเรียกแทนพิจารณาในขณะที่f_backinspect.currentframe().f_back.f_code.co_name


หากใช้งานmypyด้วยก็สามารถบ่นได้ว่า:

ข้อผิดพลาด: รายการ "ไม่มี" ของ "ตัวเลือก [FrameType]" ไม่มีแอตทริบิวต์ "f_code"

หากต้องการระงับข้อผิดพลาดด้านบนให้พิจารณา:

import inspect
import types
from typing import cast

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

45
+1: นี่คือคำตอบที่ฉันต้องการดู คำตอบอื่น ๆ สมมติว่าผู้โทรทราบชื่อฟังก์ชั่นแล้วซึ่งไร้สาระในบริบทของคำถามนี้
Richard Gomes

110
Richard: ไม่หรอก คุณกำลังสมมติว่าคุณกำลังโทรหาชื่อหรือ func_name ในฟังก์ชั่นของคุณโดยตรงในขอบเขตเดียวกับที่กำหนดไว้ซึ่งมักจะไม่ใช่กรณี โปรดทราบว่าฟังก์ชั่นเป็นวัตถุ - พวกเขาสามารถส่งผ่านไปเป็นข้อโต้แย้งกับฟังก์ชั่นอื่น ๆ ที่เก็บไว้ในรายการ / dicts สำหรับการค้นหาหรือการดำเนินการในภายหลัง ฯลฯ
mbargiel

11
@paulus_almighty การขุดลงในเฟรมสแต็คไม่ทำให้ฉันเป็นนามธรรม! ในความเป็นจริงมันเป็นสิ่งที่ตรงกันข้ามกับนามธรรม ดูบันทึกรายละเอียดการใช้งานในเอกสาร การใช้งานของงูหลามจะไม่รวมsys._getframe- มันเชื่อมต่อโดยตรงกับภายในของ CPython
senderle

6
ใช้งานได้เฉพาะภายในฟังก์ชัน แต่คำถามระบุว่าไม่ควรเรียกใช้ฟังก์ชัน
user2357112 รองรับ Monica

5
กรณีที่คุณต้องการห่อฟังก์ชั่นของคุณกับสิ่งที่ใช้งานได้ง่ายกว่าและจำได้ง่ายคุณต้องดึงข้อมูลเฟรมที่ 1 เช่นsys._getframe(1).f_code.co_nameดังนั้นคุณสามารถกำหนดฟังก์ชั่นที่ต้องการget_func_name()และคาดว่าจะได้ชื่อที่ต้องการของฟังก์ชันที่เรียกใช้
m3nda

44
my_function.func_name

นอกจากนี้ยังมีคุณสมบัติความสนุกอื่น ๆ ของฟังก์ชั่น พิมพ์dir(func_name)เพื่อแสดงรายการ func_name.func_code.co_codeเป็นฟังก์ชั่นที่รวบรวมไว้ซึ่งเก็บไว้เป็นสตริง

import dis
dis.dis(my_function)

จะแสดงรหัสในรูปแบบที่มนุษย์เกือบอ่านได้ :)


4
อะไรคือความแตกต่างระหว่าง f .__ name__ และ f.func_name?
Federico A. Ramponi

14
แซม: ชื่อเป็นส่วนตัว __ ชื่อนั้นพิเศษมีความแตกต่างทางแนวคิด
Matthew Trevor

11
ในกรณีที่ใครบางคนงงงวยโดยคำตอบก่อนหน้าโดยแมทธิวระบบความคิดเห็นได้ตีความขีดเส้นใต้คู่บางอย่างเป็นรหัสสำหรับตัวหนา หนีด้วย backtick ข้อความที่ควรอ่าน: __names__เป็นส่วนตัว__namesเป็นพิเศษ
gwideman

12
ที่จริงฉันคิดว่าถูกต้องเป็น_namesส่วนตัว (ขีดล่างเดียวก่อนเพียงแค่การประชุม) __names__เป็นพิเศษ (ขีดล่างคู่ก่อนและหลัง) ไม่แน่ใจว่าขีดเส้นใต้สองครั้งก่อนมีความหมายใด ๆ เป็นทางการหรือเป็นแบบแผน
MestreLion

8
func_nameไม่มีอยู่อีกแล้วใน python3 ดังนั้นคุณต้องใช้func.__name__ถ้าคุณต้องการความเข้ากันได้
Daenyth

34

ฟังก์ชั่นนี้จะส่งคืนชื่อฟังก์ชั่นของผู้โทร

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

มันเหมือนคำตอบของ Albert Vonpupp ที่มีเสื้อคลุมที่เป็นมิตร


1
ฉันมี "<module>" ที่ดัชนี [2] แต่การทำงานต่อไปนี้: traceback.extract_stack (ไม่มี, 2) [0] [- 1]
emmagras

3
สำหรับฉันมันใช้ไม่ได้ แต่จะทำเช่นนี้: traceback.extract_stack () [- 1] [2]
mike01010

งานนี้หากมีการเปลี่ยนแปลงดัชนีแรกที่ 1 คุณคนควรเรียนรู้ที่จะแก้ปัญหาก่อนที่จะแสดงความคิดเห็น ... traceback.extract_stack (None 2) [1] [2]
Jcc.Sanabria

29

หากคุณกำลังสนใจในวิธีการเรียนมากเกินไป, Python 3.3 หรือสูงกว่ามีนอกเหนือไปจาก__qualname____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

ฉันชอบใช้มัณฑนากรฟังก์ชั่น ฉันเพิ่มคลาสซึ่งยังเพิ่มเวลาทำงาน สมมติว่า gLog เป็นตัวบันทึก python มาตรฐาน:

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

ดังนั้นตอนนี้สิ่งที่คุณต้องทำกับฟังก์ชั่นของคุณก็คือการตกแต่งและ voila

@func_timer_decorator
def my_func():

15

sys._getframe()ไม่รับประกันว่าจะสามารถใช้งานได้ในทุกการใช้งานของ Python ( ดูที่การอ้างอิง ) คุณสามารถใช้tracebackโมดูลเพื่อทำสิ่งเดียวกันเช่น

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

   return funcName

การเรียกไปยังstack[-1]จะส่งกลับรายละเอียดกระบวนการปัจจุบัน


ขออภัยหากsys._getframe()ไม่ได้กำหนดtraceback.extract_stackจะใช้ไม่ได้ หลังมีการแสดงการทำงานที่คร่าวๆของ superset อย่างคร่าว ๆ ; คุณไม่สามารถคาดหวังที่จะเห็นอันใดอันหนึ่ง และในความเป็นจริงใน IronPython 2.7 เสมอผลตอบแทนextract_stack() []-1
SingleNegationElimination

15
import inspect

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

ที่ไหน

  • stack () [0] ผู้โทร

  • stack () [3] ชื่อสตริงของวิธีการ


1
เรียบง่าย. stack()[0]จะเป็นผู้เรียก[3]ชื่อสตริงของเมธอดเสมอ
Kontur

14

ในฐานะที่เป็นส่วนขยายของคำตอบของ@ Demynฉันได้สร้างฟังก์ชั่นยูทิลิตี้บางอย่างที่พิมพ์ชื่อฟังก์ชั่นปัจจุบันและข้อโต้แย้งของฟังก์ชั่นปัจจุบัน:

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

คุณแค่อยากได้ชื่อฟังก์ชั่นที่นี่เป็นรหัสง่ายๆ สมมติว่าคุณได้กำหนดฟังก์ชั่นเหล่านี้แล้ว

def function1():
    print "function1"

def function2():
    print "function2"

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

ผลลัพธ์จะเป็นfunction1

ตอนนี้สมมติว่าคุณมีฟังก์ชั่นเหล่านี้ในรายการ

a = [function1 , function2 , funciton3]

เพื่อรับชื่อฟังก์ชั่น

for i in a:
    print i.__name__

ผลลัพธ์จะเป็น

function1
function2
function3


5

ฉันเห็นคำตอบเล็ก ๆ น้อย ๆ ที่ใช้นักตกแต่งแม้ว่าฉันรู้สึกว่ามีเพียงไม่กี่คำ นี่คือสิ่งที่ฉันใช้สำหรับบันทึกชื่อฟังก์ชันรวมถึงค่าอินพุตและเอาต์พุตตามลำดับ ฉันปรับมันที่นี่เพื่อพิมพ์ข้อมูลแทนที่จะสร้างล็อกไฟล์และปรับให้เหมาะกับตัวอย่างเฉพาะของ 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()

เอาท์พุท:

{'function_name': 'my_function'}

1
สิ่งที่ฉันชอบเกี่ยวกับเรื่องนี้ตรงข้ามกับคนอื่น ๆ ทั้งหมดก็คือเพียงแค่เพิ่มdebugฟังก์ชั่นเมื่อคุณสามารถเพิ่มฟังก์ชั่นได้โดยเพียงเพิ่มมัณฑนากรในฟังก์ชันใดก็ได้ที่คุณต้องการหรือต้องการพิมพ์ชื่อฟังก์ชั่น ดูเหมือนว่าจะง่ายที่สุดและปรับได้มากที่สุด
SpareTimeCoder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.