一、异常处理
异常处理:处理程序可能产生的异常,增强程序的健壮,容错处理,程序异常处理后可以继续运行。
常见异常:读取一个不存在的文件。
异常处理关键字:
try:试试执行有可能出错的代码
except:捕获异常,可以出现多次,从上到下,子类到父类,精确到不精确Exception
else:没有异常才会执行
finally:有没有异常都会执行
手动抛出异常:
触发制定的错误:手动抛出异常关键字:raise
异常处理示例代码:
try:
value_str = input("请输入一个数")
value_int = int(value_str)
if value_int % 2:
print("奇数")
else:
print("偶数")
except TypeError as e:
print('类型错误')
except ValueError as e:
print("值错误")
except Exception as e:
print("不知道哪错了,但是存在异常")
finally:
print("无论对错 我胡汉三都会出来")
# 手动抛出异常
try:
raise Exception("手动抛出")
except Exception as e:
print(e, "啦啦啦")
正确输入:输入数字1
异常输入:输入字母a
捕获异常中的类继承:
自定义异常:
需要继承Exception ,可以对_ _init_ _重写
二、可迭代
简单来说:可迭代就是可以使用遍历。(遍历:逐个访问或处理数据结构中的每一个元素或成员的过程。)
常见的遍历:
列表遍历:逐个访问列表中的元素。
字典遍历:逐个访问字典中的键值对。
集合遍历:逐个访问集合中的元素。
字符串遍历:逐个访问字符串中的字符。
可迭代:一个对象是Itsrable实例则可以迭代;
内部实现了_ _iter_ _方法
from collections.abc import Iterable,Iterator
print(isinstance("hello",Iterable),isinstance([],Iterable))
print(isinstance("hello",Iterator),isinstance([],Iterator))
运行可知字符串、列表可迭代为真;是迭代器为假:
三、迭代器
重点知识:迭代器一定是可迭代的,但反过来,可迭代的不一定是迭代器。
迭代器:实现了_ _iter_ _函数(可以遍历);
实现了 _ _next_ _函数(可以使用全局next()取得下一个)
一个对象是Iterator实例则是迭代器
内部实现了_ _iter_ _与_ _next_ _两个方法
元组推导式是迭代器
from collections.abc import Iterable, Iterator
t0 = (i for i in range(10) if i % 2)
print(type(t0), isinstance(t0, Iterable), isinstance(t0, Iterator))
运行可知:(type()# 用于判断括号中对象是什么类型;)
元组t0 是迭代器 可迭代
四、生成器
生成器:没有直接使用大量空间存储数据,可以通过生成算法,生成数据
优势:节省内存开销
缺点:必须从前向后依次遍历 ,取到目标元素
写法:第一种:使用元组推导式——返回生成器
from collections.abc import Iterator, Iterable, Generator
l0 = [i for i in range(20)]
print(l0, type(l0), l0.__sizeof__())
t0 = (i for i in range(20))
print(t0, type(t0), t0.__sizeof__())
print(isinstance(t0, Iterable), isinstance(t0, Iterator), isinstance(t0, Generator))
运行可知:
第二种:函数式写法:在函数内部使用yield函数——返回值类型是生成器
from collections.abc import Iterator, Iterable, Generator
def magen():
yield 1
yield 2
yield 3
yield 4
return 520
gen = magen()
print(gen, type(gen))
print(isinstance(gen, Iterable), isinstance(gen, Iterator), isinstance(gen, Generator))
运行可知:
懒加载:
当读取一个大文件时:不到万不得已不去加载数据就是懒加载。找到自己想要的即可,后续内容不在加载。
def read_file_by_gen():
with open("06.20正则.py",encoding="utf8") as f: # 06.20正则.py是一个文件路径 请根据实际需要更改
# print(f)
for line in f:
print("正在读取中:")
yield line
content = read_file_by_gen()
for i in range(1,5): # 读取一到四行内容 循环一到五 取到一取不到五
line = next(content)
print(f"读取第{i}行")
time.sleep(1)
print(line)
运行之后就得到了指定路径的指定内容:
五、装饰器
目的:不改变函数原本实现,给函数添加新功能。
本质:闭包函数+@语法糖
闭包函数:1、外部函数内部编写内部函数
2、外部函数将内部函数返回
3、内部函数可以访问外部函数的局部变量
# 闭包函数
def myfun(f): # 外部函数
def fun(): # 内部函数
print(f"函数{f.__name__}开始执行")
f() # 外部函数的变量:函数f()
print(f"函数{f.__name__}执行结束")
return fun # 外部函数返回内部函数
# @语法糖
@myfun
def f2():
print("520")
f2()
原本实现:
不改变原有实现,添加函数执行状态:
简单案例:登录权限校验
# 闭包函数
def check_login(f): # 外部函数
def get_login(): # 内部函数
if current is None:
login() # 调用登录功能
f() # 访问外部函数的局部变量 函数f()
return get_login # 外部函数返回内部函数
current = None
# 封装一个登录功能
def login():
while True:
name = input("请输入名字")
password = input("请输入密码")
if name == "admin" and password == "123":
global current # global 关键字 在 Python 中用于声明一个变量是全局变量 可以在函数外部的其他地方访问和修改
current = "admin"
print("登录成功")
break
else:
print("登录失败")
# 封装首页
def index():
print("首页")
# 调用首页
index()
@check_login # @语法糖 调用闭包函数
# 封装个人中心
def center():
print(f"欢迎进入个人中心,当前用户{current}")
# 调用个人中心
center()
@check_login
# 封装订单管理
def order():
print(f"订单管理,当前用户{current}")
# 调用订单管理
order()
六深浅拷贝
1、等号赋值
直接赋值对象的地址,相当于使用同一个对象
d0 = {
"name": "张三",
"addr": []
}
d1 = d0
print(d0)
print(d1)
d0["age"] = 20
print("#"*20)
print(d0)
print(d1)
运行可知:d1和d0完全相同 是同一个对象
2、浅拷贝
两种使用方法:(1)、对象.copy()
(2)、import copy
copy.copy()
浅拷贝是直接拷贝内部元素的地址,开辟新的内存,内存中存储原始对象数据的地址。
import copy
d0 = {
"name": "张三",
"addr": []
}
d1 = d0
print(d0)
print(d1)
print("#"*20)
d2 = copy.copy(d0)
d0["sex"] = "男"
print(d0)
print(d2)
d0["addr"].append(3)
print("#"*20)
print(d0)
print(d2)
print(id(d0),id(d2))
print(id(d0['addr']),id(d2['addr']))
运行可知:浅拷贝是直接拷贝内部元素的地址,开辟新的内存,但内存中存储的对象地址是原始对象数据的地址。
3、深拷贝
使用方法:import copy
copy.deepcopy()
深拷贝:递归拷贝所有元素的值;完全都是新对象,两者互不干扰。
import copy
d0 = {
"name": "张三",
"addr": []
}
d1 = d0
print(d0)
print(d1)
print("#"*20)
d3 = copy.deepcopy(d0)
d0["id"] = "101"
print(d0)
print(d3)
d0["addr"].append(3)
print(d0)
print(d3)
print(id(d0),id(d3))
运行示例:
七、垃圾回收(Python的内存管理)
垃圾回收:系统运行使用内存,并且能保持内存的稳定状态 垃圾回收,回收不可用内存。
python自带垃圾回收,不需要用户去管理内存;即使主动管理内存,也不能够像c语言那么精细。
自动垃圾回收技术:从三个方面来说,
一是对象的引用计数机制;二时垃圾循环回收;三内存池机制。
1、对象的引用计数机制(引用计数) :最主要的
Python内部使用引用计数,来保持追踪内存中的对象使用次数。
一块内存被使用多少次:import sys
sys.getrefcount()
可以获得对象的当前引用计数
引用计数增加的情况:
一个对象分配一个新名称
将其放入一个容器中(如列表、元组或字典)
在函数中使用(函数为结束)
引用计数减少的情况:
使用del语句对对象别名显示的销毁
变量被重新赋值
函数执行结束
import sys
print(sys.getrefcount(1000))
a = 1000 # 赋予变量
print(sys.getrefcount(1000))
b = a # 等号赋值
print(sys.getrefcount(1000))
l = [1000] # 列表存放
print(sys.getrefcount(1000))
def fun(n): # 函数使用
print(sys.getrefcount(1000))
fun(b) # 函数执行完毕
print(sys.getrefcount(1000))
l.clear() # 列表清空
print(sys.getrefcount(1000))
b = None # 变量设置为None
print(sys.getrefcount(1000))
a = None # 变量设置为None
print(sys.getrefcount(1000))
运行结果统计1000当前引用次数:
2、循环回收
当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。
当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,、由于每个对象都包含一个
对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这
一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
3、内存池机制 Python提供了对内存的垃圾收集机制,但是它将暂时不用的内存放到内存池而不是
返回给操作系统。
Python中所有[-5, 256]使用小整数内存池,而大的对象则使用系统的malloc。