• python学习笔记:第10天 函数进阶和作用域


    1. 函数进阶

    动态接收位置参数

    之前写的函数都是固定参数的,假设有个函数需要的参数由几十个,一个个写在形参的位置会非常麻烦,因此我们要考虑使用动态参数,使用动态参数时需要在参数前加*,表示接收多个参数:

    In [13]: def func5(a, b, c, d, e, f):
        ...:     print(a, b, c, d, e, f)
    
    In [14]: func5(1, 2, ,3 ,4 , 5, 6)      # 按照之前的写法是在传参的时候参数的个数都是固定的
    1 2 3 4 5 6
    
    In [18]: def func6(*args):              # 使用动态接收参数后可以接收任个位置参数
        ...:     print(args)
    
    In [19]:
    
    In [19]: func6(1, 2, 3 ,4 , 5, 6, 7, 8, 9, 10)
    (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    In [20]:
    

    从上面的例子我们可以看出,动态参数可以接收任意个参数,在形参中作为一个元组的形式传递过来;但是此时要注意的是:动态参数必须要在位置参数的后面:

    In [20]: def func7(*args, a, b):
        ...:     print(args)
    
    In [21]: func7(1, 2, 3, 4)
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-21-8171430efdb6> in <module>
    ----> 1 func7(1, 2, 3, 4)
    
    TypeError: func7() missing 2 required keyword-only arguments: 'a' and 'b'
    
    In [22]:
    

    位置参数放在后面的时候,所有的参数都被args接收了,即a和b永远接收不到参数,因此动态参数必须在位置参数的后面

    In [22]: def func8(a, *args, b=100):    # 正确使用方法
        ...:     print(a, args, b)
    
    In [23]: func8(1, 2, 3, 4)
    1 (2, 3, 4) 100
    
    In [24]: def func8(a, b=100, *args):
        ...:     print(a, args, b)
    
    In [25]: func8(1, 2, 3, 4)
    1 (3, 4) 2
    
    In [26]: func8(1, 2)
    1 () 2
    
    In [27]: func8(1)
    1 () 100
    
    In [28]:
    

    从上面的例子可以看出,默认参数放在动态传参在之前时,只有在一种情况下才有效,即位置参数不够的情况下,会使用默认参数的值,那此时的动态传参也就没有意义了;所以只有当默认参数放在动态参数后面时,默认参数时永远生效的。

    那么我们可以总结出动态传参的要注意的顺序:*位置参数, 动态参数, 默认参数

    动态接收关键字参数

    在python中使用*可以动态接收位置参数,但是这种方法并无接收关键字参数,在python中应该使用**来接收动态关键字参数

    In [28]: def func8(**kwargs):
        ...:     print(kwargs)
    
    In [29]: func8(a='aaa', b='bbb')
    {'a': 'aaa', 'b': 'bbb'}
    
    In [30]:
    

    顺序的问题, 在函数调⽤的时候, 如果先给出关键字参数, 则整个参数列表会报错.

    def func(a, b, c, d):
    print(a, b, c, d)
    # 关键字参数必须在位置参数后⾯, 否则参数会混乱
    func(1, 2, c=3, 4)
    

    所以关键字参数必须在位置参数后⾯. 由于实参是这个顺序. 所以形参接收的时候也是这个顺序. 也就是说位置参数必须在关键字参数前⾯. 动态接收关键字参数也要在后⾯

    最终顺序(*)为:

    • 位置参数 > *args > 默认值参数 > **kwargs

    这四种参数可以任意的进⾏使⽤.如果想接收所有的参数:

    def func(*args, **kwargs):
      print(args, kwargs)
    
    func("麻花藤","⻢晕",wtf="胡辣汤")
    

    动态参数的另⼀种传参⽅式:

    def fun(*args):
      print(args)
    
    lst = [1, 4, 7]
    fun(lst[0], lst[1], lst[2])
    

    2. 命名空间

    我们用于存放变量名和其值的对应关系的空间,可以给它一个名字叫命名空间,我们的变量存储的时候就是存储在这片空间内的。

    命名空间的分类

    • 全局命名空间:在单个py文件中,函数声明外的变量都属于全局变量都属于全局命名空间
    • 局部命名空间:在函数中声明的变量会存放在局部命名空间
    • 内置命名空间:python解释器内置的一些变量(如list,tuple,str,int等等)

    命名空间的加载顺序

    • 内置命名空间
    • 全局命名空间
    • 局部命名空间

    取值顺序

    • 局部命名空间
    • 全局命名空间
    • 内置命名空间
    In[2]: a = 10
    In[3]: def func1():
      ...:     a = 20
      ...:     print(a)     # 函数内部有变量a,就优先取局部命名空间的变量
      ...:     
    In[4]: func1()
    20
    In[5]: print(a)
    10
    In[6]: 
    

    作⽤域:

    • 作⽤域就是作⽤范围, 按照⽣效范围来看分为 全局作⽤域和局部作⽤域
    • 全局作⽤域: 包含内置命名空间和全局命名空间. 在整个⽂件的任何位置都可以使⽤(遵循
      从上到下逐⾏执⾏). 局部作⽤域: 在函数内部可以使⽤.
      作⽤域命名空间:
    1. 全局作⽤域: 全局命名空间 + 内置命名空间
    2. 局部作⽤域: 局部命名空间
      我们可以通过globals()函数来查看全局作⽤域中的内容, 也可以通过locals()来查看局部作
      ⽤域中的变量和函数信息
    In[7]: a = 10
    In[8]: def func():
      ...:     a = 40
      ...:     b = 20
      ...:     def abc():
      ...:         print("哈哈")
      ...:     print(a, b) # 这⾥使⽤的是局部作⽤域 40,20
      ...:     print(globals()) # 打印全局作⽤域中的内容
      ...:     print(locals()) # 打印局部作⽤域中的内容
      ...:     
    In[9]: func()
    
    • locals(): 查看当前作用域中的名字
    • globals(): 查看全局作用域中的名字

    3. 关键字global和nonlocal

    首先先介绍一下函数的嵌套:

    # 函数的嵌套,即函数里面定义函数,该函数只能在上层函数中使用
    def fun2():
        print(222)
        def fun3():
            print(666)
        print(444)
        fun3()
        print(888)
    print(33)
    fun2()
    print(555)
    
    # 打印结果:
    # 33
    #222
    # 444
    # 666
    # 888
    # 555
    

    使用global关键字可以在局部作用域中把全局命名空间的变量拿过来用(可以修改),如果指定的变量不存在则创建。

    In[12]: a = 100
    In[13]: def func2():
       ...:     global a        # 此时这个函数中的a已经是全局变量a了
       ...:     a = 78
       ...:     print(a)
       ...:     
    In[15]: func2()
    78
    In[16]: a                   # 此时可以看到,a的值已经变成78了           
    Out[16]: 78
    

    nonlocal关键字表示在局部作用域中,调用父级命名空间中的变量。

    In[19]: a = 3
    In[20]: 
    In[20]: def func3():
       ...:     a = 9
       ...:     def func4():
       ...:         nonlocal a  # 此时使用的就是func3中的a变量
       ...:         a = 23      # 因此func3中的a被修改成了23
       ...:         print(a)
       ...:     func4()
       ...:     print(a)
       ...: func3()
    23              # 函数func4打印的结果
    23              # 函数func3打印的结果
    In[21]: print(a)
    3               # 最后函数结束打印的结果
    
    • global:把全局的内容引入到局部,如果全局命名空间没有这个变量,则创建这个变量而并不会报错
    • nonlocal:在局部, 把上一层的变量引入进内部. 如果上一层没有. 继续上一层;最外层函数中还没有时,会报错(不会再全局命名空间中查找)
  • 相关阅读:
    .NET Core 下使用 Exceptionless 记录日志
    .NET Core 下使用 Kafka
    .NET Core 下使用 RabbitMQ
    .NET Core 下使用 ElasticSearch
    .NET Core 下使用 gRPC
    【手摸手,带你搭建前后端分离商城系统】02 VUE-CLI 脚手架生成基本项目,axios配置请求、解决跨域问题
    【手摸手,带你搭建前后端分离商城系统】01 搭建基本代码框架、生成一个基本API
    【开源】Springboot API 一键生成器
    力扣1. 两数之和
    常用代码优化手段
  • 原文地址:https://www.cnblogs.com/zpzhue1/p/9880416.html
Copyright © 2020-2023  润新知