如何确定函数是否已由 `lambda` 或 `def` 声明?
How to find out if a function has been declared by `lambda` or `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
不能保证 return 正确的结果:
>>> a.__name__ = '<lambda>'
>>> is_lambda_function(a)
True
有没有比 __name__
属性产生更可靠结果的方法?
AFAIK,你不能可靠地 Python 3.
Python 2 用来定义一堆函数类型。因此,方法、lambda 和普通函数都有自己的类型。
Python 3 只有一种类型,即function
。使用 def
和 lambda
声明常规函数确实有不同的副作用: def
将名称设置为函数的名称(和限定名称)并可以设置文档字符串,而 lambda
将名称(和限定名称)设置为 <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
顺便说一句,如果问题是函数序列化,声明为 lambda 的函数可以成功 pickle,只要你给它一个唯一的合格名称:
>>> g = lambda x: 3*x
>>> g.__qualname__ = "g"
>>> pickle.dumps(g)
b'\x80\x03c__main__\ng\nq\x00.'
我趁机潜入了cpython's source to see if I could find anything, and I am afraid I have to second :你不能。
简而言之,这是 lambda 在解释器中的旅程:
- 在解析过程中,lambda 与其他所有表达式一样,are read into an expr_ty 是一个包含每个表达式数据的巨大联合。
- 然后将此
expr_ty
转换为适当的类型(Lambda,在我们的例子中)
- 一段时间后我们降落在 the function that compiles lambdas
- 这个函数调用assemble, which calls makecode, which initializes a PyCodeObject(函数、方法和lambda,都在这里结束)。
从这里,我没有看到任何特定于 lambda 的东西。这一点,再加上 Python 允许您修改对象的几乎每个属性这一事实,使得 me/us 相信您想要做的事情是不可能的。
您可以查看__code__.co_name
。它包含编译 function/lambda 时的名称:
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
是 read-only...
>>> 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
如果我声明两个函数 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
不能保证 return 正确的结果:
>>> a.__name__ = '<lambda>'
>>> is_lambda_function(a)
True
有没有比 __name__
属性产生更可靠结果的方法?
AFAIK,你不能可靠地 Python 3.
Python 2 用来定义一堆函数类型。因此,方法、lambda 和普通函数都有自己的类型。
Python 3 只有一种类型,即function
。使用 def
和 lambda
声明常规函数确实有不同的副作用: def
将名称设置为函数的名称(和限定名称)并可以设置文档字符串,而 lambda
将名称(和限定名称)设置为 <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
顺便说一句,如果问题是函数序列化,声明为 lambda 的函数可以成功 pickle,只要你给它一个唯一的合格名称:
>>> g = lambda x: 3*x
>>> g.__qualname__ = "g"
>>> pickle.dumps(g)
b'\x80\x03c__main__\ng\nq\x00.'
我趁机潜入了cpython's source to see if I could find anything, and I am afraid I have to second
简而言之,这是 lambda 在解释器中的旅程:
- 在解析过程中,lambda 与其他所有表达式一样,are read into an expr_ty 是一个包含每个表达式数据的巨大联合。
- 然后将此
expr_ty
转换为适当的类型(Lambda,在我们的例子中) - 一段时间后我们降落在 the function that compiles lambdas
- 这个函数调用assemble, which calls makecode, which initializes a PyCodeObject(函数、方法和lambda,都在这里结束)。
从这里,我没有看到任何特定于 lambda 的东西。这一点,再加上 Python 允许您修改对象的几乎每个属性这一事实,使得 me/us 相信您想要做的事情是不可能的。
您可以查看__code__.co_name
。它包含编译 function/lambda 时的名称:
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
是 read-only...
>>> 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