• Python4 流程控制、循环语句、异常处理、函数的高级用法


    Python4 ---- 流程控制、循环语句、异常处理、函数的高级用法

    流程控制、循环语句、异常处理、函数的高级用法

    Python的逻辑控制语句

    • if - elif - else
    x = 88
    if x >= 85:
        print("优秀")
    elif x >= 75:
        print("良好")
    elif x >= 60:
        print("及格")
    else:
        print("不及格")
    • 循环语句
      • for 遍历一个中迭代对象(暂时理解为list)
          l1 = [1, 2, 3, 4]
          for i in l1:
          	print(i)
      • 获取索引值和值
          l1 = [1, 2, 3, 4]
        
          for i, j in enumerate(l1):
          print(f"index: {i}, value: {j}")
      • while 一定要有逻辑判断语句来退出while循环,否则可能会死循环,占用系统额外资源,但也要看应用场景
          while 判断语句:
          	表达式
          	
          while True:
          	判断语句
          	表达式
          	
          counter = 0
          x = 4
          while True:
          	if counter == x:
          		break
          	print(f"sim statement: {counter}")
          	counter += 1
      • 跳出循环
        • break 停止当前循环
        • continue 跳过当前循环,立即执行下一个循环语句单元
            l = [1, 3, 5, 6, 7]
            for i in l:
            	if i == 3:
            		continue
            	else:
            		print(f"search element: {i}")
            	print("----") #可以把continue修改成pass查看
        • pass 跳过当前条件判断中的执行语句,后续语句继续执行

    Python的异常与处理

    • 异常
      程序遇到严重错误时, 会终止程序的运行并抛出异常

      def my_sub(a, b):
          return a / b
      
      my_sub(1, 0)
    • 捕获异常

      try:
      	表达式
      except [Exception] as e:
      	表达式
      finnaly:
      	表达式
      
      
      def my_sub(a, b):
          try:
              return a / b
          except ZeroDivisionError:
              # print(e)
              print("分母不可为0")
              return None
          finally: #在return之后也会执行,比如打开文件,写入时会报错,没有正常的关闭,报错是没有按流程走到正常的关闭流程,此时使用finally可定义关闭文件指令
              print("function my_sub end")
      
      my_sub(1, 0)
      • Exception
        所有异常的基类, 所有的异常都是Exception的子类
      • 处理异常颗粒度要细一点, 尽量不要捕获基类Exception, 尤其是数据处理的时候.(避免会有脏数据污染)
      • 常见的异常
        • IndexError
          索引值超过了列表长度

          >>> l = [1]
          >>> l[2]
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          IndexError: list index out of range
        • KeyError
          找不到Key

          >>> d = {"a": 1}
          >>> d["b"]
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          KeyError: 'b'
        • ValueError
          传入的参数错误

          >>> int('a1')
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          ValueError: invalid literal for int() with base 10: 'a1'
        • TypeError
          类型错误, 常见于运算

          >>> 1 + '2'
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          TypeError: unsupported operand type(s) for +: 'int' and 'str'      
        • SyntaxError
          语法报错, 检查自己的语法有没有写错

        • IndentationError
          缩进错误

          • 混用tab和space(空格)
          • 缩进长度不对
    • 如何处理异常

      • 处理

      • 抛出新异常(捕获而不能处理,继续返回错误)

        def my_sub(a, b):
            try:
                return a / b
            except ZeroDivisionError:
                print("分母不可为0")
                raise Exception("params error")
            finally:
                print("function my_sub end")
      • 重新抛出(会返回两次错误)

        def my_sub(a, b):
            try:
                return a / b
            except ZeroDivisionError:
                print("分母不可为0")
                raise ZeroDivisionError
            finally:
                print("function my_sub end")
      • 忽略(不推荐)
        pass
        用来指示当前处理语句没有正式写完, 尽量不要忽略异常, 否则代码的健壮度会很差, 造成不可预知的bug.

    • 自定义异常

      class ParamsError(Exception):
          pass
          
      def my_sub(a, b):
          try:
              return a / b
          except ZeroDivisionError:
              raise ParamsError("分母不可以为0")
          finally:
              print("function my_sub end")  
        	  
          结果
        C:\ProgramData\Anaconda3\python.exe D:/python/test.py
        function my_sub end
        Traceback (most recent call last):
          File "D:/python/test.py", line 7, in my_sub
        	return a / b
        ZeroDivisionError: division by zero
      
        During handling of the above exception, another exception occurred:
      
        Traceback (most recent call last):
          File "D:/python/test.py", line 14, in <module>
        	my_sub(1, 0)
          File "D:/python/test.py", line 9, in my_sub
        	raise ParamsError("分母不可以为0")
        __main__.ParamsError: 分母不可以为0
      
        Process finished with exit code 1

    练习

    • 用for循环和while来完成简单的计数

      #for
        def foo(x):
        	l1 = range(1, x)
        	sum = 0
        	for i in l1:
        		sum += i
        	print(f'1 到 {x - 1} 相加的和是: {sum}')
      
      
        foo(101)
        
        #while 
        	def foo(x):
        	sum = 0
        	i = 1
        	while True:
        		if i == x:
        			break
        		sum += i
        		i += 1
        	print(f'1 到 {x - 1} 相加的和是: {sum}')
      
      
        foo(101)
    • 用for循环和while循环两种方式来实现斐波那契函数, 限制在100以内

      • 斐波那契函数
        第N项是N-1, N-2的和
        F(n)=F(n - 1)+F(n - 2)
        
        [0, 1, 1, 2, 3, 5, 8, 13, 21....]
        
        def Fibonacci(n):
        if n <= 1:
            return n
        else:
            result = (Fibonacci(n - 1) + Fibonacci(n - 2))
            return result
            # return (Fibonacci(n-1) + Fibonacci(n-2))
        
        
        def for_f(x):
        	# i表示打印几个数
        	for i in range(x):
        		print(Fibonacci(i))
        
        
        def while_f(x):
        	i = 0
        	while True:
        		if i > x:
        			break
        		print(Fibonacci(i))
        		i += 1
        
        
        # 此种方法好理解
        def for_f2(n):
        	x = 0  # 第一个值
        	y = 1  # 第二个值
        	list_1 = [x, y]
        	for i in list_1:
        		i = x + y  # 相加下一个值
        		x = y
        		y = i
        		list_1.append(i)
        		if i >= n:
        			list_1.pop()
        	print(list_1)
        
        
        def while_f2(n):
        	p = 0  # 第一值
        	q = 1  # 第二值
        	i = 0  # 两个相加的值
        	list_1 = [p, q]
        	while True:
        		if i >= n:
        			break
        		i = p + q
        		p = q
        		q = i
        		list_1.append(i)
        	list_1.pop()
        	print(list_1)
    • 在第二周-第一节课我们实现的简单计算器的基础上, 对参数进行检查, 如果报错就抛出我们自定义异常ParamsError

    	参考自定义异常

    重新认识函数

    • 内置函数

      • 认识Python自带的, 可全局调用的函数, 避免我们命名冲突导致了函数性状发生改变

      • 查看Python携带的内置函数

        from pprint import pprint
        # 格式化输出的库
        pprint(dir(__builtins__))
      • 常见的内置函数

        • str

          >>> str(1.0)
          '1.0'
        • int

          >>> int(1.0)
          1
          >>> int("1.0")
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          ValueError: invalid literal for int() with base 10: '1.0'
          >>> int("1")
          1
          >>>
        • float

          >>> float("1.0")
          1.0
          >>> float(1)
          1.0
          >>> float('1')
          1.0
        • bytes

          >>> bytes('a'.encode("utf-8"))
          b'a'
        • bool

          >>> bool(0)
          False
          >>> bool(1)
          True
          >>> bool(2)
          True
          >>> bool('0')   *****
          True
          >>> bool(0.0)
          False
        • list
          只要是序列都可以转换成list

          >>> list("qwe")
          ['q', 'w', 'e']
        • tuple

          >>> tuple("qwe")
          ('q', 'w', 'e')
          >>> tuple([1,2])
          (1, 2)
        • dict

          >>> dict(a=1)
          {'a': 1}
        • set

          >>> set([1,2,2])
          {1, 2}
          >>> set("qweqweqwe")  ****
          {'q', 'w', 'e'}
        • id
          查看当前对象的内存地址

          >>> a = "1"
          >>> id(a)
          26114944
        • dir

          • 当前对象下的所有方法和属性
          • 在Python中一切皆为对象
          dir(__builtins__)
        • max
          返回一个序列中的最大值

          >>> max([2, 4,67,1])
          67
        • min
          返回一个序列中的最小值

          >>> min([2, 4,67,1])
          1
        • range
          返回一组数字区间的可迭代对象

          >>> r = range(100)
          >>> r
          range(0, 100)
          
          >>> for i in range(10):
          ...     print(i)
    • 函数的形参和实参

      • 形参
        形式参数, 简单地说就是还没接受到实际值的参数. 函数未调用时就是形参

        def my_power(a, b):
            return a ** b
      • 实参
        实际传入的参数, 函数调用时传入的值就叫实参

        print(my_power(2, 3))
    • 函数的返回值

      • 返回值的类型
        任意类型, 包括函数本身

      • 如何接受返回值

        • 接收单个值

        • 一个变量接受返回的多个值
          实际上返回的是个tuple

          >>> def foo(a, b):
          ...     return a*2, b*2
          ...
          >>> result = foo(1, 2)
          >>> result
          (2, 4)
        • 多个变量按顺序接收
          实现原理是元组解包(unpack)

          >>> a,b = foo(1,2)
          >>> a
          2
          >>> b
          4
          # 等同于
          >>> result = foo(1,2)
          >>> a, b = result
        • 不定长变量接收

          >>> result
          (1, 2, 3, 4, 5, 6, 7)
          >>> a, *b, c = result
          >>> a
          1
          >>> c
          7
          >>> b
          [2, 3, 4, 5, 6]

    匿名函数

    顾名思义匿名函数就是没有名字的函数, 一般都是提供给高阶函数调用.

    • 通过lambda关键字来声明匿名函数

      >>> lambda x: x **2
      # 返回的是一个匿名函数对象
      <function <lambda> at 0x018BB660>
    • 函数体是纯表达式

      • 不能有复杂的逻辑判断语句

        • 唯一例外的例子:
          lambda x: 返回值 if 纯表达式 else 返回值
          
          lambda x: True if  x % 2==0 else False
      • 不能有循环语句

      • 不能有异常捕获

      • 不能有赋值语句

      • 不能有return

        • 默认表达式运行的结果就是返回值
          >>> lambda x: x **2
          返回值就是 x**2
    • 例子

          sort默认根据第一个索引值对比,若想以第二个对比,查看以下代码
      l = [[1,2], [2,1], [6,4], [3,5]]
      l.sort(key=lambda x: x[1])
      print(l)

    高阶函数

    接受函数作为参数, 或者把函数作为结果返回

    • map(映射)
      对一个序列每个元素进行相同的操作, 这个过程就叫映射

      >>> l = [1,2,3]
      >>> m = map(lambda x: x**2, [1,2,3])
      >>> m = map(lambda x: x**2, l)
      
      # **获得返回结果是一个map对象**
      >>> m
      <map object at 0x03545C10>
      >>> l
      [1, 2, 3]
      
      # **map对象是一个可迭代对象, 需要驱动可迭代对象返回值**, list就有这样的功能. 暂时不要太纠结
      >>> list(m)
      [1, 4, 9]
      >>> l
      [1, 2, 3]
      • 等同于以下:

        def my_power_2(a):
        	return a ** 2
        
        # **匿名函数只是图方便, 所有的匿名都可以通过正常函数替换**
        >>> m = map(my_power_2, [1,2,3])
        >>> list(m)
        [1, 4, 9]
      • 多用于和math库进行运算操作

        >>> m = map(math.sqrt, [1, 4, 9, 16, 25])
        >>> list(m)
        [1.0, 2.0, 3.0, 4.0, 5.0]
    • filter(过滤)

      filter(函数, 可迭代对象)
      函数中的表达式返回结果为False, 就会被过滤
      l=list(range(10))
      l
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      # 过滤偶数
      >>> f = filter(lambda x: x%2, l)
      >>> list(f)
      [1, 3, 5, 7, 9]
      
      # 过滤奇数
      >>> f = filter(lambda x: False if x%2 == 1 else True, l)
      >>> list(f)
      [0, 2, 4, 6, 8]

    递归函数

    在函数中调用自身的函数就叫递归函数
    二叉树,一个节点下面有两个节点
    若使用for循环,较复杂且只有一条路,无法撤回
    递归表示规则的二叉树算法,回溯表示不规则的二叉树算法

    • 核心思想
      将大的任务拆分为子任务来解决复杂问题, 只要大任务能拆分成子任务, 就可以使用递归

      F(n) = F(F(n-1))
    • 声明一个递归函数(阶乘)

      • 一定要有退出机制
      F(n) = n * F(n-1)
      
      def fact(n):
          if n == 1:
              return 1
          return n * fact(n-1)

    实例

    • 给定一个列表, 根据grade来排序

      classes = [
          {"name": "n_1", "age": 24, "grade": "A"},
          {"name": "n_2", "age": 23, "grade": "B"},
          {"name": "n_3", "age": 28, "grade": "A"},
          {"name": "n_4", "age": 24, "grade": "A"},
          {"name": "n_5", "age": 25, "grade": "C"},
          {"name": "n_6", "age": 21, "grade": "D"},
          {"name": "n_7", "age": 27, "grade": "A"},
      ]
      
      升序
      sorted(classes,key=lambda classes:classes["grade"])
      classes.sort(key=lambda x:x["grade"])
      降序
      sorted(classes,key=lambda classes:classes["grade"],reverse=True)
    • 通过filter语句来筛选出GradeA的同学

     f=filter(lambda x: x["grade"] == "A",classes)
    print(list(f))
    • 通过map函数将上述同学的age + 1(对原数据结构有影响, 尽量不要用lambda)
    def age_add(x,age=1):
        x['age'] += age
        return x
    
    m = map(age_add, classes)
    # m = map(age_add, classes,2)
    print(list(m))
    • 使用递归函数重构斐波那契函数

      f(n) = f(n-1) + f(n-2)
      
        def F(n):
        	if n == 0:
        		return 0
        	elif n == 1:
        		return 1
        	else:
        		return F(n-1)+ F(n-2)
        for n in range(10):
        	print (F(n),end=" ")

    作用域

    程序创建, 访问, 改变一个变量时, 都是在一个保存该变量的空间内进行, 这个空间被称为命名空间, 即作用域

    • Built-in 内置

      • 可以在Python环境中的任何模块, 任意位置访问和调用
    • Global 全局变量

      • 只作用于当前模块(可以理解为当前文件)
      • 可以简单地理解为定以在函数外的变量就是全局变量, 如果在函数体定义那就时局部变量.
      • 如何将局部变量变成全局变量?
        • 使用global关键字
          a = 1
          
          def foo():
              global a
              a = 2
              print(a)
          
          foo()
          print(a)
          
          例:创建一个新的Py文件
          from week_3.lesson_3 import foo
          a=3
          foo()
          print(a)    此a是3,为什么?导入文件的模块不会更改当前变量
    • Enclosed(嵌套) 自由变量
      在嵌套函数中, 访问函数体之外的非全局变量

      • 只作用于嵌套函数体
      • 最大的应用就是闭包
      • 自由变量是个相对的概念
      • 将局部变量变成自由变量
        • 使用nonlocal关键字
          def make_averager():
              total = 0
              count = 0
              def averager(value):
                  nonlocal total, count
                  total += value
                  count += 1
                  return total / count
              return averager
              
          my_avg = make_averager()
          print(my_avg(1))
          print(my_avg(2))
    • Local局部变量

      • 只作用于当前函数体
      • 一旦变量在函数体中赋值, 那么该变量相对该函数来说就是局部变量
        a = 1
        b = []
        
        def foo():
            a = 2
            b.append(2)
            # 局部变量会在函数声明的时候就定义好
            # 不是按照我们逻辑思维上先执行全局变量b.append(2), 然后再声明一个局部变量b
            # 而是再函数声明之初就已经定义了b为局部变量
            # b = 3
            return None
        
        foo()
        print(a)
        print(b)

    闭包和装饰器

    • 闭包
      闭包指延申了作用域的函数, 也就是作用域中的Enclosed的概念

      def make_averager():
          series = []
          def averager(value):
              series.append(value)
              total = sum(series)
              return total / len(series)
          return averager
      
      # my_avg就是延伸了作用域的函数
      # series就是被延申作用域的变量
      my_avg = make_averager()
      print(my_avg(1))
      print(my_avg(2))
    • 装饰器

      • 实现原理
        就是闭包, 延伸了被装饰函数的作用域, 本质是将函数作为参数传递给一个可调用对象(函数或类)
      • 目的
        增加和扩展可调用对象(函数或类)的行为
      • 实现一个装饰器
        • 通过@关键字装饰函数

          def clock_it_deco(func):
              def wrapper(*args, **kwargs):
                  start_time = time.time()
                  result = func(*args, **kwargs)
                  end_time = time.time()
                  print(f"{func.__name__} execute time: {format(end_time - start_time, '.2f')} s")
                  return result
              return wrapper
          
          # @other_deco
          @clock_it_deco
          def foo(a, b):
              count = 1
              while True:
                  if count > a ** b:
                      break
                  count += 1
          
          foo(10, 5)
        • 等同于

          foo = clock_it_deco(foo)
          foo(10, 5)

    课后作业

    • 背诵作用域的概念
    • 练习作用域之间的转换
    • 默写一个装饰器, 用来输出函数的执行时间.
    • 使用装饰器来为斐波那契函数添加缓存
      def cache_deco(func):
      	# 保存n执行后的结果
      	a = {}
      	# 判断当前cache a中是否有结果, 有的话就直接返回, 没有就执行以下
      	result = func(n)
      	return result
      
      @cache_deco
      def fibo(n):
      	pass
        
        # ------
        def cache_deco(fun):
        cache = {}
      
        def wrapper(*args):
            if args not in cache:
                cache[args] = fun(*args)
            return cache[args]
      
        return wrapper
      
      
        @cache_deco
        def f(n):
        	if n <= 1:
        		return 1
        	return f(n - 1) + f(n - 2)
      
      
        print(f(12))
  • 相关阅读:
    day09 小练习 斐波那契数列 文件
    day09三目运算
    day08文件操作
    Nginx 内容缓存及常见参数配置
    阿里开源分布式事务解决方案 Fescar 全解析
    为什么你学不会递归?刷题几个月,告别递归,谈谈我的经验
    JavaScript 复杂判断的更优雅写法
    Java 线程本地 ThreadLocal 的分析和总结
    总结异步编程的六种方式
    JAVA8新特性(吐血整理)
  • 原文地址:https://www.cnblogs.com/final233/p/15751899.html
Copyright © 2020-2023  润新知