当我在Python中学习“命名和绑定”时,我看到了以下示例:
>>> def testClosure(maxIndex):
def closureTest(maxIndex=maxIndex):
return maxIndex
maxIndex += 5
return closureTest()
>>> print(testClosure(10))
10
>>> def testClosure(maxIndex):
def closureTest():
return maxIndex
maxIndex += 5
return closureTest()
>>> print(testClosure(10))
15
作者将其解释为: 在后一个函数中,内部作用域中的自由变量绑定到外部作用域中的变量,而不是对象。
然后我的问题是:Python中“绑定到变量”和“绑定到对象”有什么区别?
此外,它非常棘手:如果我重新安排代码,结果会有所不同。
>>> def testClosure(maxIndex):
maxIndex += 5
def closureTest(maxIndex=maxIndex):
return maxIndex
return closureTest()
>>> print(testClosure(10))
15
提前致谢。
答案 0 :(得分:8)
两个关键事实:
定义类似
的功能时def closureTest(maxIndex=maxIndex):
return maxIndex
默认值固定为
definition-time
,而不是run-time
。到definition-time
我的意思是处理def
语句的时间 - 定义函数时。 run-time
我指的是调用函数的时间。请注意,当您具有嵌套函数时,在调用外部函数之后会发生内部函数definition-time
。
由于变量名maxIndex
被过度使用,第一个例子变得更加复杂。如果您第一个明白这一点,您将理解第一个例子:
>>> def testClosure(maxIndex):
def closureTest(index=maxIndex): # (1)
return index
maxIndex += 5
return closureTest() # (2)
>>> print(testClosure(10))
closureTest()
时
没有参数,index
设置为
默认值10.所以这是
返回值。def testClosure(maxIndex):
def closureTest():
return maxIndex # (3)
maxIndex += 5
return closureTest() # (4)
print(testClosure(10))
(3)LEGB规则告诉Python
在{中查找maxIndex
的值
当地范围。没有maxIndex
在本地范围内定义,所以它
看起来在扩展范围内。它找到了
maxIndex
这是一个参数
到testClosure
。
(4)到closureTest()
时
被叫maxIndex
的值为15。
所以maxIndex
返回了
closureTest()
是15。
>>> def testClosure(maxIndex):
maxIndex += 5 # (5)
def closureTest(maxIndex=maxIndex): # (6)
return maxIndex
return closureTest() # (7)
(5)maxIndex
是15
(6)设置了closureTest的maxIndex
默认值为15
定义时间。
(7)调用closureTest()
时
没有参数,默认值
使用maxIndex
。价值15
归还。
答案 1 :(得分:3)
如果你想到'def'语句的参数表达式中发生的绑定,可能会更容易混淆。当你看到'def closureTest(maxIndex = maxIndex):'这是一个像'if'或'while'之类的语句,后面跟着一组要解析并绑定到函数的代码(可调用对象)。
'def'语句在其找到的范围内进行评估(概念上与嵌套/缩进的级别相同)。它的参数表达式声明了如何将参数映射到函数自己范围内的名称。任何提供默认值的函数(例如示例中的maxIndex)都会创建一个函数对象,其对应的参数名称绑定到“def”语句中(在范围内)命名或实例化的任何对象。
调用函数时,每个参数(其范围内的名称)都绑定到提供给函数的任何参数。因此,任何可选参数都被绑定到作为'def'语句的一部分被评估的任何参数。
在所有示例中,在外部函数的每次调用期间都会创建一个内部函数。在第二个示例中,参数列表为空,内部函数只是通过一个嵌套范围级别查看名称。在第一个示例中,内部函数的def语句在新函数的命名空间内创建一个默认的maxIndex名称(从而防止使用来自周围范围的值对名称进行任何解析,正如您对任何局部变量所期望的那样)在任何功能中。)
在最后一个示例中,在内部函数(重新)定义之前修改了maxIndex的值。当你意识到函数正在(重新)定义每个外部函数调用时,它应该看起来不那么棘手。
Python是一种动态语言。 'def'是每次控制流通过代码行时VM正在执行的语句。 (是的,代码已经过字节编译,但'def'被编译成VM操作码,在运行时执行代码评估和名称绑定(到函数名称)。
如果使用像'(myList = list())'这样的参数列表定义函数,则会在执行定义时实例化列表。只要在没有参数的情况下调用函数,就可以从函数代码的调用内访问它。任何带参数的调用都将使用绑定到调用时提供的参数的参数名执行。 (在def时间实例化的对象仍然被定义的代码对象引用 - 在def语句之后缩进的套件。)
如果不保持参数和参数之间的区别,这些都没有任何意义。请记住,参数是函数定义的一部分;它们定义了如何将参数映射到函数的本地名称空间。参数是调用的一部分;它们是传递给函数调用的东西。
我希望这会有所帮助。我意识到区别是微妙的,这些术语经常被误用,好像它们是可以互换的(包括整个Python文档)。
答案 2 :(得分:0)
>>> def testClosure(maxIndex):
def closureTest(maxIndex=maxIndex): # You're creating a function with a kwarg of maxIndex
return maxIndex # which references the int passed in from outside
maxIndex += 5 # Incrementing maxIndex means that the maxIndex in the outer
return closureTest() # scope now points to a different int.
>>> print(testClosure(10))
10
考虑:
>>> a = b = 1
>>> a += 1
>>> a
2
>>> b
1
我不知道“绑定到对象”和“绑定到变量”是什么意思。 “变量”是对事物的引用,当您递增a
时,您要修改它以引用不同的值,b
仍然引用原始值。
如果你没有将maxIndex的值传递给你的内部函数然后请求它,因为它没有在本地范围内定义,它在扩展范围内寻找。
我在这里做了一个非常大的假设,但你可以看到执行时间的差异,我倾向于将这个扩展查询的成本归因于:
>>> import timeit
>>> def testClosure(maxIndex):
... def closureTest(maxIndex=maxIndex):
... return maxIndex
... maxIndex += 5
... return closureTest()
...
>>> def testClosure2(maxIndex):
... def closureTest():
... return maxIndex
... maxIndex += 5
... return closureTest()
...
>>> timeit.Timer('testClosure(10)','from __main__ import testClosure').timeit()
1.4626929759979248
>>> timeit.Timer('testClosure2(10)','from __main__ import testClosure2').timeit()
1.7869210243225098