python函数的进阶

python学习网 2019-05-11 16:06:09

[函数的进阶]

1.内容大纲

  1. 形参角度:
    • 万能参数。
    • *的魔性用法。
    • 仅限关键字参数(了解)。
    • 形参的最终顺序。
  2. 名称空间。
    1. 全局名称空间,局部名称空间
    2. 加载顺序,取值顺序。
    3. 作用域。
  3. 函数的嵌套(高阶函数)。
  4. 内置函数 globals() locals()
  5. 关键字:nonlocal global。

2.详细内容

  1. 形参角度:

    • 万能参数
         如果我们在传参数时不很清楚有哪些,或者说给一个函数传了很多实参,考虑用动态参数也叫万能参数。
         万能参数,即动态参数,分为两种:动态接收位置参数 *args,动态接收关键字参数**kwargs.
      
      #动态接收位置参数 *args
      *【动态接收位置参数 *args:*args,约定俗成在*后使用args,PEP8规范中规定就使用args  (这里起到魔法效果的是 * 而不是args)】
      *【函数定义时,*代表聚合,它将所有的位置参数聚合成一个元组,赋值给了args,*args可以接受所有的位置参数。】
      *【总结:*args只可以接受多个位置参数,并且返回一个元组。不能接受关键字参数。】
      
       def eat(a,b,c,d):
           print('我请你吃:%s,%s,%s,%s'%(a,b,c,d))
       eat('蒸羊羔','蒸熊掌','蒸鹿邑','烧花鸭')#我请你吃:蒸羊羔,蒸熊掌,蒸鹿邑,烧花鸭
      
       def eat(*args):
         print('我请你吃:',args)  #返回一个元组
         print('我请你吃:%s,%s,%s,%s'%args)
       eat('蒸羊羔儿','蒸熊掌','蒸鹿尾儿','烧花鸭')##只能接受位置参数,不能接受关键字参数
       #结果:
        我请你吃: ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭')
        我请你吃:蒸羊羔儿,蒸熊掌,蒸鹿尾儿,烧花鸭
      
      #练习:传入函数中数量不定的int型数据,函数计算所有数的和并返回。
       def my_sum(*args):
           sum = 0
           print(*args)#1 2 3 4 5
           print(args)#(1, 2, 3, 4, 5)
           for i in args:  #计数思想
               sum += i
           return sum
       print(my_sum(1,2,3,4,5))#15
      
      #动态接收关键字参数: **kwargs
      *【动态接收关键字参数: **kwargs  ,**kwargs接受所有的关键字参数,然后将其转换成一个字典赋值给kwargs这个形参。】
      *【函数定义时:**将所有的关键字参数聚合到一个字典,将这个字典赋值给了kwargs】
      *【总结:**kwargs只能接受多个关键字参数,然后将其转换成一个字典。不能接受位置参数。】
       def fuc(**kwargs):
           print(kwargs)
       fuc(name = '太白',age = 18,sex = '男')#只能接受关键字参数,不能接受位置参数
       #{'name': '太白', 'age': 18, 'sex': '男'}
      
      
      #动态参数的完整写法:万能参数:*args, **kwargs,
      *【如果一个参数设置了动态参数,那么它可以接受所有的位置参数,以及关键字参数,这样就会大大提升函数拓展性,针对于实参参数较多的情况下,解决了一一对应的麻烦。】
       def fuc(*args,**kwargs):
           print(args)
           print(kwargs)
       fuc('蒸羊羔儿','蒸熊掌','蒸鹿尾儿','烧花鸭','烧雏鸡','烧子鹅',name = '太白',age = 18,sex = '男')
      #结果:
      ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅')
      {'name': '太白', 'age': 18, 'sex': '男'}
      
    • *的魔性用法。
      *的魔性用法:函数中分为打散和聚合。函数外可以处理剩余的元素。
      
      ###打散和聚合:
      
      1.聚合:在函数定义时,*代表聚合 。在args前面加一个* ,那么args可以接受多个位置参数,并且返回一个元组.(**kwargs也是同理将多个关键字参数转化成一个字典返回),所以在函数的定义时: *起到的是聚合的作用。
      
      2.打散:在函数调用时,*代表打散:在实参角度的位置参数-->(可迭代对象:字符串,列表。元组)前面加*,相当于把这些实参拆解成的一个个的组成元素当作位置参数,然后传给args 。
             在实参角度的位置参数-->字典前面加**
      
          # 在函数的调用时,*代表打散。
          def func(*args,**kwargs):
              print(args) # (1,2,3,22,33)
              print(kwargs)
          func(*[1,2,3],*[22,33])
          # (1, 2, 3, 22, 33)
          # {}
      
          func(*'fjskdfsa',*'fkjdsal')
          # ('f', 'j', 's', 'k', 'd', 'f', 's', 'a', 'f', 'k', 'j', 'd', 's', 'a', 'l')
          # {}
      
          func(**{'name': '太白'},**{'age': 18})
          # ()
          # {'name': '太白', 'age': 18}
      
          ##1:
          针对*args,在实参角度的位置参数--可迭代对象(字符串,列表,元组)前面加*,相当于把这些实参拆解成的一个个的组成元素当作位置参数,然后传给args
          '''你如何将三个数据(这三个数据都是可迭代对象类型)s1 = 'alex',l1 = [1, 2, 3, 4],tu1 = ('武sir', '太白', '女神',)的每一元素传给动态参数*args?'''
           s1 = 'alex'
           l1 = [1, 2, 3, 4]
           tu1 = ('武sir', '太白', '女神',)
           def fuc(*args):
               print(args)
           fuc(s1,l1,tu1)#('alex', [1, 2, 3, 4], ('武sir', '太白', '女神'))
           fuc(*s1,*l1,*tu1)#('a', 'l', 'e', 'x', 1, 2, 3, 4, '武sir', '太白', '女神')
      
          ##2:
          针对**kwargs,在实参角度的位置参数---字典前面加**,将字典变为关键字变量,再由**kwargs变为字典
           ##
           def func(**kwargs):
               print(kwargs)
           dic1 = {'name': '太白', 'age': 18}
           dic2 = {'hobby': '喝茶', 'sex': '男'}
           func(**dic1,**dic2)
          #结果:
          {'name': '太白', 'age': 18, 'hobby': '喝茶', 'sex': '男'}
      
          ##
           def func(*args,**kwargs):
               print(args)
               print(kwargs)
           dic1 = {'name': '太白', 'age': 18}
           dic2 = {'hobby': '喝茶', 'sex': '男'}
           func(**dic1,**dic2)
          #结果:
           ()
           {'name': '太白', 'age': 18, 'hobby': '喝茶', 'sex': '男'}
      
          func(dic1,dic2)
          #结果:
           ({'name': '太白', 'age': 18}, {'hobby': '喝茶', 'sex': '男'})
           {}
      
      3.函数外处理剩余的元素。
           a,b = (1,2)
           print(a,b)#1 2
      
           a,*b = [1,2,3,4]
           print(a,b)#1 [2, 3, 4]
      
           *rest,a,b =range(5)
           print(rest,a,b)#[0, 1, 2] 3 4
      
           print([1, 2, *[3, 4, 5]])#[1, 2, 3, 4, 5]
      
    • 仅限关键字参数(了解)
      *【形参角度第四个参数:仅限关键字参数,形参的仅限关键字参数只接受实参的关键字传的参数】
      *【仅限关键字参数是python3x更新的新特性,他的位置要放在*args后面,**kwargs前面,类似于位置参数,它与默认参数的前后顺序无所谓,它只接受关键字传的参数】
      
      def func(a,b,*args,sex= '男',c,**kwargs,):    #与默认参数的前后顺序无所谓,他的位置要放在*args后面,**kwargs前面
          print(a,b)
          print(args)  #元组
          print(sex)
          print(c)    #形参的仅限关键字参数,只能接受实参的关键字参数
          print(kwargs)
      func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80,c='666')
      #结果:
       1 2
       (3, 4, 5, 6, 7)
       女
       666
       {'name': 'Alex', 'age': 80}
      
      
       def func(a,b,*args,d,sex = '男',**kwargs):  #与默认参数的前后顺序无所谓,他的位置要放在*args后面,**kwargs前面
           print(a,b)
           print(args)
           print(sex)
           print(d)
           print(kwargs)
       func(1,2,3,4,5,d=1,sex='女',name='alex',age=80)
      
    • 形参角度的参数的顺序。
      形参角度最终的顺序为:
          位置参数,*args,仅限关键字参数,默认参数,**kwargs 
      或: 位置参数,*args,默认参数,仅限关键字参数,**kwargs
      
      #
        位置参数必须在前面,即 :位置参数,默认参数
      
      # *args 的位置?
        【动态参数*args肯定不能放在位置参数前面,这样我的位置参数的参数就接收不到具体的实参了。】
        【因为*args全部接收完了,所以动态参数必须在位置参数后面。】
        【*args一定要在位置参数与默认值参数中间:位置参数,*args,默认参数】
      
          #错:
           def func(*args,a,b,sex= '男'):
               print(a,b)
           func(1,2,3,4)
      
          # 错:args得到实参的前提,sex必须被覆盖了。
       def func(a,b,sex= '男',*args,):
           print(a,b)
           print(sex)
           print(args)
       func(1,2,3,4,5,6,7,)
      
         #对:
       def func(a, b, *args, sex='男'):
          print(a, b)
          print(args)
          print(sex)
      func(1, 2, 3, 4, 5, 6, 7, sex='女')
          #结果:
           1 2
           (3, 4, 5, 6, 7)
           女
      
      # **kwargs 位置?
      def func(a,b,*args,sex= '男',**kwargs,):
          print(a,b)
          print(sex)
          print(args)
          print(kwargs)
      func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80)
      # 1 2
      # 女
      # (3, 4, 5, 6, 7)
      # {'name': 'Alex', 'age': 80}
      
      

    2.名称空间。

    1. 全局名称空间,局部名称空间

      存放名字与值的关系’的空间------>命名空间

      全局命名空间:代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;(py文件中,存放变量名与值的关系的一个空间叫做全局名称空间)

      局部名称空间:当执行一个函数时,内存中会临时开辟一个空间,临时存放函数中的变量与值的关系,这个叫做临时名称空间【随着函数的执行的开始而创建,随着函数执行的结束而消失】

      内置名称空间:内置名称空间存放的就是python源码给你提供的一些内置函数等拿来即用的特殊的变量:input,print,list等。

      ​ 在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空。

      #名称空间;命名空间。
      
       a = 1
       b = 2
       def func():
           f = 5
           print(f)
       c = 3
       func()
      
       python分为三个空间:
          # 内置名称空间(builtins.py)
          # 全局名称空间(当前py文件)
          # 局部名称空间(函数,函数执行时才开辟)
    2.加载顺序,取值顺序。
    # 加载顺序:
     内置名称空间 ---> 全局名称空间  ----> 局部名称空间(函数执行时)
        在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用的比如abs(-1),max(1,3)等等,在启动Python解释器的时候,就已经导入到内存当中供我们使用,所以肯定是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载。
    # 取值顺序(就近原则) 单向不可逆  LEGB原则
    (从局部找时)局部名称空间  ---> 全局名称空间  --->  内置名称名称空间
    
    取值顺序就是引用一个变量,先从哪一个空间开始引用。这个有一个关键点:从哪个空间开始引用这个变量。
    # 如果你在全局名称空间引用一个变量,先从全局名称空间引用,全局名称空间如果没有,才会向内置名称空间引用。
    # 如果你在局部名称空间引用一个变量,先从局部名称空间引用。
    # 局部名称空间如果没有,才会向全局名称空间引用,全局名称空间在没有,就会向内置名称空间引用。
    所以空间的取值顺序与加载顺序是相反的,取值顺序满足的就近原则,从小范围到大范围一层一层的逐步引用。
    
        input = '太白金星'
        def func():
            input = 'alex'
            print(input) 
        func()
        #alex
    
    
        input = '太白金星'
        def func():
            input = 'alex'
        func()
        print(input)#太白金星
    3.作用域。
    # 两个作用域:
               1.全局作用域 :内置名称空间 全局名称空间。在整个文件的任何位置都可以使用(遵循 从上到下逐⾏执行).
               2.局部作用域:局部名称空间。在函数内部可以使用.
            #  全局作用域只能引用全局作用域的变量。全局变量要放在py文件的开头。
            #  局部作用域可以引用全局作用域的变量, 局部作用域不能改变全局变量。
                date = '周五'
                def func():
                    a = 666
                    print(date)#周五
                    print(a)#666
                func()
                print(a)#NameError: name 'a' is not defined
    
            # 局部作用域不能改变全局变量。
            局部作用域不能改变全局作用域的变量,当python解释器读取到局部作用域时,发现了你对一个变量进行修改的操作,
            解释器会认为你在局部已经定义过这个局部变量了,他就从局部找这个局部变量,报错了。
              #错【面试必考】
              count = 1
              def func():
                  count += 2
                  print(count)
              func()  # UnboundLocalError: local variable 'count' referenced before assignment    变量“count” 在创建前引用
    
    
            # 局部作用域可以引用父级作用域的变量,但是不能改变父级作用域的变量。
             def func():
                 count = 1
                 def inner():
                     print(count)  #1
                 inner()
             func()
    
            #错【面试必考】
            def func():
                count = 1
                def inner():
                    count += 1
                    print(count)
                inner()
            func()
            #UnboundLocalError: local variable 'count' referenced before assignment
    

    3.函数的嵌套(高阶函数)

    # 例1:
    def func1():
        print('in func1')
        print(3)
    def func2():
        print('in func2')
        print(4)
    func1()
    print(1)
    func2()
    print(2)
    # in func1  3  1   in func2 4  2
    
    
    
    # 例2:
    def func1():
        print('in func1')
        print(3)
    def func2():
        print('in func2')
        func1()
        print(4)
    print(1)
    func2()
    print(2)
    #1  in func2  in func1  3  4  2 
    
    
    # 例3:
    def fun2():
        print(2)
    
        def fun3():
            print(6)
        print(4)
        fun3()
        print(8)
    print(3)
    fun2()
    print(5)
    #3  2  4  6  8  5
    
    
    # glbals() locals()
    这两个内置函数可以反映作用域的内容,有助于我们理解作用域的范围。
    globals(): 以字典的形式返回全局作用域所有的变量对应关系。
    locals(): 以字典的形式返回当前作用域的变量的对应关系。
    

    4.内置函数 globals() locals()

    a = 1
    b = 2
    def func():
        name = 'alex'
        age = 73
        print(globals())  # 返回的是字典:字典里面的键值对:全局作用域的所有内容。
        print(locals())   # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。
    print(globals())      # 返回的是字典:字典里面的键值对:全局作用域的所有内容。
    print(locals())       # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。
    func()

    5.关键字:nonlocal global。

3.总结[全部是重点]

  1. 参数:万能参数,仅限关键字参数(了解),参数的顺序,*的魔性用法:聚合,打散。
  2. 名称空间,作用域,取值顺序,加载顺序。
  3. globals locals
  4. 高阶函数:执行顺序。
阅读(2121) 评论(0)