第四章 Python进阶

判断与循环

  判断语言  if/elif/else

  if语句:python中使用关键字if实现判断

  (1)if<condition>:(冒号表示判断的开始)

  (2)<statement>

  组成结构:

  A:if关键字,表示判断语句

  B:<condition>表示判断的条件,条件为真时,执行<statement>中的代码,条件不满足,<statement>中的代码不会执行

  C:冒号表示判断代码块的开始

  D:<statement>表示条件满足时,执行的代码块

  条件满足时:

  

  

  条件不满足时:

  

  没有输出结果:

  

  判断语句执行结束后,之后的代码需要回到判断语句开始时的缩进状态才能继续执行:

  

  

elif和else语句

  语法:

  一个完整的if结构:

  if  <condition 1>:

      <statements>

  elif<condition 2>:

    <syayements>

  elif.........

  elif<condition N>:<statements>

  else:<statements>

  说明:elif是else if 的缩写

  执行过程:

  条件<condition 1>满足,执行if后的代码块,跳过elif和else的部分

  条件<condition 1>不满足,跳过if后的代码块,转到第二个elif语句看条件<statements>是否满足,若满足,执行代码块,否则跳到下一个elif

  如果所有的if及elif都不满足,执行else代码块

  案例:

  

  

  Elif语句个数的限制:

  Elif语句个数没有限制,可以没有,可以有1个,也可以有多个

  不使用elif语句:

  

  使用多个elif:

  

  

  else语句的个数限制:最多一个,可以没有,若出现,放在if和elif的后面

  注意:python中没有switch语句,可使用if—elif—else替代

判断条件

  范围:布尔型、数字、字符串、列表、元组、字典等结构

  在python中,大部分的值都被当作真,除了以下几种情况:
  A.False,包括所有计算结果为False的表达式:
  B.None,包括所有结果为None的表达式:
  C.整数0值,包括所有计算结果为0的表达式:
  D.空字符串、空列表、空字典、空集合、空元组:
  浮点数0.0值也被当作False:
    
  浮点数的精度问题对其作为判断条件的影响:
  不推荐使用浮点数作为判断条件,存在精度问题
  

  出现问题,输出错误

  

  使用and、or、not组合复杂的判断条件

  复杂的判断条件使用and、or、not组合,对应与、或、非操作:

  组合的对象是布尔类型的值:

  

  输出结果:

  

  组合的对象不是布尔型的值:

  

  and:如果两个值都是真,返回第二个值;如果至少一个为假,返回第一个为假的值

  

  or:如果两个值都为假,返回第二个值;如果至少有一个值为真,返回第一个为真的值

  

判断的简单示例

  计算闰年:

  能被四整除不能被一百整除;

  能被四百整除;

  第一个判断条件:

  X % 4 == 0 and X % 100 !=0

  第二个判断条件:

  X % 400 == 0

  

  也可以使用or合并条件:

  

if语句的特殊结构

  语法:

  <value1>if<condition>else<value2>

  说明:

  当条件condition满足时,表达式的值为<value1>,否则为<value2>

  案例:

  

循环:

  循环的作用:将一段代码重复执行多次。

  两种结构:while循环、for循环

while语句

  while语句作用:能够在条件为真的前提下重复执行某块语句,可以拥有else子句作为可选选项。

  基本形式:

  while<condition>:

    <statements>

  执行过程:python执行statements代码块,直到条件condition不满足。

  案例:使用while循环计算1-999的和

  

  

  案例2:改造猜数字游戏,在循环中,猜中时将标志变量置为False,退出循环。

  

  

  注意:else代码块在while循环条件变为False时执行,如果while循环中存在一个else代码块,它将总被执行,除非使用break中断循环

for...in语句

  结构形式:

  for<variable>in<sequence>:

    <statements>

  for循环与while循环在执行过程的区别:

  for循环是遍历一个序列<sequence>,每次将遍历得到的值放入变量<variable>中,while循环则是执行到条件不满足为止。

  <sequence>包括:有序序列、无序序列

  案例:

  列表(有序序列)的遍历:

  

  

  案列:用for循环实现1-999的和

  

  

