python lambda函数行为



我很难理解python 3.10.0中这些lambda函数的行为

我试图从lambda演算中定义NOT逻辑运算符(参见wikipedia上的定义)下面的实现是正确的:

In [1]: TRUE  = lambda a: lambda b: a
...: FALSE = lambda a: lambda b: b
...: NOT = lambda a: a(FALSE)(TRUE)
...: assert NOT(FALSE) == TRUE

但是,当我尝试对FALSETRUE进行文字替换时,代码失败

In [2]: NOT1 = lambda a: a(FALSE)(lambda a: lambda b: a)
...: assert NOT1(FALSE) == TRUE
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[2], line 2
1 NOT1 = lambda a: a(FALSE)(lambda a: lambda b: a)
----> 2 assert NOT1(FALSE) == TRUE
AssertionError: 
谁能告诉我为什么会这样?

Python函数==通过对象标识工作。(尝试以任何其他方式实现它的范围从"痛苦的不一致";到"完全不可能"。)您创建了另一个与NOT函数具有相同行为的函数,但它不是同一个函数对象,因此==表示它们不相等。

主要原则是每次调用lambda都会创建一个新函数.

你的第一个单元格是:

def TRUE(a):
def _inner(b):
return a
return _inner
def FALSE(a):
def _inner(b):
return b
return _inner
def NOT(a):
return a(FALSE)(TRUE)

执行NOT(FALSE)导致FALSE(FALSE)(TRUE)导致FALSE._inner(TRUE)返回TRUE,一个函数.

现在你的第二个单元格,执行NOT1(FALSE)结果FALSE(FALSE)(lambda...)结果FALSE._inner(lambda...)返回lambda...,另一个函数,但不是在TRUE中定义的函数. 还记得我之前说的原则吗?lambda语句创建新函数.

比较两个函数时,==运算符与函数的内容无关。它只关心被比较的项是否指向内存中的完全相同的函数。但是,由于TRUElambda...是两个独立的函数,即使内容相同,它们也位于不同的内存位置,因此==的比较失败。

感谢所有澄清==操作符问题的人。

我试图理解这两个函数的不同之处。让我们从更简单的例子

开始
In [18]: id(f)
Out[18]: 140465987909328
In [19]: id(g)
Out[19]: 140465990159088
In [20]: assert f==g
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[20], line 1
----> 1 assert f==g
AssertionError: 

显然这两个对象有不同的ids

In [21]: id(f)
Out[21]: 140465987909328
In [22]: id(g)
Out[22]: 140465990159088

对我来说不太明显的是它们确实有不同的哈希值

In [23]: f.__hash__()
Out[23]: 8779124244333
In [24]: g.__hash__()
Out[24]: 8779124384943

我需要的等价性测试可以在__code__.co_code函数属性上进行。

In [25]: f.__code__.co_code
Out[25]: b'|x00Sx00'
In [26]: g.__code__.co_code
Out[26]: b'|x00Sx00'
In [27]: assert f.__code__.co_code == g.__code__.co_code
然而,我很好奇这两个函数对象究竟在哪里不同。查看__code__的方法和属性,我们有
In [28]: dir(f.__code__)
Out[28]: 
['__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'co_argcount',
'co_cellvars',
'co_code',
'co_consts',
'co_filename',
'co_firstlineno',
'co_flags',
'co_freevars',
'co_kwonlyargcount',
'co_lines',
'co_linetable',
'co_lnotab',
'co_name',
'co_names',
'co_nlocals',
'co_posonlyargcount',
'co_stacksize',
'co_varnames',
'replace']

我们试着验证

In [29]: L = [x for x in dir(f.__code__) if x.startswith('co_')]
In [29]: for x in L:
...:     print(f"{x:20s}", getattr(f.__code__,x)==getattr(g.__code__,x))
...: 
co_argcount          True
co_cellvars          True
co_code              True
co_consts            True
co_filename          False
co_firstlineno       True
co_flags             True
co_freevars          True
co_kwonlyargcount    True
co_lines             False
co_linetable         True
co_lnotab            True
co_name              True
co_names             True
co_nlocals           True
co_posonlyargcount   True
co_stacksize         True
co_varnames          True

唯一不同的字段是co_filenameco_lines():

In [30]: f.__code__.co_filename, g.__code__.co_filename
Out[30]: ('<ipython-input-1-242f7af8e2bb>', '<ipython-input-2-a597939a9a2e>')
In [31]: f.__code__.co_lines()
Out[31]: <line_iterator at 0x7f10948790c0>
In [32]: g.__code__.co_lines()
Out[32]: <line_iterator at 0x7f10945178c0>
In [33]: list(f.__code__.co_lines())
Out[33]: [(0, 4, 1)]
In [34]: list(g.__code__.co_lines())
Out[34]: [(0, 4, 1)]

不能修改co_filename字段:

In [35]: f.__code__.co_filename = 'something_else'
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[35], line 1
----> 1 f.__code__.co_filename = 'something_else'
AttributeError: readonly attribute

在我看来,唯一不同的是这两个字段。

有趣的是,有一种情况下,两个变量名不同的lambda函数也应该被认为是相同的:

In [40]: f = lambda x:x
In [41]: g = lambda y:y
In [42]: f.__code__.co_code
Out[42]: b'|x00Sx00'
In [43]: g.__code__.co_code
Out[43]: b'|x00Sx00'

事实上,就co_code而言,它们是。

总之,我认为为函数引入equivalence操作符可能是有争议的,不同于__eq__操作符,可能是语法~=的东西,这似乎还没有被采用。你的想法呢?

相关内容

  • 没有找到相关文章

最新更新