Как узнать, была ли функция объявлена ​​`lambda` или` def`? - PullRequest
0 голосов
/ 08 января 2019

Если я объявлю две функции a и b:

def a(x):
    return x**2

b = lambda x: x**2

Я не могу использовать type для их дифференциации, поскольку они оба одного типа.

assert type(a) == type(b)

Также types.LambdaType не помогает:

>>> import types
>>> isinstance(a, types.LambdaType)
True
>>> isinstance(b, types.LambdaType)
True

Можно использовать __name__ как:

def is_lambda_function(function):
    return function.__name__ == "<lambda>"

>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True

Однако, поскольку __name__ мог быть изменен, is_lambda_function не гарантирует правильного результата:

>>> a.__name__ = '<lambda>'
>>> is_lambda_function(a)
True

Есть ли способ, который дает более надежный результат, чем атрибут __name__?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Я воспользовался возможностью, чтобы погрузиться в источник cpython , чтобы посмотреть, смогу ли я найти что-нибудь, и я боюсь, что мне нужно секунду Серж ответил : вы не можете.

Вкратце, это лямбда-путешествие в интерпретаторе:

  1. Во время синтаксического анализа лямбды, как и любое другое выражение, считываются в expr_ty , который представляет собой огромное объединение, содержащее данные каждого выражения.
  2. Этот expr_ty затем преобразуется в соответствующий тип ( Lambda , в нашем случае)
  3. Через некоторое время мы попадаем в функцию, которая компилирует лямбды
  4. Эта функция вызывает сборка , которая вызывает makecode , который инициализирует PyCodeObject (функции, методы, а также лямбда-выражения, все заканчиваются здесь).

Из этого я не вижу ничего специфического для лямбд. Это в сочетании с тем фактом, что Python позволяет вам изменять практически все атрибуты объектов, заставляет меня / нас полагать, что то, что вы хотите сделать, невозможно.

0 голосов
/ 08 января 2019

AFAIK, вы не можете надежно работать в Python 3.

Python 2 используется для определения группы типов функций. По этой причине методы, лямбды и простые функции имеют каждый свой тип.

Python 3 имеет только один тип, function. Действительно, существуют различные побочные эффекты, когда объявление обычной функции с помощью def и lambda: def устанавливает имя для имени (и квалифицированного имени) функции и может устанавливать строку документации, тогда как lambda устанавливает name (и полное имя), равное <lambda>, и задает строку документации None. Но как это можно изменить ...

Если функции загружаются из обычного источника Python (а не вводятся в интерактивной среде), модуль inspect позволяет получить доступ к исходному коду Python:

import inspect

def f(x):
    return x**2

g = lambda x: x**2

def is_lambda_func(f):
    """Tests whether f was declared as a lambda.

Returns: True for a lambda, False for a function or method declared with def
Raises:
    TypeError if f in not a function
    OSError('could not get source code') if f was not declared in a Python module
                                         but (for example) in an interactive session
"""
    if not inspect.isfunction(f):
        raise TypeError('not a function')
    src = inspect.getsource(f)
    return not src.startswith('def') and not src.startswith('@') # provision for decorated funcs

g.__name__ = 'g'
g.__qualname__ = 'g'

print(f, is_lambda_func(f))
print(g, is_lambda_func(g))

Будет напечатано:

<function f at 0x00000253957B7840> False
<function g at 0x00000253957B78C8> True

Кстати, если проблема заключалась в сериализации функции, функция, объявленная как лямбда, может быть успешно выбрана, если вы дадите ей уникальное квалифицированное имя:

>>> g = lambda x: 3*x
>>> g.__qualname__ = "g"
>>> pickle.dumps(g)
b'\x80\x03c__main__\ng\nq\x00.'
...