range()的使用:

  range()函数可生成一个连续整数列表,用法如下:

  A.range(stop):生成0到stop-1的整数组成的列表;

  B.range(start,stop):生成从start到stop-1的整数列表;

  C.range(start,stop,step):生成从start到stop-1,间隔为step的整数列表

  案例:一个参数stop

  

  

  案例:两个参数start,stop

  

  

  案例:三个参数start,stop,step

  

  

  for循环中也可拥有else子句,当循环包含else时,总是在for循环结束之后开始执行,除非遇到break语             句。

  

  

不同序列的for循环遍历:

  有序序列:包括字符串、列表、元组,都是按顺序从第一个元素进行遍历

  如元组的遍历:

  

  

  无序序列:包括集合、字典等,按照某种设定的内部顺序进行for循环遍历,这种顺序不一定是有序的,           如集合:

  

  

  字典的for循环会按照键的某种顺序遍历:

  

  

break 和 continue的运用:

  共同特点:执行特定功能的关键字,通常与判断语句一起使用,处理循环特殊情况。

  break语句:

  作用:可以中断(break)循环语句,即使循环条件没有变更为False,或队列没有完全迭代完毕。

  需要特别注意:如果中断for或者while循环,任何相应循环中的else块都不会执行。

  break同样适用于for循环。

  

  

  continue语句

  作用:告诉程序执行跳过当前循环块中的剩余语句,并继续该循环的下一次迭代。

  

  

  注意:continue语句同样适用于for循环。

迭代器与生成器

  迭代器:可以遍历一个容器(特别是列表)的对象。

  可迭代对象:定义了返回迭代器的__iter__方法,或者定义了支持下标索引的__getitem__方法。简单说,         就是能提供迭代器的任意对象。

  迭代器规则:

  python中的容器类型包含一个迭代器帮助他们支持for循环的操作,这些容器类型需要一个__iter__()方法                                               返回相应的迭代器。

  <container>.__iter__()

  说明:__iter__()方法返回一个特殊的迭代器对象,它实现了__next__()方法并通过stopiteration异常标示                       迭代的完成。

  __next__()方法会返回下一个迭代器对象。

  迭代是访问集合元素的一种方式,常见的容器类型如列表、集合、字典、元组等,都有一个迭代器对象

  列表的迭代器:

  

  集合的迭代器:

  

  字典的迭代器:

  

  元组的迭代器:

  

  迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能向前不能后退

  __next__()方法,该方法返回容器中被迭代到的下一个元素。

  

  迭代器是"一种一次性消费品",迭代器迭代完最后一个元素后,调用.next()方法不会回到开头,而是报一个Stopiteration异常:

    

  For循环正好利用了迭代器的这种性质。当我们对一个容器类型进行循环时,python首先使用他的  __iter__()方法得到它的迭代器,然后不断调用迭代器的__next__()方法,在抛出StopIteration异常后停止循环。

  迭代器对象本身也有一个__iter__()方法,这个方法必须返回迭代器本身:

  

  有些函数返回的结果是迭代器对象:

    

 

 

 

自定义迭代器:(暂时不讲,学完面向对象再讲)

 

 

生成器

  A.使用函数的形式定义,使用yield关键字返回值。

  B.生成器也是一种迭代器,只能迭代一次,因为只在运行时生成值。

  C.大多时候使用函数实现生成器,但并不返回一个值,而是生成(yield)一个值。

  D.python3的许多标准库函数都会返回生成器,因为他占用更少资源

  

  

  生成器的最佳应用场景:

  不想同一时间将在所有计算的大量结果分配到内存,特别是结果及包括循环:

  

  

python内置函数 next():

  允许我们获取序列的下一个元素,调用一次,迭代一次。

  

  

  在元素迭代完之后,再次调用next()方法:

  

  说明:yield完所有的值之后,next()方法触发stopiteration异常

  为什么for循环没有出现该异常呢?

  因为for循环会自动捕捉到这个异常并停止调用next()方法

python中支持迭代的内置数据类型:

  字符串的迭代器:

  

  

  说明:__iter__()方法表示该字符串是一个可迭代对象,但是调用next()方法出现异常,提示str不是一个迭代器,说明它支持迭代,但不能直接进行迭代操作。

  如何对字符串实施迭代?

  iter():内置函数,可以根据一个可迭代对象返回一个迭代器对现象。

  

  

