美文网首页PYTHON基础
12.函数作用域

12.函数作用域

作者: Stone_説 | 来源:发表于2020-12-19 23:49 被阅读0次

    1.golbal关键字
    2.闭包概念
    3.默认值作用域
    4.不修改默认值的两个方法
    5.变量名解析规则LEGB
    6.函数销毁

    1.golbal关键字

    使用global关键字的变量,将函数定义时的变量声明为使用外部全局作用域中定义的变量
    在内部作用域为一个外部作用域的变量赋值,不是在内部作用域定义一个新变量,改变量作用域还是全局的

    >>> x = 5
    >>> def foo():
    ...     x += 1
    >>> foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in foo
    UnboundLocalError: local variable 'x' referenced before assignment
    >>> def foo():
    ...     global x
    ...     x += 1
    ...     print(x)
    >>> foo()
    6
    
    >>> y = 10
    >>> def foo():
    ...     global y
    ...     y = 99
    ...     y += 1
    ...     print(y)
    >>> foo()
    100
    

    2.闭包

    自由变量:未在本地作用域中定义的变量。
    闭包:这仅仅是一个概念,出现函数嵌套时,内层函数引用到了外层函数的自由变量,就形成了闭包
    注意:使用nonlocal关键字,将变量标记为不在本地作用域中定义,而在上级的某一级作用域中定义,但不能在全局作用域中定义

    >>> def counter():
    ...     count=0
    ...     def inc():
    ...             count += 1
    ...             return count
    ...     return inc
    >>> foo = counter()
    >>> foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in inc
    UnboundLocalError: local variable 'count' referenced before assignment
    
    >>> def counter():
    ...     count = 0
    ...     def inc():
    ...             nonlocal count
    ...             count += 1
    ...             return count
    ...     return inc
    >>> foo = counter()
    >>> type(foo)
    <class 'function'>
    >>> foo()
    1
    >>> foo()
    2
    

    变量在全局作用域中定义,使用nonlocal声明时会报错

    >>> a = 100
    >>> def fun():
    ...     nonlocal a
    ...     a += 1
    ...     print(a)
      File "<stdin>", line 2
    SyntaxError: no binding for nonlocal 'a' found
    

    3.默认值作用域

    python把函数的默认值放在了函数对象的属性中,这个属性会伴随函数对象的生命周期
    属性defaults属性中使用元组保存所有位置参数默认值
    属性kwdefaults中使用字典保存所有keyword-only参数的默认值

    3.1__defaults__属性
    >>> def foo(xyz=1):
    ...     print(xyz)
    ... 
    >>> foo()
    1
    >>> print(foo.__defaults__)
    (1,)
    >>> def foo(xyz=[]):
    ...     xyz.append(1)
    ...     print(xyz)
    ... 
    >>> foo()
    [1]
    >>> foo()
    [1, 1]
    >>> print(xyz)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'xyz' is not defined
    >>> print(foo.__defaults__)
    ([1, 1],)
    
    >>> def fn(xyz=[],m=123,n='abc'):
    ...     xyz.append(1)
    ...     print(xyz)
    ... 
    >>> print(id(foo),foo.__defaults__)
    139713265391504 ([1, 1],)
    >>> foo()
    [1, 1, 1]
    >>> print(id(foo),foo.__defaults__)
    139713265391504 ([1, 1, 1],)
    >>> foo()
    [1, 1, 1, 1]
    >>> print(id(foo),foo.__defaults__)
    139713265391504 ([1, 1, 1, 1],)
    

    函数地址并没有变,就是说foo这个函数对象没有变过,函数调用时defaults属性中使用元组保存默认值xyz默认值的引用类型,引用类型的元素变动,并不是元组的变化

    3.2非引用类型却省值
    >>> def foo(xyz,m=123,n='abc'):
    ...     m=456
    ...     n='def'
    ...     print(xyz)
    ... 
    >>> print(foo.__defaults__)
    (123, 'abc')
    >>> foo('stone')
    stone
    >>> print(foo.__defaults__)
    (123, 'abc')
    

    属性defaults中使用元组保存所有位置参数默认值,它不会因为在函数体内改变了局部变量的值而发生改变

    3.3__kwdefaults__属性
    >>> def foo(xyz,m=123,*,n='abc',t=[1,2]):
    ...     m=456
    ...     n='def'
    ...     t.append(300)
    ...     print(xyz,m,n,t)
    ... 
    >>> print(foo.__defaults__,foo.__kwdefaults__)
    (123,) {'n': 'abc', 't': [1, 2]}
    >>> foo('stone')
    stone 456 def [1, 2, 300]
    >>> print(foo.__defaults__,foo.__kwdefaults__)
    (123,) {'n': 'abc', 't': [1, 2, 300]}
    
    3.4"+"和"+="的区别
    >>> def foo(a=[]):
    ...     a += [5]
    ... 
    >>> print(foo.__defaults__)
    ([],)
    >>> foo()
    >>> foo()
    >>> print(foo.__defaults__)
    ([5, 5],)
    
    >>> def foo(a=[]):
    ...     a = a + [5]
    ... 
    >>> print(foo.__defaults__)
    ([],)
    >>> foo()
    >>> foo()
    >>> print(foo.__defaults__)
    ([],)
    

    +号表示两个列表合并并且返回一个全新的列表
    +=表示,就地修改前一个列表,在其后追加后一个列表,就是列表的extend方法

    4.不修改默认值的两个方法

    当使用可变类型作为默认值时,就可能会修改默认值,如何做到不修改,或者按需修改呢?

    4.1函数体内不改变默认值
    >>> def foo(xyz=[],u='abc',z=123):
    ...     print(id(xyz))
    ...     xyz=xyz[:]
    ...     print(id(xyz))
    >>> foo()
    140619548344576
    140619548344704
    
    >>> def foo(xyz=[],u='abc',z=123):
    ...     xyz=xyz[:]
    ...     xyz.append(1)
    ...     print(xyz)
    >>> print(foo.__defaults__)
    ([], 'abc', 123)
    >>> foo()
    [1]
    >>> print(foo.__defaults__)
    ([], 'abc', 123)
    >>> foo()
    [1]
    >>> print(foo.__defaults__)
    ([], 'abc', 123)
    >>> foo([10,8])
    [10, 8, 1]
    >>> print(foo.__defaults__)
    ([], 'abc', 123)
    
    4.2传入不可变类型默认值

    如果使用缺省值None就创建一个列表
    如果传入一个列表,就修改这个列表

    >>> def foo(xyz=None,u='abc',z=123):
    ...     if xyz is None:
    ...             xyz = []
    ...     xyz.append(1)
    ...     print(xyz)
    >>> foo()
    [1]
    >>> print(foo.__defaults__)
    (None, 'abc', 123)
    >>> foo()
    [1]
    >>> print(foo.__defaults__)
    (None, 'abc', 123)
    >>> foo([10,8])
    [10, 8, 1]
    >>> print(foo.__defaults__)
    (None, 'abc', 123)
    

    5.变量名解析规则LEGB

    Local,本地作用域、局部作用域的local命名空间。函数调用时创建,调用结束消亡
    Enclosing,Python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间
    Global,全局作用域,即一个模块空间。模块被import时创建,解释器退出时消亡
    Build-in,内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡。例如:print(open),print和open都是内置的变量

    6.函数销毁

    6.1全局函数销毁

    重新定义同名函数
    del语句删除函数对象
    程序结束时

    >>> def foo(xyz=[],u='abc',z=123):
    ...     xyz.append(1)
    ...     return xyz
    ...
    >>> print(foo(),id(foo),foo.__defaults__)
    [1] 2200919120344 ([1], 'abc', 123)
    
    >>> def foo(xyz=[],u='abc',z=123):
    ...     xyz.append(1)
    ...     return xyz
    >>> print(foo(),id(foo),foo.__defaults__)
    [1] 2200919120208 ([1], 'abc', 123)
    >>> del foo
    >>> print(foo(),id(foo),foo.__defaults__)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'foo' is not defined
    
    6.2局部函数销毁

    重新在上级作用域定义同名函数
    del语句删除函数名称,函数对象的引用计数减1
    上级作用域销毁时

    >>> def foo(xyz=[],u='abc',z=123):
    ...     xyz.append(1)
    ...     def inner(a=10):
    ...             pass
    ...     print(inner)
    ...     def inner(a=100):
    ...             pirnt(xyz)
    ...     print(inner)
    ...     return inner
    >>> bar=foo()
    <function foo.<locals>.inner at 0x000001C0BE8A9950>
    <function foo.<locals>.inner at 0x000001C0BE8A9A60>
    >>> print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
    1927342103000 1927342103136 ([1], 'abc', 123) (100,)
    >>> del bar
    >>> print(id(foo),id(bar),foo.__defaults__,bar.__defaults__)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'bar' is not defined
    

    相关文章

      网友评论

        本文标题:12.函数作用域

        本文链接:https://www.haomeiwen.com/subject/wrrcnktx.html