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]]):调用函数,可以提供参数
原文地址:https://www.cnblogs.com/maxiaotiaoshishui/p/7297423.html