生成器总结:

  使用生成器或迭代器,不需要一次保存序列的所有值,只在需要的时候计算序列的下一个值,从而减少程序使用的内存空间。需要强调说明的是:

  A.生成器都是迭代器,但是迭代器不一定是生成器;

  B.字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代对象;

  C.如果可以调用__iter__()方法,都是可迭代对象,但是一般使用iter()函数;

  D.满足迭代器的两个条件:1.有__iter__() 2.有__next__();

  E.isinstance()类型判断,isinstance(a,iterator)判断a是否是可迭代对象;

推导式

  列表推导式:

  

  

  python提供了列表推导式,简化了列表的创建:

  

  字典推导式:

  案例:将不区分大小写的字母的值进行合并。

    

  

  案例:转换key和value的位置。

  

   

  集合推导式:

  与列表推导式相似,唯一区别是使用大括号{}。

  

  

函数进阶:

  函数参数的传递机制:

  说明:

  A.在python中,函数的参数传递机制的本质是共享内存地址,不会复制对象,这种共享内存的方式叫做引用传递。

  B.引用传递模式指的是函数在调用过程中,参数与实际传入的对象共享同一块内存地址。

  基本数据类型的参数传递:

  

  

  结论:x和a是同一个对象。

  列表作为参数传递:

  

  

  结论:x和b共享内存地址。

  共享同一个对象的机制意味着,可以在函数中修改传入的参数的值。

  

  由于x和a共享同一个对象,修改x[0]会让a的值相应改变。

  

  如果我们在函数中给x赋予了一个新的值,比如列表,虽然x指向新的内存地址,但是原来的变量不变。

  

  

默认参数的传递

  说明:有默认参数时,python定义函数时,会预先为默认参数分配内存,每次调用时,python会从该内存地址得到这个默认参数,这样可以节省一定的空间。但也可能得到与直觉不符的结果。

  

  注意:使用默认参数时,尽量减少使用可变的容器类型,如列表、字典,以免出现意料之外的结果。有时可以方便我们进行相关操作,比如可利用该特性提供对计算结果的缓存。 

高阶函数:

  定义:以函数作为参数,或者返回一个函数的函数

  函数的对象性:

  python中,函数也是一种基本类型的对象。

  

  对象性使我们可以对函数进行如下操作:

  A.将函数作为参数传递给另一个函数;

  B.将函数作为字典的值;

  C.将函数作为另一个函数的返回值;

以函数作为字典的值:

  

  

  

函数作为参数的函数:

  

  python中已经有map()函数实现同样的功能,用法为:

  map(f,sq)

  map()函数也叫做转换函数,作用:将函数 f 作用到sq的每一个元素上去,并返回所有结果组成的列表。

  案例一:

  

  

  案例二:

  

以函数为返回值的函数:

  存在的问题:

  需求:求2的n次方

  

  

固定部分参数的函数:

  说明:python中的funtools模块提供了一个叫partial()的函数,它接收一个函数作为参数,并返回一个将这个函数的一部分参数固定的新函数。

  

  也可以按照函数定义时候的顺序依次固定各个参数:

  比如:

  

  map()、filter()和reduce()函数:

  作用:能为python函数式编程提供便利。

  map()函数:

  map()函数接收一个函数 f 和一个序列 sq

  map(f,sq)

  作用:将函数 f 作用到序列 sq 的所有元素上

  案例:通常情况下的解决方案:

  

  

  使用map()函数进行简化:

  

  

  也可以使用匿名函数lambdas进行简化:

  

  注意:map()函数还可以作用于列表函数:

  

  

  filter()函数:

  作用:也叫过滤函数,filter()可以过滤列表中的元素,并且返回所有符合要求的元素组成的列表,符合要求就是函数映射到该元素时返回值为True

  

  

  reduce()函数:

  作用:也叫迭代函数,它会依次对序列中的数据进行迭代计算。当需要对一个列表进行一些计算并返回结果时,可使用reduce()函数

  案例:计算整数列表的阶乘。

  

  总结:

  A.map()和filter()函数可以直接使用。

  B.reduce()必须从python3以上版本的函数库导入

  C.lambda表达式是所有单个函数中的第一个参数,iterable是第二个参数

  D.reduce()的lambda表达式需要两个参数:累加器和单个元素本身

  lambda表达式:

  定义:lambda表达式是一行函数,也称为匿名函数

  应用场景:在使用函数作为参数的时候,如果传入的函数比较简单或者使用次数较少,就可以使用python提供的lambda表达式来简化函数的定义。

  语法规则:

  lambda  参数:操作(参数)

  案例:

  

  

  列表的排序:

  

  

  列表并列排序:

  zip()函数:压缩、拉链

  

  

  注意:zip()函数解压缩时:参数前要加“*”

  当两个集合长度不一致时,以短的为准:

  

  

  注意:无论是压缩还是解压缩(包括map()及filter()函数),返回的都是迭代器,需要使用list()、set()、dict()将其转换为容器对象。

  作业:

  

  计算男生的数量:

  

  

  

  求平均年龄:

  

  

  拓展:

  tuple()方法:

  

  

  split()方法:

  

  

  课堂练习:

  

