lambda函数不使用本地最大范围

时间:2017-03-07 22:33:26

标签: python python-3.x

我正在编写一个简短的代码来处理数据文件,偶然发现了lambda函数工作方式我不理解的东西。

问题在于:我有一个关键字列表,以及这些关键字出现在数据文件中的行的索引列表,并希望将某些操作应用于所述行(和/或相邻的行,因此需要索引列表而不仅仅是行列表)。

为此,我定义了一个字典,将每个关键字与一个lambda函数相关联,该函数将所需操作应用于所需行。例如:

methnames = {'acell'     : lambda i : float(dat[i][1])  } #with dat the data file

(除了多个关键字和更复杂的功能)。

现在,要按照我的预期执行它,需要定义一个名为dat的全局变量,所以我只需要放一个dat=[],因为我会在将定义dat的本地范围。

除了我执行整个代码之外,我得到一个IndexError,并且回溯告诉我,即使该lambda确实是从通常应该定义dat的本地范围内调用的,它仍然使用全球dat

即使我可以解决这个问题,对于Python来说这似乎是一种非常奇怪的行为,所以我可能错过了一些东西。

以下是代码的简化版本:

dat=[]
methnames = {'acell'     : lambda i : float(dat[i][1])  }

def test(dat):
    return(methnames['acell'](0))

 a=test([['acell',0,1,1]])

通常应该给a=0,这是返回:

Traceback (most recent call last):

  File "<ipython-input-21-cc8eb6df810c>", line 1, in <module>
runfile('/home/penwwern/Documents/mineralo/MinPhys/FrI/out/test.py', wdir='/home/penwwern/Documents/mineralo/MinPhys/FrI/out')

  File "/usr/lib/python3/dist-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 699, in runfile
execfile(filename, namespace)

  File "/usr/lib/python3/dist-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 88, in execfile
exec(compile(open(filename, 'rb').read(), filename, 'exec'), namespace)

  File "/home/penwwern/Documents/mineralo/MinPhys/FrI/out/test.py", line 18, in <module>
a=test([['acell',0,1,1]])

  File "/home/penwwern/Documents/mineralo/MinPhys/FrI/out/test.py", line 15, in test
    return(methnames['acell'](0))

  File "/home/penwwern/Documents/mineralo/MinPhys/FrI/out/test.py", line 9, in <lambda>
    methnames = {'acell'     : lambda i : float(dat[i][1])  }

IndexError: list index out of range

1 个答案:

答案 0 :(得分:2)

Python不会检查调用者的范围,而是检查函数的定义范围。这是因为Python is lexically scoped

In [1]: def f(): print(i)

In [2]: def f2():
   ...:     i = 10
   ...:     f()
   ...:

In [3]: f2()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-fdec4c1c071f> in <module>()
----> 1 f2()

<ipython-input-2-5ab58f8d1867> in f2()
      1 def f2():
      2     i = 10
----> 3     f()
      4

<ipython-input-1-a5bb8b95e798> in f()
----> 1 def f(): print(i)
NameError: name 'i' is not defined

现在,我们可以在i定义的同一范围内绑定f

In [4]: i = 88

In [5]: f2()
88

但是,它确实检查了关于定义范围的封闭范围:

In [6]: def f3():
   ...:     i = 1
   ...:     def inner():
   ...:         print(i)
   ...:     return inner
   ...:

In [7]: inner = f3()

In [8]: inner()
1

In [9]: print(i)
88

词汇范围很常见。以下是来自维基百科的更多信息:

  

范围界定的一个根本区别是&#34;程序的一部分&#34;   手段。在具有词法范围的语言中(也称为静态范围),   名称解析取决于源代码中的位置和   词汇上下文,由命名变量或的位置定义   功能已定义。相比之下,在具有动态范围的语言中   名称解析取决于名称时的程序状态   遇到的是由执行上下文或调用确定的   上下文。在实践中,对于词法范围,变量的定义是   通过搜索其包含的块或函数解决,然后如果那样   无法搜索外部包含块,依此类推,而使用   查询调用函数的动态范围,然后是函数   它调用了调用函数,等等,进行调用   叠加。[4]当然,在这两个规则中,我们首先寻找一个本地   变量的定义。

https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope