Есть ли способ узнать, был ли функциональный объект лямбда-выражением или определением? - PullRequest
5 голосов
/ 04 июня 2019

Рассмотрим две функции ниже:

def f1():
    return "potato"

f2 = lambda: "potato"
f2.__name__ = f2.__qualname__ = "f2"

Если не считать исходного исходного кода, есть ли способ обнаружить, что f1 было определением, а f2 было лямбда-выражением?

>>> black_magic(f1)
"def"
>>> black_magic(f2)
"lambda"

Ответы [ 3 ]

15 голосов
/ 04 июня 2019

Вы можете проверить имя объекта кода . В отличие от имени функции, имя объекта кода не может быть переназначено. Имя объекта лямбда-кода по-прежнему будет '<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__ переназначения или созданных вручную функций и объектов кода.

0 голосов
/ 05 июня 2019

Вы можете использовать ast.NodeVisitor для достижения своей цели без жесткого кодирования любых вызовов функций, работая на слое источников, с его помощью вы можете идентифицировать ALL Lambda , FunctionDef , AsyncFunctionDef определения функций и распечатка их местоположения, имени и т. Д. См. Пример кода ниже:

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
0 голосов
/ 04 июня 2019

Здесь

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