关键字global与return:

  关键字global:总体的.global变量意味着我们可以在函数以外的区域都能访问这个变量在函数的结束位置通常有一个return关键字。

  

  

  使用global声明全局变量result:

  

  

  如果这样使用,会发生什么?

  

  运行后报异常:

  

  出错原因:

  result变量只能在创建它的函数内部才允许访问,除非他是全局的(global)。

  如何解决该问题?

  

  

  注意:在实际编程中,应该尽量避免使用global关键字。

多个return值:

  问题:在函数里如何返回两个变量呢?

  实现方式1:

  

  注意:不要使用该方法,对global的使用一定要慎重!

  实现方式2:

  可以在函数结束时,返回一个包含多个值的元组(tuple),列表(list)或者字典(dict),这是可行的方式!

  

  也可以这样:

  

  这是一种比元组和字典更好的方式。

函数的递归:

  递归的定义:递归是指在函数执行过程中调用了本身,通常用于分治法,即将问题拆分为规模较小的子问题。

  例如阶乘:

  f(n)=n!=n*(n-1)!=n*f(n-1)

  因此,阶乘函数可以定义如下:

  

  

  说明:递归可以更快实现代码,但有效率上的损失。

  案例:

  斐波那契数列:1,1,2,3,5,8,13,.....

  规律:后一个数是前两个数的和。

  f(0)=f(1)=1,f(n)=f(n-1)+f(n-2)

  利用递归实现:

  

  

  利用非递归的方式实现:

  

   

  非递归版本的基本过程:

  A.初始值:a=F(0)=1, b=F(1)=1

  B.第一轮:a=F(1), b=F(0)+F(1)=F(2)   

  C.第二轮:a=F(2), b=F(1)+F(2)=F(3).................

  D.第n轮:a=F(n),b=F(n+1)

  

  

  

  

  

  递归算法的执行时间>非递归算法

  e-5代表:10的-5次方

  说明:可见,效率差别很大,非递归版本比递归版本要快,因为递归版本存在大量重复计算。

  例如:

  计算fib1(n):需要fib1(n-1)及fib1(n-2)

  调用fib(n-1):需要fib(n-2)及fib1(n-3)

  由此可见,递归版本存在重复计算问题,所以效率较低。

  如何解决重复计算的问题呢?

  为了减少重复计算,可以考虑使用缓存机制实现一个更快的递归版本,利用默认参数可变的性质,使用缓存保存已经计算的结果。

  

  输出结果:

  

  分析:由于缓存机制的存在,所以避免了重复计算,提高了计算效率。带缓存的递归版本的时间效率为:

  

装饰器:

  定义:为函数添加新特性的函数称为装饰器。

  案例:使用高阶函数定义装饰器:

  

  

  

  python提供了@符号来简化装饰器的使用。

  

  

装饰器的引入:

  装饰器的原理:装饰器的本质是一个接收函数参数的函数。比如:把装饰器A用到函数 f 的定义上:

  @A

  def f():

  等价于一个 f=A(f)

  更一般地,使用多个装饰器:

  @A

  @B

  @C

  def f():

  等价于进行一个 f=A(B(C(f)))的操作。

  注意:@操作符必须一行一个。类似"@A@B"或者"@A def f()..."这样的定义都是非法的。

  装饰器引起的问题及解决方法:

  运行代码如下:

  

  输出结果:

  

  这不是我们想要的结果!装饰器修改了原函数的_name_和_doc_属性。

  如何解决该问题?

  funtools.wraps

  

  

  装饰器使用模板的规范:

  

  

  注意:@wrap接受一个函数进行装饰,并加入了复制函数名称、注释文档、参数列表等功能。因此我们可以在装饰器里面访问在装饰器之前的函数的属性。

