• day19.抽象


    6.1惰性即美德

      程序应该写得抽象一些。相同的代码要封装在函数中,在需要的时候调用就好。

    6.2抽象和结构

      抽象可以节省很多工作,并且使代码可读。
      操作的具体细节应该写在独立的函数中。

    6.3创建函数

      函数是可以调用的,它执行某种行为并且返回一个值。
      内置函数callable()可以判断一个对象是否可调用。
      创建函数是组织程序,使之抽象的关键。
     
      定义方式:
      def hello(name):
        return 'hello '+name
      调用方式:
      hello('alex')
      结果:
      hello alex
       注:return一个函数结束的标志。return语句是用来从函数中接收返回值的。

    6.3.1记录函数:

      给函数写文档,帮助他人理解。
      可以加入注释或在函数体的开头写上文档字符串。
      访问文档字符串的方法:func.__doc__
      在交互式解释器中使用help函数,可以看到函数及文档字符串的信息

    6.3.2并非真正函数的函数

      数学意义上的函数,总在计算参数后返回一些值。
      python的有些函数却不返回任何东西。
      没有return语句或者虽然有return但后面不跟任何值的的函数不具有返回值。

    6.4参数魔法

      6.4.1值从哪里来

        函数被定以后,所操作的值在被调用时传来。
        在定义函数时,函数名后的的变量通常叫做形式参数。
        在调用函数时,提供的值叫做实际参数。

      6.4.2我能改变参数吗:

        在函数内部为参数赋予新值不会影响函数外的变量。
        字符串(数字,元组)是不可变类型,即无法被修改。(只能用新的值覆盖)
        如果将列表(可变类型的数据)当做参数传入时,在函数内修改列表的元素会影响到外部的变量。
        如果想避免这种情况,可以复制一个列表的副本传入函数。
        这样修改函数内部的参数就不会影响外部变量。

      6.4.3关键字参数和默认值

        关键字参数:明确每个参数的作用,顺序无关,提供默认值
        位置参数和关键字参数混合使用时,位置参数应该放在关键字参数前面。
     

      6.4.4收集参数

        def func(*args):
          print(args)
        参数前的星号*将所有的值放在一个元组中。可以说将这些值收集起来,然后使用。
        这里收集的是剩余的位置参数。
        如果不提供任何供收集的元素,它就是个空元组。
        **的作用是收集剩余的关键字参数,将它们组成一个字典。
     

      6.4.5反转过程

        在调用函数时可以使用*和**解压实参,类似收集参数的逆过程。
        *解压列表
        **解压字典
      6.4.6练习使用参数
     
      6.5作用域
        变量可以看做是值的名字(引用)。
        存放这种引用关系的地方叫做命名空间,或者作用域。
        除了全局作用域,每个函数调用都会创建一个新的作用域。
        函数内的变量称为局部变量。(local variable,这是与全局变量相反的概念)
        参数的工作原理类似于局部变量,所以用全局变量的名字作为参数名并没有问题。
        在局部作用域可以任意读取上层作用域的变量。
     
      屏蔽的问题:
        在函数内读取全局变量并不是问题,但是如果局部作用域内有变量的名字和想要访问的全局变量同名时,就不能直接访问了。全局变量会被局部变量屏蔽。
        如果确定需要的话,可以使用globals函数获取全局变量值,该函数的近亲是vars,它可以返回全局变量的字典。(locals返回局部变量的字典)。
        例如函数和全局里都有一个名叫x的变量,在函数内部可以通过globals()['a']来获取。
     
      重绑全局变量:
        如果在函数内部将值赋予一个变量,它将自动变成局部变量。除非通过global关键字声明它是一个全局变量。
     
      嵌套作用域:
        python的函数是可以嵌套的,也就是可以将一个函数放在另一个里边。
        嵌套一般来说并不是很常用,但它有一个很突出的应用,例如需要用一个函数“创建”另一个。也就意味着可以像下面这样(在其他函数内)书写函数:
        def aa(x):
          def bb(y):
            return x*y
          return bb
      一个函数位于另一个函数里,外层函数返回内层函数,但并没有调用。重要的是返回的函数还可以访问它的定义域。换句话说:它带着它的环境(和相关局部变量)。
     
      每次调用外层函数,它的内部函数都被重新绑定。x变量每次都有一个新的值。由于python的嵌套作用域,来自外部作用域的这个变量,稍后会被内部函数访问,
      类似bb函数存储子封闭作用域的行为叫做闭包(closure)。
     
      外部作用域的变量一般来说是不能进行重新绑定的,但在py3中,nonlocal关键字被引入。它和global关键字的使用方式相仿,可以让用户对外部作用域(但并非全局作用域)的变量进行赋值。
     
     
    6.6递归
      递归:调用自身
      比喻:一个洋葱是一个带着一层洋葱皮的洋葱。
     
      python中默认的最大递归层数是997,超过这个次数程序就会报一个“超过最大递归深度”的错误。
      这类递归叫做无穷递归(infinite recursion),类似于while True开始的无穷循环。
      理论上讲它永远不会结束。
      我们想要的是能做一些有用的事情的递归函数。
      类似无穷循环中需要一个break来跳出循环,递归函数也需要有一个条件来退出递归。
     
      两个经典递归:阶乘和幂
        阶乘:n的阶乘定义为n*(n-1)*(n-2)*...*1
        这时,n==1就是退出阶乘的条件。
        def fact(n):
          if n == 1:
            return 1
          else:
            return n * fact(n-1)
        幂的计算:x的n次幂 == x*x的n-1次幂,直到n==1
        def my_pow(x,n):
          if n == 1:
            return x
          else:
            return x * my_pow(x,n-1)
     
        如果函数很复杂且难懂的话,在实现前用自己的话明确的定义一下是很有帮助的。
        这类使用“准程序语言”编写的程序称为“伪代码”。
     
        在大多数情况下,可以使用循环代替递归,而且效率会更高。
        但是在多数情况下,递归会更加易读---有时会大大提高可读性---尤其当读程序的人懂得递归的时候。
        尽管可以避免编写使用递归的程序,但我们至少应该理解递归算法和他人写的递归程序。
     
        另外一个经典:二元查找(binary search)
        二元查找最普遍的应用就是查找一个数字在一个(排好序的)序列中的位置。
        这个数字是否小于正中间的那个数?
        如果是的话,这个数字是否小于正四分之一位置的那个数?
        然后继续这样问下去,直到找到正确位置。、
     
        这个算法的本身就是递归的定义,亦可用递归实现:
        1.如果上下限相同,那么就是数字所在的位置,返回
        2.否则找到两者的中点,判断数字在左还是在右,继续查找数字所在的部分
     
        这个递归例子的关键就是顺序,当找到中间元素的时候,只需比较它和所查找的数字,如果要查找的数字比较大,那么它一定在右侧,否则在左侧。递归部分就是“继续查找数字所在的那半部分”。
     
        注意该算法返回的是数字所在的位置,如果它不存在与序列中,我们需要提前做出处理。
        标准库中的bisect模块可以非常有效的实现二元查找
     函数到处放:
      到现在为止,函数的使用方法和其他对象(字符串,数字,序列。。。)基本上一样,它们可以分配给变量、作为参数传递以及从其他函数中返回。
      有些函数式编程语言(Scheme,LISP等)中使用函数几乎可以完成所有的事情,尽管在python中不那么倚重函数,但也可以进行函数式程序设计。
      python在面对这类“函数式编程”方面有一些有用的函数。
      map:将序列中的元素全部传递给一个函数
        map(str,range(3)) #将一个序列中的元素转换类型 ==>['0','1','2']
      filter函数可以基于一个返回布尔值的函数对元素进行过滤。
        filter(lambda x:x.isalnum(),['abc','123','ds//']) ==>['abc','123']
        注:lambda可以用作创建短小的函数
      reduce函数会将序列中的前两个元素与给定的函数联合使用,并且将它们的返回值和第三个元素继续联合使用,直到整个序列都处理完毕,并且得到一个最终结果。
        reduce(lambda x,y:x+y,[1,2,3]) ==>6
        当然这里也可以使用内置函数sum
     小结:
      本章介绍了关于抽象的常见知识以及函数的常见应用。
      1.抽象:抽象是隐藏多余细节的艺术。定义处理细节的函数可以让函数更抽象。
      2.函数定义:函数使用def定义。它们是由语句组成的块,可以从“外部世界”获取值(参数),也可以返回一个或多个值作为运算的结果。
      3.参数:函数从参数中得到需要的信息---也就是函数调用时设定的变量。python中有两类参数:位置参数和关键字参数。参数在给定默认值时时可选的。
      4.作用域:变量存储在作用域(也叫作命名空间)中。Python中有两类主要的作用域:全局作用域和局部作用域。作用域可以嵌套
      5.递归:函数可以调用自身---如果它这么做了就叫递归。一切用递归实现的功能都可以用循环代替,但是递归函数更易读。
      6.函数式编程:Python有一些进行函数式编程的机制。包括lambda表达式以及map,filter和reduce函数。

     本章新函数:
      map(func,seq[,seq[,seq....]]):对序列中的每个元素应用函数
      filter(func,seq):根据函数过滤序列中的元素
      reduce(func,seq[,initial]):将序列中的元素两两进行运算,最后返回一个结果
      sum(seq):返回序列中所有元素的和
      apply(func[,args[,kwargs]]):调用函数,可以提供参数
  • 相关阅读:
    centos 远程授权
    jar 包启动脚本
    Java 优化要点
    Java 性能优化
    maven快速自动更新子模块项目的版本号
    spring boot Tomcat文件上传找不到零时文件夹
    redis 集群常用命令
    java基础知识点学习
    Markdown 语法
    elasticsearch
  • 原文地址:https://www.cnblogs.com/maxiaotiaoshishui/p/7297423.html
Copyright © 2020-2023  润新知