zoukankan      html  css  js  c++  java
  • python函数之作用域(python3.5)

    1 基本概念

    1.1 命名空间 (namespace)

    命名空间是变量名到对象的映射(name -> obj)。目前大多数的命名空间以类似于python字典的形式实现,实现形式在未来可能发生变化。命名空间举例:内置变量(内置函数abs, 内置的异常等),模块中的全局变量,函数调用时的局部变量。在某种意义上讲,对象的属性也形成一个命名空间。重要的是,不同的命名空间中的变量没有任何关联,两个不同的命名空间中可以包含相同的变量名。

    命名空间有不同的创建时间和生命周期

    • 内置变量命名空间在python解释器启动时创建,并且在解释器运行期间永远不会被删除;
    • 一个模块的命名空间在模块被导入时创建,并且到解释器退出会一直存在;
    • 函数的本地(局部)命名空间在函数调用时创建,函数退出时删除;
    • 解释器顶层执行的语句都是 __main__ 模块的组成部分,它们有自己的命名空间。

    注:内置变量实际上同样是以模块的形式存在,模块名为 builtins

    1.2 作用域 (scope)

    作用域是Python程序中可以直接访问一个命名空间内变量的文本区域,在这里可直接访问表示不受任何限制,即命名空间内的变量在该文本区域内可见、可引用。对于函数而言,作用域和命名空间可以认为是等价的,特定的作用域对应特定的命名空间,它们可以直接对等。

    • 本地(局部)作用域:函数或者类的内部,对应函数调用时的函数的命名空间。
    • 全局作用域:整个程序的运行环境,对应‘__main__’模块的命名空间。

    全局作用域中无法直接访问本地作用域中定义的变量:

    def func1():
        name = 1
    
    print(func1)    # <function func1 at 0x101a03d08>
    print(name)
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    # NameError: name 'name' is not defined

     

    本地作用域中的变量定义:

    • 在python中,变量赋值即定义。在局部作用域内被赋值的变量,除非由 global 或者 nonlocal 声明,否则全部为局部变量,函数调用时存在于函数命名空间。
    • global var : 声明变量 var 为全局变量,它所有的引用和赋值都在模块的命名空间进行,作用域为全局作用域
    • nonlocal var : 将外层函数命名空间中的变量 var 绑定到本地作用域,使其在本地作用域可重新赋值。如果变量没有被声明为 nonlocal,这些变量在本地作用域仅可读,尝试给变量赋值则会在本地命名空间创建一个同名变量。

    nonlocal声明的变量在上层函数中必须存在,否则报错:

    1 test = 'global variable'
    2 
    3 def scope_test():
    4     def inner():
    5         nonlocal test
    6         print(test)
    7 
    8 scope_test()    # SyntaxError: no binding for nonlocal 'test' found

    2 示例

    2.1 本地作用域中变量的搜索遵守LEGB规则

    1. L-Local(function):函数内,其中的变量称为本地变量;
    2. E-Enclosing function locals:外层函数中的变量(例如closure),包含被声明为non-local的变量
    3. G-Global(module):函数定义所在模块,其中的变量称为全局变量
    4. B-Builtin(Python):Python内置模块
     1 def scope_test():
     2     def do_local():
     3         spam = "local spam"
     4 
     5     def do_nonlocal(): 
     6         nonlocal spam  # 递归向上寻找上层函数命名空间中的spam变量
     7         spam = "nonlocal spam"
     8 
     9     def do_global():
    10         global spam    # 在全局变量中寻找spam变量,没有则创建
    11         spam = "global spam"
    12 
    13     spam = "test spam"
    14     do_local()
    15     print("After local assignment:", spam)  # 输出本地变量 spam
    16     do_nonlocal()
    17     print("After nonlocal assignment:", spam)
    18     do_global()
    19     print("After global assignment:", spam)
    20 
    21 scope_test()
    22 print("In global scope:", spam)

    结果

    1 After local assignment: test spam
    2 After nonlocal assignment: nonlocal spam
    3 After global assignment: nonlocal spam
    4 In global scope: global spam
    

    2.2 闭包

    闭包:在嵌套函数中,如果内层函数引用了外层函数的变量,就形成了一个闭包。

    自由变量:被引用的外层函数变量,称为内层函数的自由变量。

    def fn():
        a = 1
        def closure():
            nonlocal a
            a += 1
            print(a)
        return closure
    
    inner = fn()  
    print(inner.__closure__)    # (<cell at 0x10240b408: int object at 0x100277bc0>,)
    inner()  # 2
    inner()  # 3

    外层函数执行完,其命名空间删除。但是因为 a 是内层函数的自由变量,所以变量 a 被保留,可以看作是 closure 函数对象的一个附加属性。

    3 静态检测

    3.1 本地变量

    python是在编译def语句时静态检测其本地变量的。

    a = 1
    def local_test():
        a += 1
        print(a)
    local_test()    # UnboundLocalError: local variable 'a' referenced before assignment
    
    print(b)    # NameError: name 'b' is not defined

    在编译local_test函数时,python就确定了变量 a 为函数的本地变量。所以在执行 a += 1 是会直接在本地命名空间寻找变量a。

    3.2 命名空间搜索链

    name = "lzl"
     
    def f1():
        print(name)
     
    def f2():
        name = "eric"
        f1()
     
    f2() # lzl

     

    一个函数的变量搜索路径是在它定义的时候决定的,不受它调用位置的影响。

    f1定义在全局作用域中,其变量的搜索路径为:本地作用域 --> 所在模块的全局作用域。所以最后的输出结果为‘lzl’。

     

    4 匿名函数

    Python借助lambda关键字定义匿名函数,格式如下:

      lambda 参数列表: 表达式

    lambda x: x + 1
    
    # 函数功能等同于下面的函数 def _(x): return x + 1

    示例

    下面一段代码的输出结果是什么:

     1 li = [lambda :x for x in range(10)]
     2 print(li[0]())

     

    其等价形式:

    1 def fn():
    2     return x 
    3 
    4 li = []
    5 for x in range(10):
    6     li.append(fn)
    7 li[0]()  # fn() -> 9,根据变量搜索规则,x在函数中没有定义,在全局变量中查找

     

     

     

    参考: 

    [1] http://www.cnblogs.com/livingintruth/p/3296010.html

    [2] file:///Library/Frameworks/Python.framework/Versions/3.5/Resources/English.lproj/Documentation/tutorial/classes.html#id2 

    [3] http://python.jobbole.com/86465/

     

  • 相关阅读:
    Qt -- 鼠标移入移出事件 enterEvent、leaveEvent
    QT -- QPainter介绍
    Qt -- 浅析QFontMetrics 获取字体宽度,高度
    函数声明后面的const用法
    QT -- 读取file数据/写数据到file
    QT -- QLineEdit按下回车键获取信息
    C++ -- fgets,fputs,fputc,fgetc总结
    QT -- QString / std::string转换为const char*
    C++ -- fopen函数用法
    HTML DOM树
  • 原文地址:https://www.cnblogs.com/wadirum/p/7674740.html
Copyright © 2011-2022 走看看