Python中“绑定到变量”和“绑定到对象”之间的区别是什么

时间:2010-12-09 09:32:35

标签: python binding closures

当我在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

提前致谢。

3 个答案:

答案 0 :(得分:8)

两个关键事实:

  1. Python使用the LEGB rule来查找 (裸)变量名称的值。 LEGB代表Local,Extended, Global,Builtins。这意味着一个 变量名称“绑定”到本地 价值,如果没有,那么 价值在中查找 扩展范围,如果没有 这样的变量,查找是在 全球范围,最后在 内置范围。
  2. 定义类似

    的功能时
    def closureTest(maxIndex=maxIndex):
        return maxIndex
    

    默认值固定为 definition-time,而不是run-time。到definition-time我的意思是处理def语句的时间 - 定义函数时。 run-time我指的是调用函数的时间。请注意,当您具有嵌套函数时,在调用外部函数之后会发生内部函数definition-time


  3. 由于变量名maxIndex被过度使用,第一个例子变得更加复杂。如果您第一个明白这一点,您将理解第一个例子:

    >>> def testClosure(maxIndex):              
            def closureTest(index=maxIndex):     # (1)
                return index                     
            maxIndex += 5
            return closureTest()                 # (2)
    >>> print(testClosure(10))
    
    • (1)在定义时间,索引 默认值设置为10.
    • (2)调用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