常用场景:

  授权:

  装饰器能帮助检查用户身份的合法性

  装饰器应用于日志:

  

  

装饰器的用法:

  在函数中嵌入装饰器:

  将日志的例子,创建一个包裹函数,指定一个输出的日志文件:

  

  

  

  说明:在项目中出现了一个out.log文件,内容同上。

  

  也可以在指定装饰器的时候,指定日志文件:

  

  

  此时,项目中出现一个func2.log文件,内容同上

  

 变量的作用域:

  定义:变量作用域是指变量的有效生存空间。

  在函数中,python会按照以下顺序,在各个作用域的命名空间中查找变量:

  A.函数局部作用域

  B.闭包作用域

  C.全局作用域

  D.内置作用域

  如果有重名的变量,局部优先于闭包,闭包优先于全局,全局优先于内置。

  函数局部作用域:

  

  局部作用域中的变量:a,b,c

  全局作用域:函数外面定义的变量

  

  全局变量c

  如果在函数内部对全局变量重新赋值,会被认为是一个局部变量:

  

  如果想在函数中对全局变量重新赋值,可以使用关键字global:

  

  在python中,只要变量被使用,它的值就会存在当前作用域不会丢失,即使变量再循环语句或判断语句中定义。

  

  当前作用域中存在如下变量:

  A.变量i,值为9

  B.变量j,值为True

  C.变量k不存在,条件不会被满足,k不会被赋值,因此不存在当前作用域中

  内置作用域:

  定义:存储内置函数或对象的作用域,包括len、sum、int、type、等,都可以在标准库模块builtins中找到:

  

  

  闭包作用域:

  定义:在全局作用域和局部作用域之间定义的变量的作用域

  

异常处理:

  异常处理的基本概念

  错误与异常:

  例如:输入print()发生错误:

  

  错误的结束文件引发异常:

  

  说明:等我们按下ctrl+z键结束文件时,引发了EOFError(end of file error)异常

  在python中,这些错误叫做异常。

  如何处理异常?

  异常最基本的术语:try/except子句。

  说明:表示可能触发异常的代码放到try语句块里,处理异常的代码在except语句块里实现。

  例如:读取一个不存在的文件时:

  

  

  try/except

  try/except/else

  

  运行代码:

  输入ctrl+z并回车:

  

  输入ctrl+c时:

  

  当正确输入时:

  

  说明:必须每一个except与try子句相对应,用于处理try语句块的不同异常。如果只有try代码块而没有except子句,是没有意义的。

  如果try代码块没有抛出异常,则不会调用相对应的except子句中对应的异常处理逻辑。

  else子句将在没有发生异常的情况下执行。

抛出异常:

  说明:可以使用raise语句来主动抛出异常。

  使用错误名或异常名以及要抛出(Thrown)的对象。注意:你能够引发的错误或异常必须是Execption异常类或子类。

  

  

  输出:

  

  

  说明:

  我们将自定义异常使用关键字as指定了对应的别名ex,从而方便在异常处理逻辑中使用该异常。

  finally子句:

  说明:在try代码块中获取资源,然后在finally块中释放资源是一种常见的模式。

  包裹在finally子句中的代码不管是否抛出异常都将被执行,这个特性主要用来在脚本执行之后做资源清理工作。

  

  输出:

  

  注意:

  else子句只会在没有异常的情况下执行,而且是在finally语句之前执行。如果在工作区中已存在test.txt文件,则不会抛出异常,只会输出finally子句中的内容。

处理不同的异常种类:

  第一种方法:

  将所有可能发生的异常放到一个元组里。

  

  第二种方法:

  对每个单独的异常在单独的except语句块中处理,我们想要多个except语句块都可以。

  

  

  这种方式中,如果异常没被第一个except语句块处理,也可能被下一个语句块处理或者根本不会被处理。

  第三种方式:

  会捕获所有的异常

  

  说明:当不知道程序会抛出什么异常时,该方式非常有用。

  

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

  

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2021-09-22 20:58  清秀。  阅读(276)  评论(0)    收藏  举报