According to my programming language class,使用的语言使用词法作用域
在以下环境中评估函数主体: 函数是定义的,而不是函数调用的环境。
例如,SML遵循以下行为:
val x = 1
fun myfun () =
x
val x = 10
val res = myfun() (* res is 1 since x = 1 when myfun is defined *)
另一方面,Python不遵循以下行为:
x = 1
def myfun():
return x
x = 10
myfun() # 10 since x = 10 when myfun is called
答案 0 :(得分:4)
除了@abarnert和@ user2357112的响应之外,它还可以帮助您考虑等效于Python代码的SML:
get_sheet_by_name()
第一行声明一个变量val x = ref 1
fun myfun () = !x
val () = x := 10
val res = myfun ()
,该变量引用一个整数并将引用的单元格设置为1。函数主体取消引用x
以返回引用的单元格中的值。第三行将引用的单元格设置为10。第四行中的函数调用现在返回10。
我使用笨拙的x
语法来修复顺序。声明的添加仅是因为其对val () = _
的副作用。也可以这样写:
x
环境是不可变的-特别是val x = ref 1
fun myfun () = !x;
x := 10;
val res = myfun ()
始终指向相同的存储单元-但是某些数据结构(参考单元和数组)是可变的。
答案 1 :(得分:3)
您的Python myfun
正在使用其定义环境中的x
变量,但是该x
变量现在拥有一个新值。词法作用域意味着函数从定义位置记住变量,但这并不意味着它们必须在定义函数时对这些变量的值进行快照。
您的标准ML代码具有两个 x
变量。 myfun
正在使用第一个变量。
答案 2 :(得分:3)
在Python中,就像在SML或(现代)Lisp中一样,在定义函数的环境中评估函数的主体。因此,所有三种语言都在词法范围内。
在Python和Lisp中,环境是可变的。也就是说,您可以为现有变量分配一个新值,从而使该变量所属的环境发生变化。在该环境中定义的所有函数都将在该环境中进行评估-这意味着它们将看到变量的新值。
在SML中,环境不是可变的;环境不能更改,没有没有新值,因此毫无疑问函数是否会看到该新值。
语法可能会引起误解。在ML中,val x = 1
和val x = 10
都定义了一个全新的变量。在Python中,x = 1
和x = 10
是赋值语句-它们会重新赋值给现有变量,仅当尚无该变量时才定义一个新变量。 (您在Lisp中看不到这一点,例如let
和setq
很难混淆。)
顺便说一句,具有可变变量的闭包在功能上等同于可变对象(从面向对象的角度来看),因此Lisp(和Python)的这一功能传统上非常重要。
作为一个旁注,Python实际上对全局名称空间(及其上方的内置函数)有一些特殊的规则,因此您可以辩称示例中的代码在技术上不依赖于词法作用域。但是,如果将整个内容放在一个函数中并调用该函数,那么它肯定是词法作用域的一个示例,因此全局问题在这里实际上并不那么重要。
答案 3 :(得分:1)
TL;DR
(如下)给出了Python的词法作用域的简单示例 ...
但是首先让我添加一些 rationale 来帮助 me 理解问题(并写下此答案)。
在Python的变量评估中有两个相互重叠的概念,这可能会造成一些混乱:
正如OP所说,当我们可以切换变量的值(“ x
”)或可以定义一个函数(使用“ {{1} }“)甚至没有事先声明其中的变量(” x
“);例如,
x
这可能会使有关变量和内存中实际对象的名称绑定的知识更加混乱,从而使变量的 value 概念有些松散(与C等其他语言相比)。
> def f():
print(x)
> x = 1
> f()
1
Python 词法作用域在起作用:
TL;DR
然后我们可以看到,在评估变量时,递归(BGEL)范围搜索策略正在遵守定义了功能'> def f():
print(x)
> def g(foo):
x = 99
foo()
print(x)
> x = 1
> g(f)
1
99
'的作用域局部和之上。