Python 部分知识点总结(四)

此篇博客只是记录第六周未掌握或不熟悉的知识点,用来加深印象。

一、匿名函数

  1. 匿名函数,即没有名字的函数,在高阶函数传参时,使用 lambda 表达式,往往能简化代码
  2. 格式:lambda 参数列表 : 表达式
          lambda x : x **2
          foo = lambda x,y : (x+y)**2     foo(2,1)  –> 9
  3. 使用 lambda 关键字来定义匿名函数
          参数列表不需要小括号
          冒号是用来分割参数列表和表达式的
          不需要使用 return,表达式的值,就是匿名函数返回值
          lambda 表达式(匿名函数)只能写在一行上,被称为单行函数
  4. >>> (lambda x,y=3:x+y)(5,y=44)       –>   49
          >>> (lambda x,*,y=30:x+y)(5,y=9)    –>   14
          >>> print((lambda *args: [x+2 for x in args])(*range(5)))    –>  [2, 3, 4, 5, 6]
          >>> [x for x in (lambda *args:map(lambda x:x+1,args))(*range(5))]    –>  [1, 2, 3, 4, 5]
          >>> [x for x in (lambda *args:map(lambda x:(x+1,args),args))(*range(5))]
              –>    [(1, (0, 1, 2, 3, 4)), (2, (0, 1, 2, 3, 4)), (3, (0, 1, 2, 3, 4)), (4, (0, 1, 2, 3, 4)), (5, (0, 1, 2, 3, 4))]

二、递归

  1. 函数直接或者间接调用自身就是递归
         递归需要有边界条件、递归前进段、递归返回段
         递归一定要有边界条件
         当边界条件不满足的时候,递归前进
         当边界条件满足的时候,递归返回
  2. 递归调用的深度不宜过深
             Python 对递归调用的深度做了限制,以保护解释器
             超过递归深度限制,抛出 RecursionError: maxinum recursic depth exceeded 超出最大深度
             查看递归深度
             import sys
             def fool(b,b1=3):
                   fool(b)
             print(sys.getrecursionlimit())
             sys.setrecursionlimit(2000)    把递归深度改为 2000
  3. 递归的性能
             循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
             递归还有深度限制,如果递归复杂,函数反复压栈,栈内存很快就溢出了
  4. 递归总结
             递归是一种很自然的表达,符合逻辑思维
             递归相对运行效率低,每一次调用函数都要开辟栈帧
             递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了
             如果是有限次数的递归,可以使用递归调用,或者使用循环代替,循环代码稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果
             绝大多数递归,都可以使用循环实现
             即使递归代码很简洁,但是能不用则不用递归

三、返回值和作用域

  1. Python 函数使用 return 语句返回 “返回值”
         所有函数都使用返回值,如果没有 return 语句,隐式调用 return None
         return 语句并不一定是函数的语句块的最后一条语句
         一个函数可以存在多个 return 语句,但是只有一条可以被执行,如果没有一条 return 语句被执行到,隐式调用 return None
         如果有必要,可以显示调用 return None,可以简写为 return
         如果函数执行了 return 语句,函数就会返回,当前被执行的 return 语句之后的其它语句就不会被执行了
          作用:结束函数调用、返回值
  2. 函数不能同时返回多个值
          return 1,3,5 看似返回多个值,隐式的被 Python 封装成了一个元祖,然后顺便可以用解构提取更方便
  3. 函数有可见范围,这就是 作用域 的概念
          内部函数不能在外部直接使用,会抛出 NameError 异常,因为它不可见
  4. 一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域
  5. 全局作用域
                在整个程序运行环境中都可见
          局部作用域
                在函数、类等内部可见
                局部变量使用范围不能超过其所在的局部作用域
  6. 因为函数的语句块中没有类似于 x =  的语句,所以 y = x + 1 中的 x 会从上一级找,从而不报错
    x = 5
    def fn():
        y = x + 1
        print(x)
    fn()
    因为函数会先扫描语句块,且函数的语句块中有 x = 的语句,所以会先调用本地的变量,但是本地并没有赋值,所以就会报错,记住一句话:赋值即定义
    x = 5
    def fn():
        y = x + 1
        x += 1
        print(x)
    fn()
  7. 使用 global 关键字的变量,将 fn 内的 x 声明为使用外部的全局作用域中定义的 x,且全局作用域中必须有 x 的定义
    x = 5
    def fn():
        global x
        x += 1
        print(x)
    fn()
  8. global 总结
         x += 1 这种是特殊形式产生的错误原因是:先引用后赋值,而 Python 动态语言是赋值才算定义,才能被引用,解决办法是在这条语句前增加 x = 0 之类的赋值语句,或者使用 global 告诉内部作用域,去全局作用域查找变量定义
         内部作用域使用 x = 5 之类的赋值语句会重新定义局部作用域使用的变量 x,但是,一旦这个作用域中使用 global 声明 x 为全局的,那么 x = 5 相当于在为全局作用域的变量 x 赋值
  9. global 使用原则
          外部作用域变量会在内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离
          如果函数需要使用外部全局变量,请使用函数的形参传参解决
          一句话:不用 global,学习它就是为了深入理解变量作用域
  10. 使用了 nonlocal 关键字,将变量标记为不在本地作用域中定义,而在上级的某一级局部作用域中定义,但不能是全局作用域中定义,这样就形成闭包
    def counter():
        cc = 0
        def inc():
            nonlocal cc
            cc += 1
            return cc
        return inc
    foo = counter()
    foo()
  11. 默认值的作用域
    def foo(xyz=[]):
        xyz.append(1)
        print(xyz)
    foo()   –> [1]
    foo()   –> [1,1]
    因为函数也是对象,Python 把函数的默认值放在了属性中,这个属性就伴随这个函数对象的整个生命周期
    函数地址并没有变,就是说函数这个对象的属性没有变,调用它,它的属性  __defaults__ 中所使用元祖保存默认值
    为了避免出现这种情况:
          使用影子拷贝创建一个新的对象
    def foo(xyz=[]):
        xyz = xyz[:]
        xyz.append(1)
        print(xyz)
    foo()                                     –>[1]
    print(foo.__defaults__)         –>([],)
    foo()                                     –>[1]
    print(foo.__defaults__)         –>([],)
    foo([10])                               –>[10,1]
    print(foo.__defaults__)          –>([],)
          通过值得判断就可以灵活的选择创建或者修改传入对象
    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])                               –>[10,1]
    print(foo.__defaults__)          –>(None,’abc’,123)
  12. 属性 __defaults__ 中使用元祖保存所有位置参数默认值
           属性 __kwdefaults__ 中使用字典保存所有 keyword-only 参数的默认值
    def foo(w,u=’abc’,*,z=123,zz=[456]):
        u = ‘xyz’
        z = 789
        zz.append(1)
        print(w,u,z,zz)
    print(foo.__defaults__)
    foo(‘magedu’)
    print(foo.__kwdefaults__)
  13. 一个变量的查找顺序是 LEGB:Local(本地作用域)–> Enclosing(嵌套构成的闭包)–>Global(全局作用域) –>Build-in(内置模块的命名空间)
  14. 全局函数销毁
                 重新定义同名函数
                 del 语句删除函数对象
                 程序结束时
          局部函数销毁
                 重新在上级作用域定义同名函数
                 del 语句删除函数对象
                 上级作用域销毁时

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/95946

联系我们

400-080-6560

在线咨询

工作时间:周一至周五,9:30-18:30,节假日同时也值班

QR code