Как узнать, была ли функция объявлена `lambda` или`def`?
Если я объявлю две функции 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 ответа
AFAIK, вы не можете надежно в Python 3.
Python 2 используется для определения группы типов функций. По этой причине методы, лямбды и простые функции имеют каждый свой тип.
Python 3 имеет только один тип, который function
, Есть действительно разные побочные эффекты, когда объявление регулярной функции с def
и lambda
: def
устанавливает имя в имя (и полное имя) функции и может установить строку документации, в то время как lambda
устанавливает имя (и полное имя), чтобы быть <lambda>
и устанавливает для строки документа значение Нет. Но как это можно изменить...
Если функции загружаются из обычного исходного кода 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.'
Вы можете проверить
__code__.co_name
. Он содержит то, что имя было во время компиляции функции / лямбда:
def a(x):
return x**2
b = lambda x: x**2
def is_lambda_function(f):
return f.__code__.co_name == "<lambda>"
>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True
И, вопреки
__name__
,
__code__.co_name
это только для чтения...
>>> a.__name__ = "<lambda>"
>>> b.__name__ = "b"
>>> a.__code__.co_name = "<lambda>"
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: readonly attribute
>>> b.__code__.co_name = "b"
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: readonly attribute
... так что результаты останутся прежними:
>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True
Я воспользовался возможностью погрузиться в исходники cpython, чтобы посмотреть, смогу ли я найти что-нибудь, и я боюсь, что должен ответить Сержу: вы не можете.
Вкратце, это лямбда-путешествие в интерпретаторе:
- Во время синтаксического анализа лямбды, как и любое другое выражение, считываются в expr_ty, который представляет собой огромный союз, содержащий данные каждого выражения.
- это
expr_ty
затем преобразуется в соответствующий тип (в нашем случае лямбда) - Через некоторое время мы попадаем в функцию, которая компилирует лямбды
- Эта функция вызывает ассемблер, который вызывает makecode, который инициализирует PyCodeObject (здесь заканчиваются функции, методы, а также лямбда-выражения).
Из этого я не вижу ничего специфического для лямбд. Это, в сочетании с тем фактом, что Python позволяет вам изменять практически все атрибуты объектов, заставляет меня / нас верить в то, что вы хотите сделать, невозможно.