Рассмотрим две функции ниже:
def f1():
return "potato"
f2 = lambda: "potato"
f2.__name__ = f2.__qualname__ = "f2"
Если не считать самоанализ исходного кода, есть ли способ определить, что f1 было определением, а f2 — лямбдой?
>>> black_magic(f1)
"def"
>>> black_magic(f2)
"lambda"
Обратите внимание, что связанный ответ и комментарий @PeterDeGlopper зависят от того, что вы не сделали того, что сделала последняя строка вашего кода, то есть задавать атрибут __name__ вашей лямбды
Зачем вам это нужно?






Вы можете проверить имя объект кода. В отличие от имени функции, имя объекта кода нельзя переназначить. Имя объекта кода лямбды по-прежнему будет '<lambda>':
>>> x = lambda: 5
>>> x.__name__ = 'foo'
>>> x.__name__
'foo'
>>> x.__code__.co_name
'<lambda>'
>>> x.__code__.co_name = 'foo'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
Оператор def не может определить функцию, кодовый объект которой имеет имя '<lambda>'. является можно заменить объект кода функции после создания, но это происходит редко и достаточно странно, поэтому с этим, вероятно, не стоит обращаться. Точно так же это не будет обрабатывать функции или объекты кода, созданные путем ручного вызова types.FunctionType или types.CodeType. Я не вижу хорошего способа справиться с __code__ переназначением или созданными вручную функциями и объектами кода.
Однако вы можете полностью заменить объект кода.
Возможно, забота о том, был ли function создан с помощью лямбда-выражения или оператора def, является также редкой и странной.
@chepner, возможно, это нормально, если вы полностью замените объект кода, потому что если вы это сделаете, вы просто замените само тело функции, поэтому оно будет отражать, является ли функция, которой он был заменен, функцией или лямбдой.
@alkasm Нет, нет. Это тот же function объект, но с новым телом. Вы не заменили глобальное пространство имен, замыкания, значения параметров по умолчанию и т. д.
Прохладный. Работает и в Python 2.7.
@chepner возможное использование - проверка неанонимных функций lambda (как правило, плохая практика) и изменение их на def? использование flake8, несомненно, было бы проще
@Chris_Rands Просто выполните текстовый поиск lambda в этом случае. Нет смысла пытаться найти функции, которые изначально были определены с помощью lambda, если вы не видите, где он был изначально определен.
Здесь
def f1():
return "potato"
f2 = lambda: "potato"
def is_lambda(f):
return '<lambda>' in str(f.__code__)
print(is_lambda(f1))
print(is_lambda(f2))
выход
False
True
Фактически это ответ @ user2357112.
Вы можете использовать ast.NodeVisitor для достижения своей цели без жесткого кодирования каких-либо вызовов функций, работая на уровне источников, с его помощью вы можете идентифицировать определения функций ВСЕлямбда, ФункцияDef, АсинкФунктионДеф и распечатать их местоположение, имя и т. д. См. пример кода ниже. :
import ast
class FunctionsVisitor(ast.NodeVisitor):
def visit_Lambda(self, node):
print(type(node).__name__, ', line no:', node.lineno)
def visit_FunctionDef(self, node):
print(type(node).__name__, ':', node.name)
def visit_AsyncFunctionDef(self, node):
print(type(node).__name__, ':', node.name)
def visit_Assign(self, node):
if type(node.value) is ast.Lambda:
print("Lambda assignment to: {}.".format([target.id for target in node.targets]))
self.generic_visit(node)
def visit_ClassDef(self, node):
# Remove that method to analyse functions visitor and functions in other classes.
pass
def f1():
return "potato"
f2 = f3 = lambda: "potato"
f5 = lambda: "potato"
async def f6():
return "potato"
# Actually you can define ast logic in separate file and process sources file in it.
with open(__file__) as sources:
tree = ast.parse(sources.read())
FunctionsVisitor().visit(tree)
Вывод для приведенного ниже кода следующий:
FunctionDef : f1
Lambda assignment to: ['f2', 'f3'].
Lambda , line no: 27
Lambda assignment to: ['f5'].
Lambda , line no: 28
AsyncFunctionDef : f6
Это интроспекция исходного кода - выходит за рамки вопроса.
@wim Не могу не согласиться, но на самом деле SO по теме ast не так много, поэтому я пытаюсь дать широкий ответ, чтобы помочь пользователям, которым понадобится этот самоанализ и которые столкнутся с этим вопросом в будущем, и отвечая на фактический вопрос в то же время.
Однако код в вопросе переназначает
__name__.