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(空格)
- 缩进长度不对
-
- Exception
-
如何处理异常
-
处理
-
抛出新异常(捕获而不能处理,继续返回错误)
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
语句来筛选出Grade
为A
的同学
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))