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]]):调用函数,可以提供参数

时间: 2024-10-03 00:53:27

day19.抽象的相关文章

day19<异常&File类>

异常(异常的概述和分类) 异常(JVM默认是如何处理异常的) 异常(try...catch的方式处理异常1) 异常(try...catch的方式处理异常2) 异常(编译期异常和运行期异常的区别) 异常(Throwable的几个常见方法) 异常(throws的方式处理异常) 异常(throw的概述以及和throws的区别) 异常(finally关键字的特点及作用) 异常(finally关键字的面试题) 异常(自定义异常概述和基本使用) 异常(异常的注意事项及如何使用异常处理) 异常(练习) Fil

抽象工厂模式

思考:工厂方法模式:http://www.cnblogs.com/maggiejyt/p/7561253.html 工厂方法模式UML: 问题:如果这家工厂不止要生产Ball(球),还要还有Sneakers(球鞋)等 则UML图为 当Product有多种类时则是抽象工厂模式 代码(Ball的代码见简单工厂模式:http://www.cnblogs.com/maggiejyt/p/7561253.html) Sneakers(球鞋抽象类) package com.maggie.FactoryMet

快学Scala 第二十一课 (初始化trait的抽象字段)

初始化trait的抽象字段: trait Logged { println("Logged constructor") def log(msg: String){ println("Logged")} } trait FileLogger extends Logged { var filename: String override def log(msg: String) { println("filename:" + filename) } }

【Python&数据结构】 抽象数据类型 Python类机制和异常

这篇是<数据结构与算法Python语言描述>的笔记,但是大头在Python类机制和面向对象编程的说明上面.我也不知道该放什么分类了..总之之前也没怎么认真接触过基于类而不是独立函数的Python编程,借着本次机会仔细学习一下. 抽象数据类型 最开始的计算机语言,关注的都是如何更加有效率地计算,可以说其目的是计算层面的抽象.然而随着这个行业的不断发展,计算机不仅仅用于计算,开发也不仅只关注计算过程了,数据层面的抽象也变得同样重要.虽然计算机语言一开始就有对数据的抽象,但是那些都只是对一些最基本的

Java设计模式—工厂方法模式&amp;抽象工厂模式

工厂方法模式与抽象工厂模式都是设计模式中重要而且常见的模式.       工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 通用类图如下: 在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义:Creator为抽象创建 类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的. 工厂方法模式的扩展方式有很多种,下边是工厂方法模式一个比较实用的源代码: 抽象产品类: pub

大话设计模式:抽象工厂模式

抽象方法模式:提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们具体的类. 三种模式的对比:   简单工厂模式 工厂模式 抽象工厂模式 产品 可以有多个但是都属于同一类, 同一等级.都继承产品抽象类. 可以有多个但是都属于同一类,同一等级. 都继承产品抽象类. 可以有不同种类的产品,每类有多中 具体产品: 抽象产品 只能有一个 只能有一个; 多个抽象产品类:每个抽象产品类可 以派生多种具体产品: 抽象工厂类   只能有一个,可以派生出多个具体工厂类: 只有一个,可派生出多个具体工厂类:

2.35 Java基础总结①抽象②接口③设计抽象类和接口的原则④接口和抽象类的区别

java基础总结①抽象②接口③设计抽象类和接口的原则④接口和抽象类的区别 一.抽象 abstract作用:不能产生对象,充当父类,强制子类正确实现重写方法和类相比仅有的改变是不能产生对象,其他的都有,包括构造.属性等等任何一个类只要有一个抽象的方法就成了抽象类 抽象方法 public abstract A();①方法是抽象的,这个类也是抽象的:②子类必须重写抽象方法,除非子类也是抽象类 抽象类可以没有抽象方法,但一般不这么设计 二.接口 interface 接口也是Java的一种引用数据类型(J

【设计模式】1、抽象工厂模式

对于这个抽象工厂的相应的代码实现 /** * 功能:抽象工厂模式的作用 适用性 1.一个系统要独立于它的产品的创建.组合和表示时. 2.一个系统要由多个产品系列中的一个来配置时. 3.当你要强调一系列相关的产品对象的设计以便进行联合使用时. 4.当你提供一个产品类库,而只想显示它们的接口而不是实现时. * 时间:2016年2月11日22:18 * 作者:cutter_point */ package com.shejimoshi.create.AbstractFactory; public in

iOS设计模式——抽象工厂

何为抽象工厂? 抽象工厂提供一个固定的接口,用于创建一系列有关联或相依存的对象,而不必指定其具体类或其创建的细节.客户端与从工厂得到的具体对象之间没有耦合. 抽象工厂与工厂方法模式的区别 抽象工厂与工厂方法模式在许多方面有很多相似之处,以至于我们常常搞不清楚应该在什么时候用哪一个.两个模式都用于相同的目的:创建对象而不让客户端知晓返回了什么确切的具体对象. 抽象工厂:@.通过对象组合创建抽象产品.@.创建多系列产品.@.必须修改父类的接口才能支持新的产品. 工厂方法:@.通过类继承创建抽象产品.