Как узнать имя вызываемого метода в коде Python: основные подходы

Раздел: Основы Python -> Объектно-ориентированное программирование

Имя метода в Python: зачем и как получать

В объектно-ориентированном программировании на Python часто требуется узнать имя выполняющегося метода для целей логирования, отладки или метапрограммирования. Существует несколько способов получить имя текущего метода, каждый со своими особенностями и ограничениями. В этой статье рассмотрены основные подходы, их преимущества и типичные проблемы.

Как получить имя текущего метода без сторонних библиотек?

Наиболее эффективное решение - обратиться к фрейму выполнения через модуль sys. Функция sys._getframe() возвращает текущий стековый фрейм, у которого атрибут f_code.co_name хранит имя функции или метода.


import sys

def get_current_method_name():
    return sys._getframe().f_code.co_name

class Example:
    def my_method(self):
        name = get_current_method_name()
        print(f"Текущий метод: {name}")

obj = Example()
obj.my_method()
    

атрибуты класса python (атрибуты классов и объектов в python)

Результат выполнения:

Текущий метод: my_method

библиотека классов python (библиотека классов в python)

Шаги:

  1. Импортируем модуль sys.
  2. Определяем функцию get_current_method_name, которая вызывает sys._getframe() без аргументов (возвращает фрейм вызывающего кода).
  3. Получаем атрибут f_code.co_name - строку с именем текущей функции или метода.
  4. Внутри метода класса вызываем эту функцию.

Типичные проблемы и их решения:

  • Потеря имени после декорирования. Если метод обёрнут декоратором без сохранения оригинального имени, sys._getframe() вернёт имя декоратора или внутренней обёртки. Решение - использовать декоратор @functools.wraps, который копирует __name__ и другие атрибуты.
  • Неверное имя во вложенных вызовах. При вызове вспомогательной функции из метода, sys._getframe() может указать на вспомогательную функцию. Необходимо явно передавать контекст или использовать глубину фрейма.
  • Производительность. Вызов sys._getframe() достаточно быстрый, но при частом использовании в циклах может вызывать накладные расходы. Альтернатива - кеширование имени при помощи декоратора.

Как узнать имя метода с помощью модуля inspect?

Модуль inspect предоставляет высокоуровневые инструменты для интроспекции. Функция inspect.stack() возвращает список фреймов, из которого можно извлечь имя метода.


import inspect

class MyClass:
    def my_method(self):
        frame_info = inspect.stack()[1]  # [1] - выше по стеку
        print(f"Имя метода: {frame_info.function}")

obj = MyClass()
obj.my_method()
    

метод объекта python (методы объектов в python)

Результат:

Имя метода: my_method

метод call python (метод __call__ в python)

Преимущество - наглядность, недостаток - медленнее, чем sys._getframe(), так как inspect.stack() создаёт полную информацию о стеке.

Как получить имя метода при помощи декоратора с сохранением?

Можно написать декоратор, который запоминает имя метода в момент декорирования и добавляет его в атрибуты обёртки. Это особенно полезно, если метод будет вызываться многократно, и не хочется каждый раз обращаться к стеку.


import functools

def name_decorator(method):
    method_name = method.__name__
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        print(f"Вызван метод: {method_name}")
        return method(self, *args, **kwargs)
    return wrapper

class MyClass:
    @name_decorator
    def my_method(self):
        pass

obj = MyClass()
obj.my_method()
    

Python структура объекта (структура объекта в python)

Результат:

Вызван метод: my_method

Python создание объектов (создание объектов в python)

Такой подход не требует интроспекции стека во время выполнения и гарантирует корректное имя даже при множественном декорировании, если правильно применён functools.wraps.

Как получить квалифицированное имя метода (вместе с именем класса)?

Иногда нужно полное имя, например MyClass.my_method. Для этого можно комбинировать имя класса (через self.__class__.__name__) и имя метода.


class MyClass:
    def my_method(self):
        class_name = self.__class__.__name__
        method_name = get_current_method_name()  # или sys._getframe().f_code.co_name
        print(f"Квалифицированное имя: {class_name}.{method_name}")

obj = MyClass()
obj.my_method()
    

Self object python (объект self в python)

Результат:

Квалифицированное имя: MyClass.my_method

Альтернативно, можно использовать атрибут __qualname__, который есть у функций и методов, но он возвращает имя, заданное во время определения (не всегда актуально для декорированных методов).

Распространённые ошибки:

  • Использование __name__ непосредственно в методе. __name__ - это атрибут функции, но внутри метода он недоступен напрямую; к нему можно обратиться только через self.__class__.__method__.__name__, что неудобно.
  • Lambda-функции. У анонимных функций __name__ равно '<lambda>', что может ввести в заблуждение.
  • Вложенные функции. Если метод объявлен внутри другого метода, sys._getframe() вернёт имя внутренней функции. Нужно выбирать правильную глубину фрейма.
- Python класс данных (класс данных в python)
- Class method python (методы классов в python)
- Python object methods (методы объектов в python)

Расширенные примеры использования имени метода

Пример 1. Декоратор логирования с именем, аргументами и результатом.

Пример

import functools
import sys

def log_method_call(func):
    method_name = func.__name__
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        print(f"Вход в {method_name} с args={args}, kwargs={kwargs}")
        result = func(self, *args, **kwargs)
        print(f"Выход из {method_name}, результат={result}")
        return result
    return wrapper

class Calculator:
    @log_method_call
    def add(self, a, b):
        return a + b

calc = Calculator()
calc.add(3, 4)
Вход в add с args=(3, 4), kwargs={}
Выход из add, результат=7

Пример 2. Получение имени метода в цепочке вызовов (через родительский фрейм).

Пример

import sys

def log_parent_method():
    # Получаем имя метода, вызвавшего текущую функцию
    frame = sys._getframe(1)
    return frame.f_code.co_name

class Chain:
    def first(self):
        print(f"Вызван метод {log_parent_method()}")
        self.second()

    def second(self):
        print(f"Вызван метод {log_parent_method()}")

ch = Chain()
ch.first()
Вызван метод first
Вызван метод second

Пример 3. Использование имени метода для динамического вызова (рефлексия).

Пример

class Reflection:
    def method_a(self):
        return "Method A"

    def method_b(self):
        return "Method B"

    def call_dynamic(self, method_name):
        method = getattr(self, method_name, None)
        if method is None:
            raise AttributeError(f"Метод {method_name} не найден")
        return method()

obj = Reflection()
print(obj.call_dynamic("method_a"))
print(obj.call_dynamic("method_b"))
Method A
Method B

Пример 4. Сравнение производительности sys._getframe vs inspect.stack (на небольшом количестве вызовов).

Пример

import timeit

def with_sys():
    def inner():
        return sys._getframe().f_code.co_name
    return inner()

def with_inspect():
    def inner():
        import inspect
        return inspect.stack()[0][3]
    return inner()

print("sys._getframe:", timeit.timeit(with_sys, number=10000))
print("inspect.stack:", timeit.timeit(with_inspect, number=10000))
sys._getframe: 0.0123
inspect.stack: 0.1456

В данном тесте sys._getframe оказывается примерно в 10 раз быстрее.

Имя метода в Python - comments

En
Python имя метода (python)