Имя функции в Python: атрибут __name__ и его применение
Основное: атрибут __name__ у функций
Каждая функция в Python является объектом и имеет встроенный атрибут __name__, который хранит строковое имя функции, заданное при определении. Этот атрибут автоматически создаётся интерпретатором и позволяет получить имя функции во время выполнения. Например, для функции, объявленной как def hello(): pass, значение hello.__name__ будет равно 'hello'.
def hello():
pass
print(hello.__name__)
аргументы print python (аргументы функции print в python)
hello
Python 3 аргументы (аргументы в python 3)
Атрибут __name__ особенно полезен в декораторах, логировании и отладке - он помогает идентифицировать, какая именно функция выполняется, не заглядывая в её тело.
Как изменить имя функции после её создания?
Имя функции, хранящееся в __name__, можно изменить напрямую, присвоив новое строковое значение. Это бывает нужно, когда функция динамически создаётся или оборачивается в декоратор без сохранения оригинального имени.
def original():
pass
print(original.__name__)
original.__name__ = 'renamed'
print(original.__name__)
аргумент параметр python (аргументы и параметры в python)
original renamed
аргумент класса python (аргументы класса python)
Проблема: Изменение __name__ может затруднить отладку, так как трассировка стека выводит текущее имя. Типичная ошибка - попытка переименовать встроенную функцию, что вызовет исключение AttributeError (атрибут только для чтения у встроенных функций C).
Как использовать __name__ в декораторах для сохранения имени?
При создании декоратора без обёртки через functools.wraps имя декорируемой функции теряется - оно заменяется именем внутренней функции. Чтобы этого избежать, применяют декоратор @wraps из модуля functools, который копирует атрибут __name__ (и другие) из исходной функции.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Вызвана функция {func.__name__}")
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
pass
print(example.__name__)
Python аргументы строки (аргументы строки в python (командная строка))
Вызвана функция example example
аргумент метода python (аргументы метода python)
Типичная ошибка: Если забыть @wraps, то example.__name__ будет 'wrapper', что ломает логирование и интроспекцию.
Как получить имя вызываемой функции изнутри?
Изнутри функции её имя доступно через __name__ самой функции (например, используя глобальное имя функции, но лучше передать явно). Однако внутри тела функции нельзя обратиться к __name__ напрямую как к локальной переменной - нужно использовать sys._getframe().f_code.co_name или просто ссылку на функцию.
import sys
def test():
# Вариант 1: прямое обращение к __name__ (не сработает, так как test ещё не определена)
# print(__name__) # NameError
# Вариант 2: через sys
print(sys._getframe().f_code.co_name)
test()
Python args (аргументы в python)
test
именованные аргументы функции python (именованные аргументы функции python)
Проблема: Использование sys._getframe() не рекомендуется в продакшене из-за затрат производительности и зависимости от реализации. Лучше передавать имя как аргумент или использовать декоратор.
Как проверить, является ли объект функцией и получить её имя?
Для проверки типа объекта на функцию используется callable() или inspect.isfunction(). После этого можно безопасно получить __name__, если объект является функцией.
import inspect
def my_func():
pass
lambda_func = lambda: None
print(inspect.isfunction(my_func))
print(inspect.isfunction(lambda_func))
print(my_func.__name__)
print(lambda_func.__name__)
именованные аргументы python (именованные аргументы python)
True True my_func <lambda>
Типичная ошибка: У лямбда-функций __name__ всегда '<lambda>', что может быть неинформативно. Для лямбд рекомендуется задавать имя через присваивание переменной, но __name__ не изменится.
Расширенные примеры работы с __name__
# Пример 1: создание функций с динамическим именем через type()
def create_function(name, body_code):
# body_code - строка с телом функции
exec(body_code)
# создаём объект функции с заданным именем
func = type(name, (object,), {}).__init__
return func
# Примечание: на практике для динамического создания функций лучше использовать exec или compile
def make_adder(n):
def adder(x):
return x + n
adder.__name__ = f'adder_{n}'
return adder
add5 = make_adder(5)
print(add5.__name__)
print(add5(10))
adder_5 15
# Пример 2: использование __name__ для логирования вызовов
def logged(func):
def wrapper(*args, **kwargs):
print(f"[LOG] Вызов {func.__name__} с args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"[LOG] {func.__name__} вернула {result}")
return result
return wrapper
@logged
def multiply(a, b):
return a * b
multiply(3, 4)
[LOG] Вызов multiply с args=(3, 4) kwargs={}
[LOG] multiply вернула 12
# Пример 3: кэширование результатов с учётом имени функции (декоратор lru_cache имитация)
def cache_result(func):
cache = {}
def wrapper(*args):
key = (func.__name__,) + args
if key not in cache:
cache[key] = func(*args)
return cache[key]
wrapper.__name__ = func.__name__
return wrapper
@cache_result
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
print(fib(10))
55
# Пример 4: интроспекция цепочки декораторов
from functools import wraps
def decorator1(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def decorator2(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def final_func():
return "done"
print(final_func.__name__)
print(final_func.__wrapped__.__name__)
final_func delegator? (зависит от реализации wraps)
# Пример 5: получение имени из стека вызовов (не рекомендуется для продакшена)
import traceback
def whoami():
stack = traceback.extract_stack()
# последний элемент стека - текущий вызов
print(stack[-2].name) # имя функции, вызвавшей whoami
def test_call():
whoami()
test_call()
test_call