day01
计算机内部存储数据的原理
"""计算机内部只认识01二进制"""
是因为计算机是基于电工作的,而电是有高低电频之分
00000001 0000000 100000001
比特位(bit)
8 位= 1bytes (字节)
1024bytes = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
...
编程语言的发展史
机器语言:内部使用01二进制表示
优势:执行速度快
劣势:学习难度大
汇编语言
优势:执行速度快
劣势:学习难度大
高级语言
Python C
优势:学习难度大大降低,编程效率大大提高
劣势:执行速度相比较慢了
'''技术是服务产品的'''
编程语言的分类
1. 编译型语言
类似于谷歌翻译,翻译一次永久使用
优点:方便
劣势:不方便修改bug
2. 解释型语言
类似于同声传译 一次翻译一句
劣势:执行效率低
'''
问:如何让我的计算机能够识别编译型语言或者解释型语言?
1. 识别C++语言
安装C++编译器
2. 识别python语言
安装python解释器
'''
python解释器版本
'''在IT软件行业不能轻易的升级软件,尤其是跟程序相关的升级更新'''
1. 创作者:龟叔
2. 版本
python1.X:最原始的版本,我们可以忽略
python2.x:最高2.7 版本
python3.x
'''学习阶段可以使用3.6以上的版本都行'''
python解释器的下载与安装
1. 下载
官网:https://www.python.org/
2. 安装
3. 主要文件介绍
Scripts文件夹
基本使用
1. windows系统如何呼起cmd窗口
windows + R => 输入cmd即可
2. 如何切换路径
2.1 如何切换盘符
D:
2.2 如何切换路径
cd 路径名称
3. 进入python环境
执行 python.exe
退出python解释器:exit()
路径
1. 什么是路径
2. 什么是相对路径
eg:洋哥在'隔壁'
3. 什么是绝对路径
eg: 洋哥在中国上海浦东周浦澄天伟业5 楼512
环境变量(重点)
'''原理:Windows系统找文件的顺序是:先在当前路径下查找,如果查找不到,就去环境变量中查找,从上往下依次查找,如果,在某一个路径下找到,就不再往下找。'''
配置步骤:
1. 右键此电脑,点击属性,高级系统设置,环境变量,Path选项、
'''注意:win7和其他系统的区别'''
解释器多版本共存
1. 确保每个解释器的路径在环境变量中
2. 拷贝原来的python.exe重命名为自己的名字
第一个python程序
print ('hello world' )
方式1 :
cmd窗口直接进入python环境
方式2 :
python36 路径
'''可以永久保存代码,但是还是没有代码提示功能'''
pycharm的下载与安装
1. vscode
2. sublime
3. pycharm
'''特别好用,适用所有阶段的程序员使用'''
缺点:收费
4. notepad++
day02
pycharm软件的基本使用
File
settings
Apperance
Theme
File
settings
Editor
Font
File
settings
Project
Interpreter
'''后缀名是让我们看出文件的本质,所以,我们python也有自己独特的后缀名.py'''
1. 如果没有项目,我们应该先新建一个项目,其实就是新建一个文件夹
python语法注释
"""注释是代码之母!"""
1. 什么是注释
2. 如何使用注释?
2.1 单行注释
使用警号
2.2 多行注释
2.3 把上面一行代码复制到下面一行的快捷键:ctrl + D
'''以上这些规范是谁规定的呢?''' >>>: PEP8规范
变量
1. 什么是变量?
变化的量,用来记录事务的某种状态
2. 如何使用变量?
姓名:洋哥
年龄:18
身高:1.8
name = 'tony'
age = 18
height = 1.8
"""
语法格式:
name = 'tony'
变量 赋值符号 变量值
底层原理:
age = 18
1. 先在内存空间中申请一块地址来存储变量值18
2. 把申请的内存地址跟变量名age绑定在一起
3. 之后想使用变量值18只需要通过访问变量age即可
"""
变量的命名规范
'''补充:快捷键注释代码:ctrl + ?'''
1. 必须有数字、字母、下划线任意的组合
user@name 123_name _ my_password_123456
'''_虽然满足我们的规范,但是我们不推荐使用,因为他有特殊的含义'''
2. 变量名不能以数字开头
3. 不能与关键字冲突
4. 变量名的命名一定要见名知意(重点)
1. 驼峰体
UserNameFromDb
userNameFromDb
2. 下划线
user_name_from_db
"""python中推荐使用下划线的形式"""
常量
'''python中没有真正意义上的常量,我们约定俗成认为全大写的变量就是常量'''
eg: HOST='127.0.0.1'
const PI=3.14
变量的三要素
name = 'tony'
1. 变量值
2. 内存地址编号
3. 数据类型
"""
一个变量名只能指向一个内存地址
一个内存地址可以有多个变量指向
"""
python底层优化
'''小整数池'''
垃圾回收机制
1. 什么是垃圾数据?
2. python开发出了一套自动回收方案
1. 引用计数
2. 标记清楚
3. 分代回收
数据类型(重点)
视频文件 音频文件 表格 图像 图片 文本...
整型 浮点型 字符串 列表 元组 集合 字段...
整型int
作用:记录年龄、身高、电话号码
eg:
age = 18
浮点型float
作用:记录薪资、bmi
eg:
salary = 100.1
a = 1.0
总结
day04——数据类型
字符串str
定义:
方式1 :
name = 'kevin'
方式2 :
name = "kevin"
方式3 :
name = '''kevin'''
方式4 :
name = """kevin"""
ps:用引号引起来的部分
res = '洋哥说:' 趁年轻,学技能,养活自己''
'''注意:当有引号套用的情况下,外面是单引号里面就要是双引号,外面是双引号里面就要是单引号'''
res = '洋哥说:“趁年轻,学技能,养活自己”'
res = "洋哥说:'趁年轻,学技能,养活自己'"
列表list
定义:中括号括起来,内部可以存放多个元素,元素与元素之间使用逗号隔开,可以是任意的数据类型和嵌套
names_list = ['张三' , '李四' , '王五' ]
l = [1 , 1.1 , 'kevin' , [666 , 777 , 888 , 'tony' , [22 ,33 , 'kevinNB' ]]]
l = [1 , 1.1 , 'kevin' , [666 , 777 , 888 , 'tony' , [22 ,33 , 'kevinNB' ]]]
l1 = l[3 ]
l2 = l1[4 ]
l3 = l2[2 ]
print (l3)
l4 = l[3 ][4 ][2 ]
print (l4)
字典(重要)dict
定义:大括号括起来,内部可以存放多个元素,元素与元素之间使用逗号隔开,是以K:V键值对的形式存储
K:是对V的描述性信息(一般情况是字符串)
V:真正的数据,其实相当于变量值,也是任意的数据类型
d = {'username' : 'kevin' , 'age' : 18 , 'hobby' : 'music' }
d = {'username' : 'kevin' , 'age' : 18 , 'hobby' : 'music' }
print (d['username' ])
print (d['age' ])
print (d['hobby' ])
info = {
'username' :'tony' ,
'addr' :{
'国家' :'中国' ,
'info' :[666 , 999 , {'编号' :466722 , 'hobby' :['read' , 'study' , 'music' ]}]
}
}
d1 = info['addr' ]
d2 = d1['info' ]
d3 = d2[2 ]
d4 = d3['hobby' ]
d5 = d4[2 ]
print (d5)
d6 = info['addr' ]['info' ][2 ]['hobby' ][2 ]
print (d6)
布尔值bool
'''只有两种情况'''
True 对 正确的 可行的...
False 错误 不可行的
is_right
is_delete
...
'''结果可能是布尔值的情况,我们都采用is开头命名'''
'''在python中,所有的数据类型都可以转为布尔值'''
在python中,什么是真,什么是假?
哪些是假的情况:
0 , None , '' , [], {}...
其他都是真
元组tuple
t1 = (11 , 22 , 33 , 44 )
集合set
s = {11 ,22 ,33 ,44 }
'''集合中得元素不能直接取出'''
与用户进行交互
password = input ('请输入你的密码:>>>' )
print (password, type (password))
print (int (password) == 123 )
print (password == str (123 ))
print (123 , end='' )
格式化输出
(1 ) 什么是格式化输出?
把一段字符串里面的某些内容替换掉之后再输出,就是格式化输出。
(2 ) 为什么要格式化输出?
我们经常会输出具有某种固定格式的内容,比如:'亲爱的xxx你好!你xxx月的话费是xxx,余额是xxx‘,我们需要做的就是将xxx替换为具体的内容。
# %s称为占位符(掌握)可以为所有的数据类型占位
res = ' 亲爱的%s你好!你%s月的话费是%s,余额是%s,我们需要做的就是将xxx替换为具体的内容。'
# print(res % (' kevin', 99, 2, 99999999))
# print(res % (' kevin1', 991, 2, 100))
# print(res % (' kevin2', 992, 2, 1199999))
# print(res % (' kevin3', 993, 2, 22999999))
# print(res % (' kevin4', 994, 2, 933999999))
# res1 = ' my name is %s'
# print(res1 % ' tony')
# %d占位符(了解)只能给数字类型占位
# print("my name is %d" % ' kevin')
print("金额:%08d" % 111)
print("金额:%08d" % 666666)
print("金额:%08d" % 99999999999)
基本运算符
% ** //
'''补充'''
s1 = 'hello'
s2 = 'world'
print (s1 + s2)
print (s1 * 10 )
== !=
n = 666
n += 4
n -= 3
n *= 2
n /= 2
m = 10
n = 20
names_list = ['kevin' , 'tony' , 'tank' , 'tom' ]
l1 = names_list[0 ]
l2 = names_list[1 ]
l3 = names_list[2 ]
l4 = names_list[3 ]
'''左右两边的个数必须一致'''
ll1, *a, ll3, ll4 = ['kevin' , 'tony' , 'tank' ,'a' ,'b' , 'tom' ]
print (ll1, a, ll4)
day05——运算符、if、while循环
逻辑运算符
and 与
2 > 3 and 1 != 1 and True and 3 > 2
or 或
2 > 1 or 1 != 1 or True or 3 > 2
not 非(取反)
not True
成员运算符
符号:in (在) not in (不在)
name_list = ['kevin' , 'jack' , 'tony' , 'tom' ]
print ('k' in 'kevin' )
print ('hello' in 'helloworld' )
身份运算符
符号:is (比较的是内存地址) ==(比较的是值)
s1 = ['a' , 'b' , 'c' ]
s2 = ['a' , 'b' , 'c' ]
print (s1 == s2)
print (id (s1))
print (id (s2))
print (s1 is s2)
'''
值相等的内存地址不一定相等
内存地址相等的值一定相等
'''
流程控制(重点)
流程控制总共有3 种情况:
1. 顺序结构
2. 分支结构
3. 循环结构
分支结构
"""
注意事项:
1. 根据条件的成立与否,决定是否执行if代码块
2. 我们通过缩进代码块,来表示代码之间的从属关系
3. 不是所有的代码都拥有子代码块
4. 我们推荐使用缩进4格
5. 同属于一个代码块的子代码块缩进量一定要一样
ps:遇到冒号就要回车换行,缩进
"""
关键字:if
"""
语法格式:
if 判断条件:
print('小姐姐好')
"""
"""
语法格式:
if 判断条件:
条件成立执行的子代码块
else:
条件不成立执行的子代码块
"""
"""
语法格式:
if 条件1:
条件1成立执行的子代码块
elif 条件2:
条件1不成立条件2成立执行的子代码块
elif 条件3:
条件1、2不成立条件3成立执行的子代码块
elif 条件4:
条件1、2、3不成立条件4成立执行的子代码块
else:
以上条件都不成立的时候执行的代码块
"""
score = 56
if score >= 90 :
print ('优秀' )
elif score >=80 :
print ('良好' )
elif score >=70 :
print ('一般' )
elif score >=60 :
print ('及格' )
if判断之嵌套
age = 18
height = 1.6
weight = 90
is_beautiful=True
is_success=True
if age < 20 and height == 1.6 and weight<100 and is_beautiful:
print ('给个微信呗!!!' )
'''要么给,要么不给'''
if is_success:
print ('吃饭 看电影...' )
else :
print ('慢走不送!!!' )
else :
print ('好吧' )
if练习题
1. 写一个登录功能,用户名是:kevin 密码是:123
username=input ('username:>>>' )
password=input ('password:>>>' )
if username == 'kevin' and password == '123' :
print ('登录成功' )
else :
print ('登录失败' )
2. 根据用户名的不同打印不同的身份
kevin:管理员 jack:保安 tom:保洁 tank:销售 其余的都是普通人
username = input ('请输入你的名字:>>>' )
if username == 'kevin' :
print ('管理员' )
elif username == 'jack' :
print ('保安' )
elif username == 'tom' :
print ('保洁' )
elif username == 'tank' :
print ('销售' )
else :
print ('普通人' )
循环结构while
"""
while语法格式
while 条件:
循环体
"""
while True :
username=input ('username:>>>' )
password=input ('password:>>>' )
if username == 'kevin' and password == '123' :
print ('登录成功' )
else :
print ('登录失败' )
while+break
while True :
username=input ('username:>>>' )
password=input ('password:>>>' )
if username == 'kevin' and password == '123' :
print ('登录成功' )
break
else :
print ('登录失败' )
break本层含义
while True :
username=input ('username:>>>' )
password=input ('password:>>>' )
if username == 'kevin' and password == '123' :
print ('欢迎光临' )
while True :
cmd=input ('请输入你的指令:>>>' )
if cmd == 'q' :
break
print ('正在执行你的指令:%s' % cmd)
break
else :
print ('登录失败' )
print (123 )
标志位的使用
flag = True
while flag:
username=input ('username:>>>' )
password=input ('password:>>>' )
if username == 'kevin' and password == '123' :
print ('欢迎光临' )
while flag:
cmd=input ('请输入你的指令:>>>' )
if cmd == 'q' :
flag = False
print ('正在执行你的指令:%s' % cmd)
else :
print ('登录失败' )
作业
1. 整理内容到思维导图,写写博客
2. 猜年龄的游戏
2.1 普通要求
只允许输错3 次,输入正确直接结束
2.2 当输错3 次之后,不要直接结束,问用户是否还要在次输入,如果要,继续输入,如果不要,直接结束
day06——for循环、字符串内置方法
作业讲解
"""
1. 普通要求
三次机会
2. 拔高要求
三次之后再次询问是否继续输入
"""
real_age = 18
count = 1
while count < 4 :
guess_age = input ('请输入你猜测的年龄:>>>' )
guess_age = int (guess_age)
if guess_age > real_age:
print ('猜大了' )
elif guess_age < real_age:
print ('猜小了' )
else :
print ('哎呀,猜对了' )
break
count+=1
'''拔高'''
real_age = 18
count = 1
while count < 4 :
guess_age = input ('请输入你猜测的年龄:>>>' )
guess_age = int (guess_age)
if guess_age > real_age:
print ('猜大了' )
elif guess_age < real_age:
print ('猜小了' )
else :
print ('哎呀,猜对了' )
break
count+=1
if count == 4 :
cmd = input ('三次机会使用完了,是否再次输入yes/no' )
if cmd == 'yes' :
count=1
else :
print ('结束' )
break
while+continue
count=0
while count < 11 :
if count ==3 :
count+=1
continue
print (count)
count+=1
'''continue结束本次循环,并且回到while循环的条件处从新判断'''
while+else(了解)
count=0
while count < 10 :
if count ==5 :
count+=1
continue
print (count)
count+=1
else :
print ('哈哈哈哈' )
'''当while循环没有被中断(break)的时候,就会执行'''
死循环
'''在程序中,可千万不能出现死循环'''
while True :
print (100 )
'''能用for循环实现的,都不要使用while循环'''
for循环
name_list = ['kevin' , 'tony' , 'jack' , 'tom' ]
for name in name_list:
print (name)
"""
语法格式:
for 变量 in 可迭代对象: 字符串、列表、字典、元组
print(name)
"""
for i in 'helloworld' :
print (i)
PS:for 后面的变量名命名的时候,如果没有特殊的含义,我们一般使用i,j,k,v,item等等
'''重点'''
d = {'username' :'kevin' , 'age' :18 , 'hobby' :'music' }
for i in d:
print (i, d[i])
range关键字
第一种玩法:
for i in range (10 )
print (i)
第二种玩法:
for i in range (4 , 18 ):
print (i)
第三种玩法:
for i in range (2 , 20 , 3 ):
print (i)
"""
推断:https://movie.douban.com/top250?start=0&filter= 第一页
https://movie.douban.com/top250?start=25&filter= 第二页
https://movie.douban.com/top250?start=50&filter= 第三页
https://movie.douban.com/top250?start=75&filter= 第四页
https://movie.douban.com/top250?start=100&filter= 第五页
...
https://movie.douban.com/top250?start=225&filter= 第十页
"""
url = 'https://movie.douban.com/top250?start=%s&filter='
for i in range (0 ,250 ,25 ):
print (url % i)
'''小补充:range在不同解释器版本中是有区别的。'''
for+break
for i in range (10 ):
if i == 3 :
break
print (i)
for+continue
for i in range (10 ):
if i == 3 :
continue
print (i)
for+else
for i in range (10 ):
if i == 3 :
break
print (i)
else :
print ('哈哈哈哈' )
for循环嵌套
for i in range (1 ,10 ):
for j in range (1 ,i+1 ):
print ('%s*%s=%s' %(i,j,i*j),end=' ' )
print ()
数据类型内置方法
1. 整型
print (bin (10 ))
print (oct (10 ))
print (hex (10 ))
print (int ('0b1010' , 2 ))
print (int ('0o12' , 8 ))
print (int ('0xa' , 16 ))
2.
>>> s = '12.3'
>>> res=float (s)
>>> res,type (res)
(12.3 , <class 'float' >)
字符串的内置方法(重点)
print (str (19 ))
print (str (19.1 ))
print (str ([1 , 2 , 3 , 4 ]))
print (str ({'username' :'kecin' , 'age' :18 }))
print (str ((1 ,2 ,3 ,4 )))
print (str (True ))
print (str ({11 ,22 ,33 }))
username = input ('username:>>>' ).strip()
password = input ('password:>>>' ).strip()
if username == 'kevin' and password == '123' :
print ('登录成功' )
else :
print ('登录失败' )
>>> str1 = 'hello python!'
>>> str1[6 ]
p
>>> str1[-4 ]
h
>>> str1[0 ]='H'
>>> str1[0 :9 ]
hello pyt
>>> str1[0 :9 :2 ]
hlopt
>>> str1[::-1 ]
!nohtyp olleh
>>> len (str1)
13
>>> 'hello' in str1
True
>>> 'tony' not in str1
True
>>> str1 = ' life is short! '
>>> str1.strip()
life is short!
>>> str2 = '**tony**'
>>> str2.strip('*' )
tony
>>> str3='hello world'
>>> str3.split()
['hello' , 'world' ]
>>> str4 = '127.0.0.1'
>>> str4.split('.' )
['127' , '0' , '0' , '1' ]
>>> str5 = '今天你好吗?'
>>> for line in str5:
... print (line)
...
今
天
你
好
吗
?
作业
1. 整理今日内容
2. 熟悉今天上午讲的练习题
3. 预习下周内容
day07——内置方法
练习题讲解
温馨提示:
⽤户名与密码源于字符串 source_data='kevin|123'
想办法从中拆分出⽤户名和密码⽤于后续账户信息⽐对
普通要求:
1. 验证失败情况下可⼀直循环验证,成功则直接退出
拔⾼练习:
1. 只允许三次失败机会
2. 登录成功后进⼊内层循环,⽤户输⼊任何指令利⽤格式化
输出
打印正在执⾏该⽤户指令即可,直到⽤户输⼊字⺟q退出内层
循环
字符串的内置方法(重要)
res = 'keViN123 oldBoY'
print (res.lower())
print (res.upper())
"""
图片验证码不区分大小写
思路:把用户输入的验证码全部转为大写或者小写,跟原来的验证码都转为大写或者小写进行比较
"""
print (res.islower())
print (res.isupper())
a ='hello'
print (a.islower())
res = 'Kevin123 OldGirl'
print (res.startswith('K' ))
print (res.startswith('kev' ))
print (res.startswith('Keva' ))
print (res.endswith('Girl' ))
print (res.endswith('rl' ))
"""format方法格式化"""
'''第一种玩法'''
'''第二种玩法'''
'''第三种玩法'''
s = '{age}my name is {name}, my age is {age}{name}{name}{name}{name}{name}{name}'
print (s.format (name='kevin' , age=20 ))
l = ['tony' , 'kevin' , 'jack' , 'tom' ]
print (l[0 ] + '|' + l[1 ] + '|' + l[2 ] + '|' + l[3 ])
print ('|' .join(l))
s = 'my name is kevin kevin kevin kevin'
print (s.replace('kevin' , 'jack' , 2 ))
s = '123'
guess_age = input ('请输入你的年龄:' )
if guess_age.isdigit():
if int (guess_age) == 18 :
print ('猜对了' )
else :
print ('你输入的年龄不合法' )
字符串需要了解的方法
'''字符串内置方法需要了解的'''
msg='tony say hello hello hello'
'''find:是用来判断一个子字符串是否在另外一个字符串中'''
res = 'my name is kevin'
print (res.title())
print (res.capitalize())
print (res.swapcase())
列表的内置方法(重点)
1. 类型转换
print (list ('hello' ))
print (list ([11 , 22 , 33 , 44 ]))
print ((list ({'username' :"kevin" , "age" :20 })))
print (list ((11 , 22 , 33 , 44 )))
print (list ({11 , 22 , 33 , 44 }))
2. 列表的增加和修改
'''列表的最大索引长度:len(l)-1'''
my_friends = ['tony' , 'jason' , 'tom' , 4 , 5 , 6 , 7 , 8 ]
print (my_friends[0 :3 ])
print (my_friends[0 :5 :2 ])
print (len (my_friends))
列表的删除
'''删除'''
my_friends = ['tony' , 'jason' , 'tom' , 4 , 5 , 6 , 7 , 8 ]
s = my_friends.pop(1 )
print (s)
print (my_friends)
可变类型与不可变类型
"""
可变类型:列表
值改变,内存地址没有改变
不可变类型:整型 浮点型 字符串
值改变,内存地址也改变
"""
作业
1. 队列:先进来的先出去,先进先出
2. 栈:先进来的后出来,先进后出
'''使用列表的内置方法实现以上两种数据结构'''
day08——列表剩余、字典、元组、集合内置方法
练习题讲解
'''栈'''
stack = []
stack.append(666 )
stack.append(777 )
stack.append(888 )
print (stack)
print (stack.pop())
print (stack.pop())
print (stack.pop())
列表的剩余部分
l = [11 ,22 ,3 ,42 ,7 ,55 ]
s1 = 'hello'
s2 = 'world'
print (s1 > s2)
字典的内置方法dict
'''
大括号括起来,内部存放多个元素,元素之间逗号隔开,K:V键值对的形式
k: 是对V的描述性信息,一般是字符串类型和不可变类型(整数,字符串)
V: 才是真正的数据值,其实就是变量值,可以是任意的数据类型
'''
dic = {'username' :'kevin' , 'age' :18 }
元组tuple
"""
小括号括起来,内部存放多个元素,元组之间逗号隔开,元素不可改变,元素类型不能是任意的,
"""
定义:t1 = (11 , 22 , 33 , 44 )
print (tuple ('hello' ))
print (tuple ([11 , 22 , 33 , 44 ]))
print (tuple ((1 ,2 ,3 ,4 )))
print (tuple ({'username' :'kevin' , 'age' :19 }))
print (tuple ({111 ,222 ,33 ,444 }))
print (tuple1[-1 ])
print (tuple1[-2 ])
print (tuple1[-3 ])
>>> len (tuple1)
'''元组笔试题'''
t = (111 )
t1 = (111.11 )
t2 = ('hello' )
t3 = ('hello' , )
l = ['hello' ,]
'''容器类型:可以存放多个元素的数据类型都是容器类型
推荐:如果是容器类型,内部就算有一个元素,也最后都加上一个逗号隔开
'''
print (type (t))
print (type (t1))
print (type (t2))
print (type (t3))
print (type (l))
集合类型set
"""
作用:去重、关系运算
大括号括起来,内部存放多个元素,元素之间逗号隔开,数据类型只能是不可变的类型
不能直接取值
"""
定义:
s = {11 ,22 ,33 ,44 }
s = {}
s = {11 ,22 ,33 ,44 }
print (type (s))
print (type ({}))
s1 = set ()
print (type (s1))
d = {}
d1=dict ()
l = list ()
l = []
print (type (d1))
print (type (l))
s = set ([1 ,2 ,3 ,4 ])
print (s)
print (set ((1 ,2 ,3 ,4 )))
print (set ({'name' :'jason' ,'age' :20 }))
print (set ('hello' ))
作业
1. 整理上午内容
2. 阅读文档资料中得练习题,并试着理解逻辑
3. 预习明日内容
day09——字符编码、文件操作
练习题讲解
a = 'abcdefccdd'
展示的结果:{'a' :2 , 'b' :3 , 'd' :4 }
'''遇到字符串相关的题目,大多数情况都是转为列表,字典等常见数据类型来处理'''
name_list = 'kevin tony jack jack tom kevin tom jack tony tom kevin tom jack tony'
data_dict = {}
lst = name_list.split()
for i in lst:
if i in data_dict:
data_dict[i] += 1
else :
data_dict[i] = 1
print (data_dict)
'''bug debug'''
debug补充
1. 在当前行的代码左侧点击一下,会出现一个红点(打断点)
2. 在代码编辑区域右键选择debug,不要在选择run
员工管理系统
user_data_list = []
while True :
print ("""
1. 添加用户
2. 查看用户
3. 删除用户
4. 退出系统
""" )
cmd = input ('请输入你要选择的序号>>>:' ).strip()
if not cmd.isdigit():
continue
if cmd == '1' :
print ('添加用户' )
user_id = input ('请输入你的编号:' ).strip()
for user_data_dic in user_data_list:
if user_data_dic.get('user_id' ) == user_id:
'''如果代码走到这里,说明该用户已经存在,提示用户已经存在,请从新输入'''
print ('输入的用户已经存在,请从新输入' )
break
else :
usernane = input ('请输入用户名:' ).strip()
age = input ('请输入年龄:' ).strip()
salary = input ('请输入薪资:' ).strip()
hobby = input ('请输入爱好:' ).strip()
tmp_dict={}
tmp_dict['user_id' ] = user_id
tmp_dict['username' ] = usernane
tmp_dict['age' ] = age
tmp_dict['salary' ] = salary
tmp_dict['hobby' ] = hobby
user_data_list.append(tmp_dict)
print ('当前用户:%s添加成功' % usernane)
elif cmd == '2' :
print ('查看用户' )
'''
1. 查看指定用户的数据信息
2. 查看所有用户信息
'''
print (user_data_list)
elif cmd == '3' :
print ('删除用户' )
elif cmd == '4' :
print ('退出系统' )
else :
print ('输入的数据不合法' )
字符编码
什么是字符编码?
计算机内部只认识二进制,但是,我们人类语言有各种各样的样式,为了让计算机能够认识人类的语言,内部有一种机制就是讲人类语言转为计算机语言
字符编码的发展史
由于计算机最开始是由美国人发明的,美国人为了让计算机识别英文字符
ASCII码表:使用一个字节来记录一个英文字符
"""
所有字符加起来一共127个字符
使用一个字节(8位)可以记录255种字符
"""
A-Z:65 -90
a-z:97 -122
0 -9 :48 -57
'''小写字母对应的数字一定大于大写字母'''
s = 'hello'
s1 = 'world'
print (s > s1)
中国人:我们中国人也要使用计算机,中国人也发明了一张编码表
GBK码:记录中文英文与数据之间的对应关系
'''
我们使用两个字节或者三个字节来记录中文字符
'''
2 ** 16 = 65535
韩国人: 韩国人也要使用计算机,我们也发明了一张编码表、
Euc-kr表:只有韩文字符、英文字符与数字的一一对应关系
日本人:
Shift_JIS表: 1 、只有日文字符、英文字符与数字的一一对应关系
...
unicode表(万国码):
'''结论:unicode码只在内存中起作用'''
utf8编码
'''我们日常使用的字符编码都是utf8编码,但是,utf系列还有utf16 utf32... utf8mb4'''
字符编码的使用
1. 如何解决乱码问题?
文本文件使用什么字符编码保存,打开的时候就要使用对应的字符编码
2. python解释器版本不同带来的差异
3. 添加文件模板
settings
Editor
File and code template
python script
4. 编码与解码(核心)
将人类能够读懂的语言转为计算机能够读懂的语言
将计算机能够读懂的语言转为人类能够读懂的语言
ss = '趁年轻,学技能,养活自己'
s1 = ss.encode('utf8' )
print (s1, type (s1))
print (s1.decode('utf8' ))
s = b'kevin123'
print (s.encode('utf8' ))
文件介绍
'''python操作文件'''
1. 打开文件
2. 操作文件
3. 关闭文件
f=open ('a.txt' , 'r' , encoding='utf-8' )
print (f.read())
f.close()
"""
语法格式:
open('文件路径', '读写模式', '字符编码')
"""
'''当路径中可能存在有转移的字符时,字符串前面加上r来解决转义的问题'''
r'D:\python25\day09\a.txt'
文件的读写模式
1. r >>> read: 只读
2. w >>> write:只写
3. a >>> append: 追加
'''写文件的是一定要小心,它会清空文件的'''
with open ('a.txt' , 'w' , encoding='utf-8' ) as f:
f.write('oldboy\n' )
f.write('oldboy\n' )
f.write('oldboy\n' )
f.write('oldboy\n' )
f.write('oldboy\n' )
作业
1. 整理今日内容
2. 练习员工管理系统
3. 预习明日内容
day10——文件操作
文件的读写模式
r w a
'''以上三种模式只能操作文本'''
a w的区别
w: write
1. 文本不存在,会新建一个空文件
2. 文件存在,会先清空文件数据,在从新写
3. 写的数据类型一定是字符串类型
a: append 追加
文件的操作方法
1. 读方法
with open (r'a.txt' , 'r' , encoding='utf-8' ) as f:
'''一次性读取文件所有的数据就会存在漏洞:导致内存溢出'''
'''把文件内的一行一行数据组装成列表元素返回,注意末尾的换行符'''
2. 写方法
with open ('a.txt' , 'w' , encoding='utf-8' ) as f:
pass
文件读操作优化
'''文件句柄f支持for循环'''
'''以后读取文件数据的时候,都使用for循环去一行一行的读取,不会出现内存溢出的情况'''
课堂练习
1. 简易版本的注册功能
要求:'kevin|123'
把用户的用户名和密码组织成你自己喜欢的格式保存到文件中
username = input ('username>>>:' )
password = input ('password>>>:' )
data = '%s|%s' % (username, password)
with open ('userinfo.txt' , 'w' , encoding='utf-8' ) as f:
f.write(data)
print ('%s: 注册成功' % username)
2. 简易版本的登录功能
with open ('userinfo.txt' , 'r' , encoding='utf-8' ) as f:
data = f.read()
real_username, real_password = data.split('|' )
if username == real_username and real_password == password:
print ('登录成功' )
else :
print ('登录失败' )
进阶版本
1. 支持多用户注册
'''快捷键:tab缩进, shift+tab 反向缩进'''
'''多用户注册'''
while True :
username = input ('username>>>:' )
password = input ('password>>>:' )
with open ('userinfo.txt' , 'r' , encoding='utf-8' ) as f1:
for line in f1:
real_username, *_ = line.split('|' )
if real_username == username:
print ('该用户已经存在,请从新输入' )
break
else :
data = '%s|%s\n' % (username, password)
with open ('userinfo.txt' , 'a' , encoding='utf-8' ) as f:
f.write(data)
print ('%s: 注册成功' % username)
2. 支持多用户登录
代码整合
while True :
print ("""
1. 注册
2. 登录
""" )
cmd = input ('请输入你的选择:' ).strip()
if cmd == '1' :
while True :
username = input ('username>>>:' )
password = input ('password>>>:' )
with open ('userinfo.txt' , 'r' , encoding='utf-8' ) as f1:
for line in f1:
real_username, *_ = line.split('|' )
if real_username == username:
print ('该用户已经存在,请从新输入' )
break
else :
data = '%s|%s\n' % (username, password)
with open ('userinfo.txt' , 'a' , encoding='utf-8' ) as f:
f.write(data)
print ('%s: 注册成功' % username)
elif cmd =='2' :
username = input ('username>>>:' )
password = input ('password>>>:' )
with open ('userinfo.txt' , 'r' , encoding='utf-8' ) as f:
for line in f:
real_username, real_pwd = line.split('|' )
real = real_pwd.strip('\n' )
if real_username == username and real == password:
print ('登录成功' )
break
else :
print ('登录失败' )
文件的操作模式
1. r w a模式
"""
1. 只能操作文本文件
2. 都是以字符为单位
3. rt wt at => t可以省略
4. encoding参数必须写
"""
2. b模式:二进制
"""
1. 能操作任何的数据类型,eg:文本,视频,音频...
2. 写法:rb ab wb >>>: 此时的b不能省略
3. encoding参数不写
4. 就是以字节为单位
"""
with open ('b.txt' , 'rb' ) as f:
print (f.read().decode('utf8' ))
作业
1. 整理今日内容
2. 熟练掌握课堂练习
3. 做一个简易的拷贝功能
day11——文件指针、函数前戏
练习题讲解
"""
1. 先定义一个源文件路径
2. 先定义一个目标文件路径
3. 读取源文件的数据,然后写入到目标文件
"""
source_file_path = 'a.txt'
target_file_path = 'b.txt'
with open (r'%s' % source_file_path, 'r' , encoding='utf-8' ) as f1:
with open (r'%s' % target_file_path, 'w' , encoding='utf-8' ) as f2:
for line in f1:
f2.write(line)
文件二进制读操作
with open ('a.txt' , 'rb' ) as f:
print (f.read(3 ).decode('utf-8' ))
"""
1. r模式
read()里面的数字代表的是一个字符
2. b模式
read()里面的数字代表的是一个字节
"""
文件的移动指针(了解)
with open ('a.txt' , 'rb' ) as f:
print (f.read(12 ).decode('utf-8' ))
f.seek(-3 ,1 )
print (f.read().decode('utf-8' ))
"""
f.seek总共有3种模式
1. offset参数
偏移量,移动的位置
如果是整数,从左往右读取
如果是负数,从右往左读取
2. whence参数
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
"""
文件的修改(了解)
with open ('b.txt' , 'r' , encoding='utf-8' ) as f:
data = f.read()
print (data)
new_data = data.replace('kevin' , 'jack' )
with open ('b.txt' , 'w' , encoding='utf-8' ) as f1:
f1.write(new_data)
import os
with open ('a.txt' ,'rt' ,encoding='utf-8' ) as read_f:
with open ('b.txt' ,mode='wt' ,encoding='utf-8' ) as wrife_f:
for line in read_f:
wrife_f.write(line.replace('SB' ,'kevin' ))
os.remove('a.txt' )
os.rename('b.txt' ,'a.txt' )
函数铺垫
l = [1 , 2 , 3 , 4 , 5 , 6 ,7 ,8 ,9 ,1 ,2 ,]
def my_len ():
n = 0
for i in l :
n+=1
print (n)
print (my_len())
"""
1. 我们现在写的代码冗余性比较强
2. 我们现在写的代码兼容性不强
3. 模仿len的功能实现原理
4. 我们自己写的这个函数没有返回值(就是函数执行之后,没有返回结果)
函数简介:
类似于工具,提前准备好,方便后续使用
"""
函数的语法结构
def my_len (参数1 , 参数2 ):
'''求列表的长度'''
print (123 )
return 'hello'
"""
1. def 定义或者说声明一个函数,不能省略
2. ()函数名(不能省略)
就相当于是变量名,命名遵循变量的命名规范,见名知意
3. 参数(可有可无)
函数的精髓
什么是函数:在使用函数之前要满足的一定条件
4. 函数体代码注释(可有可无,推荐有)
主要写一些函数功能的介绍,和一些参数的解释
5. 函数体代码(核心)
6. 返回值(可有可无)
return
"""
函数的定义与调用(核心)
1. 函数必须先定义,后调用
2. 函数在定义阶段,如果有参数,调用阶段也需要给对应的参数
3. 函数在定义阶段只检测语法是否正确,不执行具体的代码功能
4. 函数在调用阶段会执行具体的函数体代码
5. 如何调用函数?
函数的底层原理:
1. 申请一块内存空间,存储函数体代码
2. 把函数体代码绑定给函数名
3. 通过调用函数(函数名())来执行函数体代码
函数的分类
1. 内置函数
eg:len , print ,之前学习的数据类型的内置方法都是内置函数
2. 自定义函数
2.1 . 无参函数:没有参数的函数
2.2 . 有参函数:带参数的函数
2.3 . 空函数:啥都没有
def my_len (a,b ):
print ('hello' )
print (a,b)
my_len('a' ,'b' )
def my_func ():
pass
...
def login ():
pass
def chongqian ():
pass
函数的返回值(重要)
def my_func1 ():
print ('helloworld' )
return '123'
res = my_func1()
print (res)
作业
1. 整理今日内容
2. 继续练习拷贝功能
3. 继续连续多用户注册和登陆功能
4. 尝试把多用户注册和登录功能封装成函数版本(可选)
day12——参数、名称空间
函数参数的两大分类
1. 形式参数
def my_func (a, b ):
pass
2. 实际参数
my_func(1 ,2 )
"""
形参和实参的关系
实际参数以变量名=数据值的形式进行传递
形参和实参的表现形式
形参的表现形式只有一种,就是变量名
实参的表现形式有多种... 核心:都是数据值
"""
位置参数
1. 位置参数
在函数定义阶段,括号中填写的参数
2. 位置形参
在函数定义阶段,括号中从左往右填写的变量名就是位置形参
def index (a, b, c ):
pass
3. 位置实参
在函数调用阶段,括号中从左往右传入的数据值就是位置实参
index(1 ,2 ,3 )
a = 10
b =20
if a>b:
return a
else :
return b
4. 关键字实参
res = my_max(b=20 , 111 )
'''参数越简单,越靠前放'''
默认参数
'''在函数定义阶段,就可以给变量赋值了'''
1. 在调用阶段如果你给了数据值,就使用你给的
2. 在调用阶段如果你不传数据值,那就使用默认的
def register (name, age, gender='male' ):
print ("%s:%s:%s" % (name, age, gender))
register('kevin' , 19 )
register('kevin1' , 19 )
register('kevin2' , 19 , 'female' )
'''注意:'''
1. 当有了默认参数,就可以传值和不传值了
x = 100
def index (a, b, c=x ):
print (a, b, c)
x = 200
index(1 ,2 )
可变长参数
def index (x, y, *a ):
print (x,y)
print (a)
"""
*在形参中得使用:接收多余的位置参数,并组装成元素的形式一一返回
"""
index(1 , 2 , 3 , 4 , 5 , 6 )
def func ( **a ):
print (a)
func(age=20 , name='kevin' , salary=1000 , gender='male' )
"""
**在形参中得使用:接收对于的关键字参数,并且把关键字参数组织成字典的形式
"""
def index (a, b, name, *c, **d ):
print (a, b)
print (name)
print (c)
print (d)
index(1 , 2 , 3 , 4 , 5 , name='kevin' , age=20 , gender='male' )
"""
*和**后面的变量名可以随意的起名字,只是一个变量名而已
python中推荐使用
*args arguments
**kw args keyword arguments
def index(*args, **kwargs):
pass
"""
"""
*在实参中得使用:把列表、元组内的各个元素打散,然后一一传值
"""
def index (**kwargs ):
print (kwargs)
dict = {'username' : 'kevin' , 'age' : '18' , 'gender' : 'male' }
"""
**在实参中得使用:把字典内的元素打散,然后以关键字的形式一一传入
"""
index(**dict )
index(**{'username' : 'kevin' , 'age' : '18' , 'gender' : 'male' })
名称空间的概念(namespace)
1. 内置的名称空间
2. 全局名称空间
name = 'kevin'
if True :
a = 1
while True :
x = 10
3. 局部名称空间
'''只要在函数中出现的都是局部的'''
4. 存活周期
名字的查找顺序
1. 如果在局部中:
局部 >>> 全局 >>> 内置
2. 如果在全局中:
全局 >>> 内置
def index ():
def index1 ():
def index2 ():
def index3 ():
def index4 ():
x = 666
print (x)
index4()
index3()
index2()
index1()
index()
作业
1. 整理今日内容
2. 继续练习多用户注册和登录,并整理成函数版本
3. 预习明日内容
day13——函数名多种用法、闭包函数、装饰器
名称空间的作用域
就是作用的范围
1. 内置的
2. 全局的
3. 局部的
global与nonlocal关键字的使用
x = ['kevin' , 'jack' ]
def index ():
x.append('tony' )
index()
print (x)
"""
局部修改全局的
1. 修改的是不可变类型,必须使用global关键字声明一下
2. 修改的是可变类型,就不用global声明了,直接可以改
"""
def index ():
x = ['a' , 'b' ]
def func ():
x.append('c' )
func()
print (x)
index()
"""
内部的局部修改外部的局部
1. 如果是不可变类型,必须使用nonlocal关键字声明一下
2. 如果是可变类型,就不需要使用nonlocal关键字声明一下
"""
函数对象(函数名)
'''函数名不加括号就是函数的内存地址'''
'''函数名只要加括号,就会执行!!!!!!!!!'''
func_dict = {
'1' : register,
'2' : login,
'3' : transfer,
'4' : shopping,
'5' : withdraw,
'6' : chongzhi,
}
while True :
print ("""
1. 注册
2. 登录
3. 转账
4. 购物
5. 提现
6. 充值
""" )
choice = input ('请输入编号:' ).strip()
if choice in func_dict:
func_name=func_dict.get(choice)
func_name()
else :
print ('不存在' )
函数的嵌套调用
def my_max (a, b ):
if a > b:
return a
return b
def many_max (a, b, c, d ):
res=my_max(a, b)
res1=my_max(res, c)
res2=my_max(res1, d)
return res2
ret=many_max(1 ,2 ,3 ,4 )
print (ret)
函数的嵌套定义
def all_func (type ):
def register ():
pass
def login ():
pass
def shopping ():
pass
def transfer ():
pass
if type == '1' :
register()
elif type == '2' :
login()
elif type == '3' :
shopping()
elif type == '4' :
transfer()
else :
print ('不存在' )
all_func('1' )
all_func('2' )
闭包函数
闭:定义在函数内部的函数
包:内部函数使用外部函数名称空间中得名字
'''只有同时满足以上两个条件的函数,才是闭包函数'''
'''爬虫的知识'''
import requests
def func (url ):
def get_content ():
res=requests.get(url)
if res.status_code == 200 :
with open (r'aaa.html' , 'wb' ) as f:
f.write(res.content)
return get_content
res=func("http://www.jd.com" )
res()
res()
res()
res()
res()
res()
res()
res1=func("http://www.baidu.com" )
res1()
res1()
res1()
res1()
res1()
res1()
装饰器(重点)
"""
装饰器不是一个新的知识点,它是我们之前学习的:名称空间,函数对象,闭包函数组合而来
"""
装饰:为被装饰对象添加新的功能
器:工具
在不改变被装饰"对象内部代码" 和"原有调用方式" 的基础之上添加额外的功能
eg:
def index ():
print ('from index' )
index()
'''给index函数添加统计执行的时间'''
import time
def index ():
time.sleep(3 )
print ('from index' )
start_time = time.time()
index()
end_time = time.time()
print ('index函数一共执行了%s秒' % (end_time - start_time))
作业
1. 整理今日内容
2. 预习明天内容装饰器
day14——装饰器
装饰器简易版本
def outer (func_name ):
def get_time ():
start——tim = time.time()
func_name()
end_time = time.time()
print ('执行了%s秒' % (end_time - start_time))
return get_time
index = outer(index)
index()
装饰器解决参数问题
def outer (func_name )
def get_time (*args,**kwargs )
start_time = time.time()
func_name(*args, **kwargs)
end_time = time.time()
print ('执行了%s秒' % (end_time - start_time))
return get_time
index = outer(index)
index()
解决返回值问题
def outer (func_name ):
def get_time (*args, **kwargs ):
start_time = time.time()
res=func_name(*args, **kwargs)
end_time = time.time()
print ('执行了%s秒' % (end_time - start_time))
return res
return get_time
home=outer(home)
print (home())
课堂练习题
flag
def login_auth(func_name):
def auth(*args,**kwargs):
if dict.get (
res = func_name(*args,**kwargs)
return res
username = input(
password = input(
if username ==
res = func_name(*args,**kwargs)
dict[
return res
else :
print(
return auth
@login_auth
def index():
print(
return 123
index()
装饰器的固定模板
def outer (func ):
def inner (*args, **kwargs ):
print ('在函数执行之前需要添加的功能' )
res=func(*args, **kwargs)
print ('在函数执行之后需要添加的功能' )
return res
return inner
语法糖
def outer (func ):
def inner (*args, **kwargs ):
print ('在函数执行之前需要添加的功能' )
res=func(*args, **kwargs)
print ('在函数执行之后需要添加的功能' )
return res
return inner
@outer
def index ():
print ('from index' )
index()
@outer
def home ():
print ('from home' )
home()
双层语法糖
import time
def outer (func_name ):
def get_time (*args, **kwargs ):
start_time = time.time()
func_name(*args, **kwargs)
end_time = time.time()
print ('执行了%s秒' % (end_time - start_time))
return get_time
def login_auth (func_name ):
def auth ():
username = input ('username:' ).strip()
password = input ('password:' ).strip()
if username == 'kevin' and password == '123' :
res=func_name()
return res
else :
print ('认证失败,不能执行函数' )
return auth
@login_auth
@outer
def index ():
time.sleep(1 )
print ('from index' )
index()
装饰器修复技术
from functools import wraps
def login_auth (func_name ):
@wraps(func_name )
def auth ():
username = input ('username:' ).strip()
password = input ('password:' ).strip()
if username == 'kevin' and password == '123' :
res=func_name()
return res
else :
print ('认证失败,不能执行函数' )
return auth
课堂练习题
def outter1 (func1 ):
print ('加载了outter1' )
def wrapper1 (*args, **kwargs ):
print ('执行了wrapper1' )
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2 (func2 ):
print ('加载了outter2' )
def wrapper2 (*args, **kwargs ):
print ('执行了wrapper2' )
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3 (func3 ):
print ('加载了outter3' )
def wrapper3 (*args, **kwargs ):
print ('执行了wrapper3' )
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1
@outter2
@outter3
def index ():
print ('from index' )
有参装饰器
def outter (source_data,a,b,c,d,e,f, *args, **kwargs ):
source_data='file'
def login_auth (func_name ):
def auth (*args, **kwargs ):
username = input ('username:' ).strip()
password = input ('password:' ).strip()
if source_data == 'file' :
print ('数据来源是文件' )
elif source_data=='mysql' :
print ('数据来源是MySQL' )
elif source_data=='oracle' :
print ('数据来源是oracle' )
else :
print ('认证失败,不能执行函数' )
return auth
return login_auth
@outter('file' ,1 ,2 ,3 ,4 ,5 ,6 , )
def index ():
print ('from index' )
day15——递归函数、二分法、匿名函数、列表字典集合生成式
递归函数
就是直接或者间接的调用自己
"""
递归:
1.递推
逐层寻找答案
2.回溯
根据最终的答案推导出最原始的答案
3.递归函数必须有结束条件!!!
"""
lst = [1 , [2 , [3 , [4 , [5 , [6 , [7 , ]]]]]]]
def get_lst (lst ):
for i in lst:
if typr(i) is int :
print (i)
else :
get_lst(i)
get_lst(lst)
二分法
eg:l = [1 , 22 , 44 ,10 , 3 , 45 , 66 , 88 ,101 , 20 , 30 ,40 ]
'''二分法原则:1.列表中得有数字必须要有序,不管是升序还是降序都可以,如果没有顺序就不能使用二分法'''
target_num = 100
l = [1 , 22 , 44 ,10 , 3 , 45 , 66 , 88 ,101 , 20 , 30 ,40 ]
def my_half (target_num, l ):
if l == []:
print ('没找到' )
return
middle_num = len (l) // 2
if target_num > l[middle_num]:
list = l[middle_num+1 :]
print (list )
my_half(target_num,list )
elif target_num < l[middle_num]:
list = l[:middle_num]
print (list )
my_half(target_num, list )
else :
print ('ok' )
my_half(target_num,l)
三元表达式
def my_max (a,b ):
if a>b:
return a
else :
return b
'''三元表达式实现上述功能'''
def my_max (a,b ):
return a if a>b else b
"""
语法结构:
条件成立之后的结果 if 条件 else 渐渐不成功之后的结果
使用场景:结果二选一的情况
"""
a = 1
b = 10
c = 10
d = 20
res = a if a > b else (c if c > d else ('bbb' if d > c else 'aaa' ))
print (res)
cmd = input ('请选择是否输入:(y/n)' )
res = '继续' if cmd == 'y' else '不继续'
print (res)
列表生成式
name_list = ['kevin' , 'jack' , 'ly' , 'tony' ]
1. 传统做法
name_list = ['kevin' , 'jack' , 'ly' , 'tony' ]
new_list = []
for name in name_list:
res = f'{name} _NB'
new_list.append(res)
print (new_list)
name_list = ['kevin' , 'jack' , 'ly' , 'tony' ]
'''特殊用法'''
name_list = ['kevin' , 'jack' , 'ly' , 'tony' ]
res = [f'{name} _NB' if name != 'jack' else '111' for name in name_list]
print (res)
字典生成式、集合生成式
'''补充:'''
"""
enumerate:
使用for循环的时候,可以解压赋值出来两个值,一个是索引,一个是元素
stret:
控制的是起始位置,默认是从0开始
"""
res = (i for i in l1)
print (res)
匿名函数
def index ():
pass
index()
"""
语法格式:
lambda 形参:返回值
lambda x:x**2
匿名函数一般步单独使用,会配合其他函数使用
map()
"""
l = [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ]
res=list (map (lambda x:x**2 , l))
print (res)
day16——迭代器、生成器、异常处理
配合匿名函数使用的方法
1. map (函数名,要遍历的数据)
l = [11 ,22 ,33 ,44 ,55 ]
lll = ['name' , 'age' , 'hobby' , 'aaa' , 'b' , 'ddd' , 'fff' ]
ll2 = ['nam2e' , 'age1' , 'hobby1' , 'aaa1' , 'b' , 'ccc' , 'ddd' , 'fff' ]
ll3 = ['name1' , 'age2' , 'hobby2' , 'aaa2' ]
res = zip (l, ll1, ll2, ll3)
print (list (res))
d = {
'Aevin' : 10000 ,
'zack' : 2000 ,
'zony' : 30000 ,
'Fank' : 5000000000
}
'''
A-Z:65-90
a:97
'''
'''如果是字典,比较的是key,说白了就是你暴露什么,就按照什么比较'''
def index (key ):
return d[key]
'''如果传了key参数,就是返回什么值就按照什么值比较'''
print (max (d, key=index))
print (max (d, key=lambda key:d[key]))
l = [1 ,10 ,20 ,55 ,66 ,70 ]
def infex (x )
return x>20
res = filter (lambda x:x>20 ,l)
print (list (res))
可迭代对象
迭代就算每一次的结果必须依赖于上一次的结果
v1.0 v2.0 v3.0
内置有_iter_方法的对象都是可迭代对象
'''内置的意思是python自带的,解释器中已经存在的,我们可以直接使用的'''
str ,list ,dict ,tuple ,set ,文件对象
print (c)
print (c.__iter__())
print (iter (c))
0x000001B18D4DC610 >
'''一般双下滑先开头的方法都有一个简便的写法,就是函数名()'''
迭代器对象
既有_iter_方法, 也含有_next_方法
'''文件对象即是可迭代对象又是迭代器对象'''
可迭代对象调用_next_方法
注意:
迭代给我们提供了一种不依赖索引取值的方法
res=c.__iter__()
res.__next__()
res.__next__()
res.__next__()
res.__next__()
print (c.__iter__().__next__())
print (c.__iter__().__next__())
print (c.__iter__().__next__())
print (c.__iter__().__next__())
print (c.__iter__().__next__())
for循环内部原理
l = [1 , 2 , 3 , 4 , 5 , 6 , 7 ]
'''for循环内部其实也报错了,只不过错误没让我们看见,内部处理了'''
"""
for循环内部执行流程:
1.把关键字in后面的数据类型转为了迭代__iter__
2.循环next取值
3.next取值完毕之后也报错了,自动处理错误并且结束while循环
"""
捕捉异常
1. 什么是异常?
2. 异常
2.1 Traceback
2.2 XXXError
2.3 XXXError冒号后面的内容,报错的详细原因,我们主要看的也是这部分,大致定位错误的原因
3. 异常的种类
1. 语法错误
2. 逻辑错误
4. 常见的错误类型
NameError
IndexError
KeyError
ValueError
ZeroDivisionError
...
5. 如何捕捉异常
try :
被监测的代码
except 错误的类型1 as e:
错误处理,e:错误的原因
except 错误的类型2 as e:
错误处理,e:错误的原因
except 错误的类型3 as e:
错误处理,e:错误的原因
except 错误的类型4 as e:
错误处理,e:错误的原因
6. '''万能异常Exception/BaseException'''
try :
待监测的代码(可能会出错的代码)
except Exception as e:
print (e)
7. '''结合else使用'''
try :
待监测的代码(可能会出错的代码)
except Exception as e:
针对各种常见的错误类型全部统一处理
print (e)
else :
try 的子代码正常运行结束没有任何的报错 再执行else 子代码
8. 结合finally 使用
try :
待监测的代码(可能会出错的代码)
except Exception as e:
针对各种常见的错误类型全部统一处理
print (e)
else :
try 的子代码正常运行结束没有任何的报错 再执行else 子代码
finally :
无论try 的子代码是否报错 最后都要执行finall子代码
"""
try except异常捕捉需要注意
1. try里面被监测的代码尽量少
2. 在明显没有错误的代码不要被捕捉
"""
异常处理补充
1. 断言
name = 'jason'
assert isinstance (name,str )
print ('sb' )
name.strip()
2. 主动抛异常
name = 'jason'
if name == 'jason' :
raise Exception('主动报错' )
else :
print ('正常执行' )
异常处理实战应用
1. 异常处理能尽量少用就少用
2. 被try 监测的代码能尽量少就尽量少
3. 当代码中可能会出现一些无法控制的情况报错才应该考虑使用
eg:使用手机访问网络软件 断网
编写网络爬虫程序请求数据 断网
课堂练习
使用while 循环+异常处理+迭代器对象 完成for 循环迭代取值的功能
l1 = [11 ,22 ,33 ,44 ,55 ,66 ,77 ,88 ,99 ]
l1 = [11 ,22 ,33 ,44 ,55 ,66 ,77 ,88 ,99 ]
iter_l1 = l1.__iter__()
while True :
try :
print (iter_l1.__next__())
except Exception as e:
break
生成器对象
1. 本质
还是内置有__iter__和__next__的迭代器对象
2. 区别
迭代器对象是解释器自动提供的
数据类型\文件对象>>>:迭代器对象
生成器对象是程序员编写出来的
代码、关键字>>>:迭代器对象(生成器)
3. 创建生成器的基本语法
函数体代码中填写yield 关键字
"""
1.函数体代码中有yield关键字 那么函数名加括号并不会执行函数体代码 会生成一个生成器对象(迭代器对象)
"""
"""
2.使用加括号之后的结果调用__next__才会执行函数体代码
"""
"""
3.每次执行完__next__代码都会停在yield位置 下次基于该位置继续往下找第二个yield
"""
def func ():
print ('哈哈哈' )
yield 111
print ('呵呵呵' )
yield 222
print ('嘿嘿嘿' )
yield 333
print ('嘻嘻嘻' )
yield 444
res = func()
r1 = res.__next__()
print (r1)
r2 = res.__next__()
print (r2)
r3 = res.__next__()
print (r3)
r4 = res.__next__()
print (r4)
'''4.yield还有点类似于return 可以返回返回值'''
课堂练习
自定义生成器对标range 功能(一个参数 两个参数 三个参数 迭代器对象)
for i in range (1 ,10 )
print (i)
def my_range (strat_num, end_num=None , step=1 ):
if not end_num:
end_num = strat_num
strat_num = 0
while strat_num < end_num:
yield strat_num
strat_num += step
for i in my_range(100 ):
print (i)
for i in my_range(1 ,10 ):
print (i)
for i in my_range(1 , 10 , 2 ):
print (i )
yield冷门用法
def eat (name, food=None ):
print (f'{name} 正在用餐' )
while True :
food = yield
print (f'{name} 正在吃{food} ' )
res = eat('王鸿轩' )
res.__next__()
res.send('大便' )
生成器表达式
说白了就是生成器的简化写法
l1 = (i ** 2 for i in range (100 ))
print (l1)
for i in l1:
print (i)
"""
面试题(有难度)
大致知道原理即可
"""
def add (n, i ):
return n + i
def test ():
for i in range (4 ):
yield i
g = test()
for n in [1 , 10 ]:
g = (add(n, i) for i in g)
"""
第一次for循环
g = (add(n, i) for i in g)
第二次for循环
g = (add(n, i) for i in (add(n, i) for i in g))
"""
res = g.__next__()
print (res)
res = list (g)
print (res)
day17——模块
索引取值与迭代取值的差异
l1 = [11 ,22 ,33 ,44 ,55 ]
1. 索引取值
可以任意位置任意次数取值
不支持无序类型的数据取值
2. 迭代取值
只能从前往后依次取值无法后退
支持所有类型的数据取值(无序有序)
ps:两者的使用需要结合实际应用场景
模块简介
1. 模块的本质
内部具有一定的功能(代码)的py文件
2. python模块的历史
python刚开始的时候所有搞其他编程语言的程序员都看不起 甚至给python起了个外号>>>:调包侠(贬义词)
随着时间的发展项目的复杂度越来越高 上面那帮人也不得不用一下python 然后发现真香定律>>>:调包侠(褒义词)
3. python模块的表现形式
1. py文件(py文件也可以称之为模块文件)
2. 含有多个py文件的文件夹(按照模块功能的不同划分不同的文件夹存储)
3. 已被编译为共享库或DLL的c或c++扩展(了解)
4. 使用c编写并链接到python解释器的内置模块(了解)
模块的分类
1. 自定义模块
我们自己写的模块文件
2. 内置模块
python解释器提供的模块
3. 第三方模块
别人写的模块文件(python背后真正的大佬)
导入模块的两种句式
"""
强调:
1.一定要搞清楚谁是执行文件 谁是被导入文件
2.以后开发项目的时候py文件的名称一般是纯英文
不会含有中文甚至空格
01 作业讲解.py 不会出现
test.py views.py 出现
3.导入模块文件不需要填写后缀名
"""
1. import 句式
以import a为例研究底层原理
"""
1.先产生执行文件的名称空间
2.执行被导入文件的代码将产生的名字放入被导入文件的名称空间中
3.在执行文件的名称空间中产生一个模块的名字
4.在执行文件中使用该模块名点的方式使用模块名称空间中所有的名字
"""
2. from ...import ...句式
以from a import name,func1为例研究底层原理
"""
1.先产生执行文件的名称空间
2.执行被导入文件的代码将产生的名字放入被导入文件的名称空间中
3.在执行文件的名称空间中产生对应的名字绑定模块名称空间中对应的名字
4.在执行文件中直接使用名字就可以访问名称空间中对应的名字
"""
导入模块补充说明
1. import 与from ...import ...两者优缺点
import 句式
由于使用模块名称空间中的名字都需要模块名点的方式才可以用
所以不会轻易的被执行文件中的名字替换掉
但是每次使用模块名称空间中的名字都必须使用模块名点才可以
from ...import ...句式
指名道姓的导入模块名称空间中需要使用的名字 你不需要模块名点
但是容易跟执行文件中名字冲突
2. 重复导入模块
解释器只会导入一次 后续重复的导入语句并不会执行
3. 起别名
import jason as js
from jason import jasonandtony as js
from a import name as n,func1 as f1
4. 涉及到多个模块导入
import a
import jason
如果模块功能相似度不高 推荐使用第一种 相似度高可以使用第二种
import a,jason
循环导入问题
1. 循环导入
两个文件之间彼此导入彼此并且相互使用各自名称空间中的名字 极容易报错
2. 如何解决循环导入问题
1. 确保名字在使用之前就已经准备完毕
2. 我们以后在编写代码的过程中应该尽可能避免出现循环导入
判断文件类型
所有的py文件都可以之间打印__name__对应的值
当py文件是执行文件的时候__name__对应的值是__main__
当py文件是被导入文件的时候__name__对应的值是模块名
if __name__ == '__main__'
print ('哈哈哈 我是执行文件 我可以运行这里的子代码' )
上述脚本可以用来区分所在py文件内python代码的执行
使用场景
1. 模块开发阶段
2. 项目启动文件
"""
from a import * *默认是将模块名称空间中所有的名字导入
__all__ = ['名字1', '名字2'] 针对*可以限制拿的名字
"""
模块的查找顺序
1. 内存
import aaa
import time
time.sleep(15 )
print (aaa.name)
aaa.func1
2. 内置
import time
print (time)
print (time.name)
"""
以后在自定义模块的时候尽量不要与内置模块名冲突
"""
3. 执行文件所在的sys.path(系统环境变量)
一定要以执行文件为准!!!
我们可以将模块所在的路径也添加到执行文件的sys.path中即可
import sys
print (sys.path)
sys.path.append(r'D:\pythonProject03\day17\mymd' )
import ccc
print (ccc.name)
绝对导入与相对导入
"""
再次强调:一定要分清楚水水执行文件!!!
模块的导入全部以执行文件为准
"""
绝对导入
from mymd.aaa.bbb.ccc.ddd import name
from mymd.aaa.bbb.ccc import ddd
ps:套路就算按照项目根目录一层层往下查找
相对导入
.在路径中表示当前目录
..在路径中表示上一层目录
..\..在路径中表示上上一层目录
不在一句执行文件所在的sys.path 而是以模块自身路径为准
from . import b
相对导入只能用于模块文件中 不能在执行文件中使用
"""
相对导入使用频率较低 一般用绝对导入即可 结构更加清晰
"""
包
大白话:多个py文件的集合>>>:文件夹
专业:内部含有__init__.py文件的文件夹(python2必须要求python3无所谓)
day18——包的使用、具体模块用法
包的具体使用
虽然python3对包的要求降低了 不需要__init__.py也可以识别 但是为了兼容性考虑最好还是加上__init__.py
1. 如果只想用包中某几个模块 那么还是按照之前的导入方式即可
from aaa import md1,md2
2. 如果直接导入包名
import aaa
导入包名其实就是导包下面的__init__.py文件 该文件内有什么名字就可以通过包名点什么名字
编程思想的转变
1. 面条版阶段
所有的代码全部堆叠在一起
2. 函数版阶段
根据功能的不同封装不同的函数
3. 模块版阶段
根据功能的不同拆分成不同的py文件
"""
第一个阶段可以看成是直接将所有的数据放在c盘
视频 音频 文本 图片
第二个阶段可以看成是将c盘下的数据分类管理
视频文件夹 音频文件夹 文本文件夹 图片文件夹
第三个阶段可以看成是将c盘下的数据根据功能的不同划分到更合适的位置
系统文件夹 C盘
视频文件夹 D盘
图片文件夹 E盘
ps:类似于开公司(小作坊——小公司——上市公司)
为了资源的高效管理
"""
软件开发目录规范
1. 文件及目录的名字可以变换 但是思想是不变的 分类管理
2. 目录规范主要规定开发程序的过程中针对不同的文件功能需要做不同的分类
myproject项目文件夹
1. bin 文件夹 主要存放项目启动文件
start.py 启动文件可以放在bin 目录下 也可以直接在项目根目录
2. conf文件夹 主要存放项目配置文件
setting.py 里面存放项目的默认配置 一般都是全大写
3. core文件夹 主要存放项目核心文件
src.py 里面存放项目核心功能
4. interface文件夹主要存放项目接口文件
goods.py 根据具体业务逻辑划分对应的文件
user.py
account.py
5. db文件夹 主要存放项目相关数据
userinfo.txt
db_handler.py 存放数据库操作相关的代码
6. log文件夹 主要存放项目日志文件
log.log
7. lib文件夹 主要存放项目公共功能
common.py
8. readme文件 主要存放项目相关说明
9. requirements.txt文件 主要存放项目所需模块及版本
常用内置模块之collections模块
1. 具名元组:namedtuple
from collections import namedtuple
card = namedtuple('扑克牌' , ['num' , 'color' ])
c1 = card('A' , '黑♠' )
c2 = card('A' , '红♥' )
print (c1,c1.num,c1.color)
print (c2,c2.num,c2.color)
2. 队列
队列与堆栈
队列:先进先出
堆栈:先进后出
队列和堆栈都是一边只能进一边只能出
常用内置模块之时间模块
import time
"""
三种时间表现形式
1.时间戳
秒数
2.结构化时间
主要是给计算机看的 人看不适应
3.格式化时间
主要是给人看的
"""
print (time.strftime('%Y/%m/%d %X' ))
time.sleep(10 )
import datetime
print (datetime.datetime.now())
print (datetime.datetime.today())
print (datetime.date.today())
"""
datetime 年月日 时分秒
date 年月日
time 时分秒(后续会有此规律)
"""
from datetime import date,datetime
print (date.today())
print (datetime.today())
print (datetime.utcnow())
import datetime
c = datetime.datetime(2023 , 3 , 9 , 19 , 22 )
print ('指定日期:' ,c)
from datetime import datetime
import datetime
ctime = datetime.date.today()
print (ctime)
time_del = datetime.timedelta(days=3 )
print (ctime + time_del)
ctime = datetime.datetime.today()
print (ctime)
time_del = datetime.timedelta(minutes=20 )
print (ctime + time_del)
常用内置模块之随机数模块
import random
'''产生图片验证码:每一位都可以是大写字母 小写字母 数字4位'''
def func (n ):
code = ''
for i in range (n):
random_upper = chr (random.randint(65 ,90 ))
random_lower = chr (random.randint(97 ,122 ))
random_num = str (random.randint(0 ,9 ))
res = random.choice([random_num,random_lower,random_upper])
code += res
return code
ret = func(4 )
print (ret)
day19——os、sys、json模块
os模块
os模块主要与代码运行所在的操作系统打交道
import os
'''1.创建目录(文件夹)'''
'''2.删除目录(文件夹)'''
'''3.列举指定路径下文件名称'''
'''4.删除/重命名文件'''
'''5.获取/切换当前工作目录'''
'''6.动态获取项目根路径(重要)'''
'''7.判断路径是否存在(文件、目录)'''
'''8.路径拼接'''
"""
涉及到路径拼接一定不要自己做 因为不同的操作系统路径分隔符不一样
"""
sys模块
import sys
print (sys.path)
print (sys.getrecursionlimit())
sys.setrecursionlimit(2000 )
print (sys.version)
(tags/v3.8 .6 :db45529, Sep 23 2020 , 15 :52 :53 ) [MSC v.1927 64 bit (AMD64)]
res = sys.argv
if len (res) != 3
print ('执行命令缺少了用户名或密码' )
else :
username = res[1 ]
password = res[2 ]
if username == 'jason' and password == '123' :
print ('jason您好 文件正常执行' )
else :
print ('您不是jason无权执行该文件' )
json模块
json模块也称为序列化模块 序列化可以打破语言限制实现不同编程语言之间数据交互
json格式数据的作用
json格式数据的形式
字符串类型并且引号都是双引号
json相关操作
针对数据
json.dumps
json.loads
针对文件
json.dump()
json.load()
json模块实战
用户登录注册功能
import os
import json
base_dir = os.path.dirname(__file__)
db_dir = os.path.join(base_dir, 'db' )
if not os.path.isdir(db_dir):
os.mkdir(db_dir)
username = input ('username>>>:' ).strip()
target_user_file_path = os.path.join(db_dir, f'{username} .json' )
if not os.path.isfile(target_user_file_path):
print ('你赶紧滚蛋 用户名都不对 搞什么飞机' )
else :
password = input ('password>>>:' ).strip()
with open (target_user_file_path,'r' ,encoding='utf8' ) as f:
real_user_dict = json.load(f)
if password == real_user_dict.get('password' ):
print ('登录成功' )
else :
print ('密码错误' )
day20——json模块补充、作业讲解
json模块补充说明
import json
d = {'name' :'json老师' , 'pwd' :123 }
res = json.dumps(d)
print (res)
res = json.dumps(d, ensure_ascii=False )
print (res)
作业讲解
1. 编写一个统计指定文件类型的脚本工具
输入指定类型的文件后缀
eg:.txt
并给出一个具体路径 之后统计该类型文件在该文件下的个数
ps:简单实现即可 无需优化
import os
dir_path = input ('请输入目录路径>>>:' ).strip()
ends_name = input ('请输入想要统计的文件后缀名>>>:' ).strip()
path_list = os.listdir(dir_path)
file_num = 0
for name in path_list:
if name.endswith(ends_name):
file_num += 1
print (f'在{dir_path} 目录第一层级 以后缀名{ends_name} 结尾的文件个数有:{file_num} ' )
"""
1.如果出现了目录的嵌套 如何统计
请说出大致思路 课下不用编写代码
2.只给一个目录的路径 直接统计该目录下第一层所有类型的内容数量
.txt:3 .py:2 目录:2 课下尝试编写代码
"""
2. 针对json实操 尝试单文件多用户(一行一个)是否可实现>>>:哪个更方便
不要求完成 单纯体会两种思路的难易
3. 编程小练习
有一个目录文件下面有一堆文本文件
eg:
db目录
J老师视频合集
R老师视频合集
C老师视频合集
B老师视频合集
文件内容自定义即可 要求循环打印出db目录下所有的文件名称让用户选择
用户选择哪个文件就自动打开该文件并展示内容
涉及到文件路径全部使用代码自动生成 不准直接拷贝当前计算机固定路径
import os
base_dir = os.path.dirname(__file__)
data_dir = os.path.join(base_dir, 'data' )
file_name_list = os.listdir(data_dir)
while True :
for num, file_name in enumerate (file_name_list, start=1 ):
print (num, file_name)
choice_num = input ('请输入您想要查看的文件编号(q)>>>:' ).strip()
if choice_num == 'q' :
print ('拜拜 下次再见 等你哟~' )
break
if not choice_num.isdigit():
print ('你眼瞎啊 编号只能是数字 你妹的!' )
continue
choice_num = int (choice_num)
if choice_num not in range (1 , len (file_name_list) + 1 ):
print ('我看你是真瞎 编号不在范围内 看清楚好好输' )
continue
target_name = file_name_list[choice_num - 1 ]
target_file_path = os.path.join(data_dir, target_name)
with open (target_file_path, 'r' , encoding='utf8' ) as f:
for line in f:
print (line, end='' )
print ()
print ('-------------------本次观看完了哟!----------------------' )
购物车
import json
import os.path
base_dir = os.path.dirname(__file__)
new_dir = os.path.join(base_dir,'shop_car_db' )
if not os.path.exists(new_dir):
os.mkdir(new_dir)
is_login = {
'username' :''
}
def register ():
while True :
username = input ('请输入您的用户名>>>:' ).strip()
password = input ('请输入您的密码>>>:' ).strip()
confirm_pwd = input ('请确认您的密码>>>:' ).strip()
if not password == confirm_pwd:
print ('密码不一致请重新输入' )
continue
file_name_path = os.path.join(new_dir,f'{username} .json' )
if file_name_path == username:
print ('用户名已存在 请重新输入' )
continue
user_dict = {
'username' :username,
'password' :password,
'balance' :15000 ,
'shop_car' :{}
}
with open (file_name_path,'w' ,encoding='utf8' ) as f:
json.dump(user_dict,f)
print (f'用户{username} 注册成功' )
break
def login ():
while True :
username = input ('请输入您的用户名>>>:' ).strip()
user_file_path = os.path.join(new_dir,f'{username} .json' )
if not os.path.exists(user_file_path):
print ('用户不存在请重新输入' )
continue
password = input ('请输入您的密码>>>:' ).strip()
with open (os.path.join(new_dir,f'{username} .json' ),'r' ,encoding='utf8' ) as f:
user_dict = json.load(f)
if not password == user_dict.get('password' ):
print ('密码错误请重新输入' )
continue
is_login['username' ] = username
print ('登录成功' )
break
def login_auth (func_name ):
def inner (*args,**kwargs ):
if is_login.get('username' ):
res = func_name(*args,**kwargs)
return res
else :
print ('您尚未登录 请先登录' )
login()
return inner
@login_auth
def add_shop_car ():
temp_shop_car = {}
while True :
good_list = [
['挂壁面' ,3 ],
['印度飞饼' ,22 ],
['极品木瓜' ,666 ],
['土耳其土豆' ,999 ],
['伊拉克版面' ,1000 ],
['董卓戏张飞公仔' ,2000 ],
['仿真玩偶' ,10000 ]
]
for num,good_data in enumerate (good_list,start=1 ):
print (f"商品编号:{num} | 商品名称:{good_data[0 ]} | 商品价格:{good_data[1 ]} " )
choice_num = input ('请输入您要选择的商品编号(q)>>>:' ).strip()
'''添加结束标志 用户保存购物车数据'''
if choice_num == 'q' :
user_file_path = os.path.join(new_dir,f'{is_login.get("username" )} .json' )
with open (user_file_path,'r' ,encoding='utf8' ) as f:
user_data_dict = json.load(f)
"""
user_data_dict['shop_car'] = temp_shop_car 不能直接替换 可能有原先的数据
{"username": "jason", "password": "123", "balance": 15000, "shop_car": {'印度飞饼':[10,22]}}
"""
old_shop_car = user_data_dict.get('shop_car' )
for g_name,g_list in temp_shop_car.items():
if g_name in old_shop_car:
old_shop_car[g_name][0 ] += temp_shop_car[g_name][0 ]
else :
old_shop_car[g_name] = g_list
user_data_dict['shop_car' ] = old_shop_car
with open (user_file_path,'w' ,encoding='utf8' ) as f:
json.dump(user_data_dict,f,ensure_ascii=False )
break
if not choice_num.isdigit():
print ('您输入的不是数字 请重新输入' )
continue
choice_num = int (choice_num)
if choice_num not in range (1 ,len (good_list)+1 ):
print ('您输入的编号超出范围 请重新输入' )
continue
target_good_list = good_list[choice_num-1 ]
good_num = input (f'请输入您想要购买的{target_good_list[0 ]} 的商品个数>>>:' ).strip()
if not good_num.isdigit():
print ('商品数量必须是纯数字' )
continue
good_num = int (good_num)
"""
temp_shop_car = {'印度飞饼':[10,22]}
"""
good_name = target_good_list[0 ]
if good_name in temp_shop_car:
temp_shop_car.get(good_name)[0 ] += good_num
else :
temp_shop_car[good_name] = [good_num,target_good_list[1 ]]
print (f'您当前的购物车已有{temp_shop_car} ' )
@login_auth
def pay_shop_car ():
user_file_path = os.path.join(new_dir,f'{is_login.get("username" )} .json' )
with open (user_file_path,'r' ,encoding='utf8' ) as f:
user_data_dict = json.load(f)
shop_car = user_data_dict.get('shop_car' )
if not shop_car:
print ('您的购物车没有商品' )
return
current_balance = user_data_dict.get('balance' )
total_money = 0
for g_list in shop_car.values():
total_money += g_list[0 ] * g_list[1 ]
if total_money > current_balance:
print ('gun 账户余额不够' )
return
user_data_dict['balance' ] -= total_money
user_data_dict['shop_car' ] = {}
with open (user_file_path,'w' ,encoding='utf8' ) as f:
json.dump(user_data_dict,f)
print (f'尊敬的{is_login.get("username" )} 您本次消费{total_money} 剩余{user_data_dict.get("balance" )} ' )
func_dict = {
'1' :register,
'2' :login,
'3' :add_shop_car,
'4' :pay_shop_car
}
while True :
print ("""
1.注册功能
2.登录功能
3.添加购物车
4.结算购物车
""" )
choice_num = input ('请输入您想要选择的功能(q)>>>:' ).strip()
if choice_num == 'q' :
break
if choice_num in func_dict:
func_dict.get(choice_num)()
else :
print ('功能编号不存在' )
day21——正则表达式、re模块
正则表达式前戏
案例:京东注册手机号校验
基本需求:手机号必须是11 位、手机号必须以13 15 17 18 19 开头、必须是出纳数字
'''纯python代码实现'''
whlie True :
phone_num = input ('请输入您的手机号>>>:' ).strip()
if len (phone_num) == 11 :
if phone_num.isdigit():
if phone_num.startswith('13' ) or phone_num.startswith('15' ) or phone_num.startswith(
'17' ) or phone_num.startswith('18' ) or phone_num.startswith('19' ):
print ('手机号码输入正确' )
else :
print ('手机号开头不对' )
else :
print ('手机号必须是纯数字' )
else :
print ('手机号必须是11位' )
'''python结合正则实现'''
import re
phone_number = input ('please input your phone number: ' )
if re.match('^(13|14|15|18)[0-9]{9}$' , phone_number):
print ('是合法的手机号码' )
else :
print ('不是合法的手机号码' )
"""
正则表达式是一门独立的技术 所有编程语言都可以使用
它的作用可以简单的概括为:利用一些特殊符号(也可以直接写需要查找的具体字符)的组合产生一些特殊的含义然后去字符串中筛选出符合条件的数据
>>>:筛选数据(匹配数据)
"""
字符组
'''字符组默认匹配方式是挨个挨个匹配'''
[0123456789] 匹配0 -9 任意一个数(全写)
[0 -9 ] 匹配0 -9 任意一个数(缩写)
[a-z] 匹配26 个小写英文字母
[A-Z] 匹配26 个大写英文字母
[0 -9a-zA-Z] 匹配数字或者小写字母或者大写字母
ps:字符组内所有的数据默认都是或的关系
特殊符号
'''特殊符号默认匹配方式是挨个挨个匹配'''
. 匹配除换行符以外的任意字符
\w 匹配数字、字母、下划线
\W 匹配非数字、非字母、非下划线
\d 匹配数字
^ 匹配字符串的开头
$ 匹配字符串的结尾
两者组合使用可以非常精确的限制匹配的内容
a|b 匹配a或者b(管道符的意思是或)
() 给正则表达式分组 不影响表达式的匹配功能
[] 字符组 内部填写的内容默认都是或的关系
[^] 取反操作 匹配除了字符组里面的其他所有字符
注意上尖号在中括号内和中括号外意思完全不同
量词
'''正则表达式默认情况下都是贪婪匹配>>>:尽可能多的匹'''
* 匹配零次或多次 默认是多次(无穷次)
+ 匹配一次或多次 默认是多次(无穷次)
? 匹配零次或一次 作为量词意义不大主要用于非贪婪匹配
{n} 重复n次
{n,} 重复n次或更多次 默认是多次(无穷次)
{n,m} 重复n到m次 默认是m次
ps:量词必须结合表达式一起使用 不能单独出现 并且只影响左边第一个表达式
jason\d{3 } 只影响\d
贪婪匹配与非贪婪匹配
"""所有的量词都是贪婪匹配如果想要变为非贪婪匹配只需要在量词后面加问号"""
待匹配的文本
<script>alert(123 )</script>
待使用的正则(贪婪匹配)
<.*>
请问匹配的内容
<script>alert(123 )</script> 一条
待使用的正则(非贪婪匹配)
<.*?>
转义符
"""斜杠与字母的组合有时候有特殊含义"""
\n 匹配的是换行符
\\n 匹配的是文本\n
\\\\n 匹配的是文本\\n
ps:如果是在python中使用 还可以在字符串前面加r取消转义
正则表达式实战建议
1. 编写校验用户身份证号的正则
^[1 -9 ]\d{13 ,16 }[0 -9x]$
^[1 -9 ]\d{14 }(\d{2 }[0 -9x])?$
^([1 -9 ]\d{16 }[0 -9x]|[1 -9 ]\d{14 })$
2. 编写校验邮箱的正则
3. 编写校验用户手机号的正则(座机、移动)
4. 编写校验用户qq号的正则
'''很多时候 很多问题 前人已经弄好了 你只需要花点时间找一找就可以'''
ps:能够写出简单的正则 能够大致看懂复杂的正则
re模块
在python中如果想要使用正则 可以考虑re模块
import re
ret = re.split('[ab]' , 'abcd' )
print (ret)
ret = re.sub('\d' , 'H' , 'eva3jason4yuan4' , 1 )
print (ret)
ret = re.subn('\d' , 'H' , 'eva3jason4yuan4' )
print (ret)
re模块补充说明
1. 分组优先
2. 分组别名
res = re.search('www.(?P<content>baidu|oldboy)(?P<hei>.com)' , 'www.oldboy.com' )
print (res.group())
print (res.group('content' ))
print (res.group('hei' ))
print (res.group(0 ))
print (res.group(1 ))
print (res.group(2 ))
网络爬虫简介
网络爬虫:通过编写代码模拟浏览器发送请求获取数据并按照自己指定的要求筛选出想要的数据
小练习:利用正则表达式筛选出红牛所有的分公司数据并打印或保存
公司名称:
公司地址:
公司邮编:
公司电话:
day24——ATM
day27——面向对象前戏、类与对象
人狗大战
"""推导步骤1:代码定义出人和狗"""
"""推导步骤2:将产生人和狗的字典封装成函数并封装人和狗的攻击函数"""
"""推导步骤3:人和狗的攻击混乱"""
面向对象核心思路前戏
"""推导步骤4:如何实现只有人只能调用人的攻击动作 狗只能调用狗的攻击动作>>>:数据与功能的绑定"""
def get_person (name,age,gender,p_type,attack_val,life_val ):
def person_attack (person_dict,dog_dict ):
print (f"人:{person_dict.get('name' )} 准备揍狗:{dog_dict.get('name' )} " )
dog_dict['life_val' ] -= person_dict.get('attack_val' )
print (f"人揍了狗一拳 狗掉血:{person_dict.get('attack_val' )} 狗剩余血量:{dog_dict.get('life_val' )} " )
person_dict = {
'name' :name,
'age' :age,
'gender' :gender,
'p_type' :p_type,
'attack_val' :attack_val,
'life_val' : life_val,
'person_attack' : person_attack
}
return person_dict
def get_dog (name, d_type, attack_val, life_val ):
def dog_attack (dog_dict, person_dict ):
print (f"狗:{dog_dict.get('name' )} 准备咬人:{person_dict.get('name' )} " )
person_dict['life_val' ] -= dog_dict.get('attack_val' )
print (f"狗咬了人一口 人掉血:{dog_dict.get('attack_val' )} 人剩余血量:{person_dict.get('life_val' )} " )
dog_dict = {
'name' : name,
'd_type' : d_type,
'attack_val' : attack_val,
'life_val' : life_val,
'dog_attack' : dog_attack
}
return dog_dict
person1 = get_person('jason' , 18 , 'male' , '猛男' , 8000 , 99999999 )
dog1 = get_dog('小黑' , '恶霸' , 800 , 900000 )
person1.get('person_attack' )(person1, dog1)
面向对象核心思想:数据与功能的绑定
编程思想
1. 面向过程编程
过程即流程 面向过程就是按照固定的流程解决问题
eg:截止ATM为止 使用的几乎都是面向过程编程
注册功能 登录功能 转账功能
需要列举出每一步的流程 并且随着步骤的深入 问题的解决越来越简单
ps:提出问题 然后制定出该问题的解决方案
2. 面向对象编程
对象即容器 数据与功能的结合体 (python中一切皆对象)
eg:游戏人物
亚索 劫 盲僧
面向对象编程有点类似于造物主的感觉 我们只需要造出一个个对象
至于该对象将来会如何发展跟程序员没关系 也无法控制
"""
上述两种编程思想没有优劣之分 需要结合实际需求而定
如果需求是注册 登录 人脸识别肯定面向过程更合适
如果需求是游戏人物肯定是面向对象更合适
实际编程两种思想是彼此交融的 只不过占比不同
"""
面向对象之类与对象
对象:数据与功能的结合体 对象才是核心
类:多个对象相同数据和功能的结合体 类主要就是为了节省代码
"""
一个人 对象
一群人 人类(所有人相同的特征)
一条狗 对象
一群狗 犬类(所有狗相同的特征)
"""
现实中一般是先有对象再有类
程序中如果想要产生对象 必须要先定义出类
类与对象的创建
面向对象并不是一门新的技术 但是为了很好的一眼区分开 针对面向对象设计了新的语法格式
python中一定要有类 才能借助于类产生对象
1. 类的语法结构
class 类名 :
'''代码注释'''
对象公共的数据
对象公共的功能
1. class 是定义类的关键字
2. 类名的命名与变量名几乎一致 需要注意的时候首字母推荐大写用于区分
3. 数据:变量名与数据值的绑定 功能(方法)其实就算函数
2. 类的定义与调用
类在定义阶段就会执行类体代码 但是属于类的局部名称空间 外界无法直接调用
class Student :
school_name = '清华大学'
def choice_course (self ):
print ('学生选课功能' )
'''在面向对象中 类和对象访问数据或者功能 可以统一采用句点符'''
'''类名加括号就会产生对象 并且每执行一次都会产生应该全新的对象'''
obj1 = Student()
obj2 = Student()
obj3 = Student()
print (obj1.school_name)
print (obj2.school_name)
print (obj3.school_name)
Student.school_name = '家里蹲大学'
print (obj1.school_name)
print (obj2.school_name)
print (obj3.school_name)
'''数据和功能 也可以统称为属性 数据>>>属性名 功能>>>方法'''
对象独有的数据
class Student ():
school_name = '清华大学'
def choice_course (self ):
print ('学生选课功能' )
obj1 = Student()
obj2 = Student()
'''推导流程1:每个对象手动添加独有的数据'''
'''推导流程2:将添加对象独有数据的代码封装成函数'''
'''推导流程3:给学生对象添加独有数据的函数只有学生对象有资格调用'''
'''推导步骤4:init方法变形'''
'''推导步骤5:变量名修改'''
class Student :
school_name = '清华大学'
def __init__ (self, name, age, hobby ):
self.name = name
self.age = age
self.hobby = hobby
def choice_course (self ):
print ('学生选课功能' )
stu1 = Student('jason' , 18 , 'read' )
print (stu1.name)
print (stu1.school_name)
对象独有的功能
class Student :
school_name = '清华大学'
def __init__ (self, name, age, hobby ):
self.name = name
self.age = age
self.hobby = hobby
def choice_course (self ):
print (f'学生{self.name} 正在选课' )
stu1 = Student('jason' , 18 , 'music' )
stu2 = Student('kevin' , 28 , 'read' )
'''定义在类中的功能 默认就是绑定给对象使用的 谁来调谁就是主人公'''
stu1.name = 'tony'
stu1.pwd = 123
print (stu1.__dict__)
day28——动静态方法、继承、派生方法
动静态方法
在类中定义的函数有多种特性
class Student :
school_name = '摆烂大学'
def func1 (self ):
print ('看谁最能摆烂 真的好棒棒哦!!!' )
@classmethod
def func2 (cls ):
print ('嘿嘿嘿 猜猜我是干嘛滴' ,cls)
@staticmethod
def func3 (a ):
print ('哈哈哈 猜猜我又是什么' ,a)
obj = Student()
面向对象之继承的概念
"""
面向对象三大特性
封装 继承 多态
1.三者中继承最为核心(实操最多 体验最强)
2.封装和多态略微抽象
"""
1. 继承的含义
在现实生活中继承表示人与人之间资源的从属关系
eg:儿子继承父亲 干女儿继承干爹
在编程世界中继承表示类与类之间资源的从属关系
eg:类A继承类B
2. 继承的目的
在现实生活中儿子继承父亲就拥有了父亲所有资源的支配权限
在编程世界中类A继承类B就拥有了类B中所有的数据和方法使用权限
3. 继承的实操
class Son (Father ):
pass
1. 在定义类的时候类名后面可以加括号填写其他类名 意味着继承其他类
2. 在python支持多继承 括号内填写多个类名彼此逗号隔开即可
class Son (F1, F2, F3):
pass
"""
1.继承其他类的类 Son
我们称之为子类、派生类
2.被继承的类 Father F1 F2 F3
我们称之为父类、基类、超类
ps:我们最常用的就是子类和父类
"""
继承的本质
"""
对象:数据与功能的结合体
类(子类):多个对象相同数据和功能的结合体
父类:多个类(子类)相同数据和功能结合体
ps:类与父类本质都是为了节省代码
"""
继承本质应该分为两部分
抽象:将多个类相同的东西抽出去形成一个新的类
继承:将多个类继承刚刚抽取出来的新的类
名字的查找顺序
1. 不继承的情况下名字的查找顺序
class C1 :
name = 'jason'
def func (self ):
print ('from func' )
obj = C1()
obj.name = '你迷了吗'
print (obj.__dict__)
print (obj.name)
print (C1.name)
"""
对象查找名字的顺序
1.先从自己的名称空间中查找
2.自己没有再去产生该对象的类中查找
3.如果类中也没有 那么直接报错
对象自身 >>> 产生对象的类
"""
2. 单继承情况下名字的查找顺序
'''
对象自身 >>> 产生对象的类 >>> 父类
'''
class A1 :
def func1 (self ):
print ('from A1 func1' )
def func2 (self ):
print ('from A1 func2' )
self.func1()
class B1 (A1 ):
def func1 (self ):
print ('from B1 func1' )
obj = B1()
obj.func2()
"""
强调:对象点名字 永远从对象自身开始一步步查找
以后在看到self.名字的时候 一定要搞清楚self指代的是哪个对象
"""
3. 多继承情况下名字的查找顺序
菱形继承
广度优先(最后才会找闭环的定点)
非菱形继承
深度优先(从左往右每条道走完为止)
ps:mro()方法可以直接获取名字的查找顺序
'''
对象自身 >>> 产生对象的类 >>> 父类(从左往右)
'''
'''
对象自身 >>> 产生对象的类 >>> 父类(从左往右)
'''
class G :
name = 'from G'
pass
class A :
pass
class B :
pass
class C :
pass
class D (A ):
pass
class E (B ):
pass
class F (C ):
pass
class S1 (D,E,F):
pass
obj = S1()
print (S1.mro())
经典类与新式类
经典类:不继承object 或者其子类的类
新式类:继承object 或者其子类的类
在python2中有经典类和新式类
在python3中只有新式类(所有类默认都继承object )
"""
class Student(object):pass
ps:以后我们在定义类的时候 如果没有其他明确的父类 也可以习惯写object兼容
派生方法
子类基于父类某个方法做了扩展
class Person :
def __init__ (self,name,age ):
self.name = name
self.age = age
class Student (Person ):
def __init__ (self,name,age,course ):
super ().__init__(name,age)
self.course = course
class Teacher (Person ):
def __init__ (self,name,age,level ):
super ().__init__(name,age)
self.level = level
stu = Student('jason' ,18 ,'python' )
print (stu.__dict__)
tea = Teacher('kevin' ,28 ,'99' )
print (tea.__dict__)
class C (list ):
def append (self, a ):
if a == 'jason' :
print ('gun' )
return
super ().append(a)
obj = C()
obj.append(111 )
obj.append('jason' )
print (obj)
day29——面向对象封装、多态、反射
派生方法实战演练
import json
import datetime
d = {
't1' : datetime.date.today(),
't2' : datetime.datetime.today(),
't3' : 'jason'
}
"""
序列化报错
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type date is not JSON serializable
"""
"""
能够被序列化的数据是有限的>>>:里里外外都必须是下列左边的类型
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
"""
"""
查看dumps源码 注意cls参数 默认传JsonEncoder
查看该类的源码 发现default方法是报错的发起者
编写类继承JsonEncoder并重新写default方法 之后调用dumps手动传cls=我们自己写的类
"""
import json
import datetime
d = {
't1' : datetime.date.today(),
't2' : datetime.datetime.today(),
't3' : 'jason'
}
class MyJsonEncoder (json.JSONEncoder):
def default (self, o ):
if isinstance (o,datetime.datetime):
return o.strftime('%Y-%m-%d %X' )
elif isinstance (o,datetime.date):
return o.strftime('%Y-%m-%d' )
return super ().default(o)
res = json.dumps(d,cls=MyJsonEncoder)
print (res)
面向对象三大特性之封装
封装:就是将数据和功能'封装' 起来
隐藏:将数据和功能隐藏起来不让用户直接调用 而是开发一些接口间接调用从而可以在接口内添加额外的操作
伪装:将类里面的方法伪装成类里面的数据
class Person :
def __init__ (self, name, age, hobby ):
self.__name = name
self.__age = age
self.__hobby = hobby
def get_info (self ):
print (f"""
姓名:{self.__name}
年龄:{self.__age}
爱好:{self.__hobby}
""" )
def set_name (self,new_name ):
if len (new_name) == 0 :
raise ValueError('好歹写点东西' )
if new_name.isdigit():
raise ValueError('名字不能说数字' )
self.__name = new_name
obj = Person('jason' ,18 ,'read' )
obj.get_info()
obj.set_name('' )
伪装
BMI指数:衡量一个人的体重与身高对健康影响的一个指标
体质指数(BMI)=体重(kg)÷身高^2 (m)
EX:70kg÷(1.75 ×1.75 )=22.86
class Person :
def __init__ (self,name,height,weight ):
self.name = name
self.height = height
self.weight = weight
@property
def BMI (self ):
return self.weight / (self.height ** 2 )
obj = Person('jason' ,1.8 ,70 )
print (obj.BMI)
class Foo :
def __init__ (self,val ):
self.__name = val
@property
def name (self ):
return self.__name
@name.setter
def name (self,value ):
if not isinstance (value,str ):
raise TypeError(f'{value} must be str' )
self.__name = value
@name.deleter
def name (self ):
raise PermissionError('Can not delete' )
f = Foo('jason' )
print (f.name)
f.name = 'tony'
print (f.name)
del f.name
三大特性之多态
多态:一种事物的多种形态
水:液态 固态 气态
动物:人 猪 猫 狗
"""
面向对象中多态意思是 一种事物可以又多种形态但是针对相同的功能应该定义相同的方法
这样无论我们拿到的是哪个具体的事务 都可以通过相同的方法调用功能
"""
"""
鸭子类型:只要你看上去像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
是一种动态的编程风格,我们更多关注的是对象的行为而不是对象的类型,我们不用再使用type或者isinstance查出它的类型了
"""
"""
文件 能够读取数据也能够保存数据
内存 能够读取数据也能够保存数据
硬盘 能够读取数据也能够保存数据
......
一切皆文件
"""
'''python永远提倡自由简介大方 不约束程序员行为 但是多态提供了约束的方法'''
import abc
class Animal (metaclass=abc.ABCMeta):
@abc.abstractmethod
def talk (self ):
pass
class Cat (Animal ):
def xxx (self ):
pass
def talk (self ):
pass
cat = Cat()
面向对象之反射
利用字符串操作对象的数据和方法
1. hasattr () 重点
判断对象是否含有某个字符串对应的属性名或方法名
2. getattr () 重点
根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3. setattr ()
根据字符串给对象设置或者修改数据
4. delattr ()
根据字符串删除对象里面的名字
'''判断某个名字对象是否可以使用(存在)'''
'''判断用户随意指定的名字对象是否可以使用(存在)'''
"""
字符串的名字跟变量名区别大不大
'school_name'
school_name
非常大 完全不一样
"""
class C1 :
school_name = '小姐姐学院'
def choice_course (self ):
print ('大宝贝们正在选课' )
obj = C1()
while True :
target_name = input ('请输入您想要操作的名字>>>:' )
if hasattr (obj, target_name):
print ('恭喜您 系统中有该名字' )
data_or_func = getattr (obj, target_name)
if callable (data_or_func):
print ('您本次使用的是系统中的某个方法' )
data_or_func()
else :
print ('您本次使用的是系统中的某个数据' )
print (data_or_func)
else :
print ('很抱歉 系统中没有该名字' )
反射实战案例
1. 什么时候应该考虑使用反射 只要需求中出现了关键字
对象....字符串....
2. 实战案例
1. 模拟cmd终端
class WinCmd :
def tasklist (self ):
print ("""
1.学习编程
2.学习python
3.学习英语
""" )
def ipconfig (self ):
print ("""
地址:127.0.0.1
地址:上海浦东新区
""" )
def get (self, target_file ):
print ('获取指定文件' ,target_file)
def put (self, target_file ):
print ('上传指定文件' ,target_file)
def server_run (self ):
print ('欢迎进入简易版本cmd终端' )
while True :
target_cmd = input ('请输入您的指令>>>:' )
res = target_cmd.split(' ' )
if len (res) == 1 :
if hasattr (self, res[0 ]):
getattr (self, res[0 ])()
else :
print (f'{res[0 ]} 不是内部或者外部命令' )
elif len (res) == 2 :
if hasattr (self, res[0 ]):
getattr (self, res[0 ])(res[1 ])
else :
print (f'{res[0 ]} 不是内部或者外部命令' )
obj = WinCmd()
obj.server_run()
2. 一切皆对象
import settings
print (dir (settings))
useful_dict = {}
for name in dir (settings):
if name.isupper():
useful_dict[name] = getattr (settings, name)
print (useful_dict)
day30——魔法、元类
面向对象的魔法方法
魔法方法:类中定义的双下方法都称为魔法方法
不需要认为调用 会在特定的条件下自动触发
eg:__init__创建空对象之后自动触发给对象添加独有的数据
1. __init__
对象添加独有数据的时候自动触发
2. __str__
对象被执行打印操作的时候自动触发
3. __call__
对象加括号调用的时候自动触发
4. __getattr__
对象点不存在的名字的时候自动触发
5. __getattribute__
对象点名字就会自动触发 有它的存在就不会执行上面的__getattr__ 优先级高
6. __setattr__
给对象添加或者修改数据的时候自动触发 对象.名字 = 值
7. __enter__
当对象被当作with 上下文管理操作的开始自动触发 并且该方法返回什么 as 后面的变量名就会接收到什么
8. __exit__
with 上下文管理语法运行完毕之后自动触发(子代码结束)
魔法方法笔试题
1. 补全下列代码使得运行不报错即可
class Context :
pass
with Context() as f:
f.do_somethong()
class Context :
def do_something (self ):
pass
def __enter__ (self ):
return self
def __exit__ (self, exc_type, exc_val, exc_tb ):
pass
with Context() as f:
f.do_something()
2. 自定义字典类型并让字典能够通过句点符的方式操作键值对
class MyDict (dict ):
def __setattr__ (self, key, value ):
self[key] = value
def __getattr__ (self, item ):
return self.get(item)
obj = MyDict()
obj.name = 'jason'
obj.pwd = 18
obj.hobby = 'read'
print (obj.name)
print (obj.pwd)
print (obj.hobby)
print (type (obj))
元类简介
"""推导步骤1:如何查看数据的数据类型"""
"""推导步骤2:其实type方法是用来查看产生对象的类名"""
"""推导步骤3:python中一切皆对象 我们好奇type查看类名显示的是什么"""
class Student :
pass
obj = Student()
print (type (obj))
print (type (Student))
class A :pass
class B :pass
print (type (A), type (B))
"""结论:我们定义的类其实都是由type类产生的>>>:元类(产生类的类)"""
创建类的两种方式
class Teacher :
school_name = '老女儿'
def func1 (self ):pass
print (Teacher)
print (Teacher.__dict__)
cls = type ('Student' , (object ,), {'name' :'jason' })
print (cls)
print (cls.__dict__)
"""
了解知识:名称空间的产生
1.手动写键值对
针对绑定方法不好定义
2.内置方法exec
能够运行字符串类型的代码并产生名称空间
"""
元类定制类的产生行为
"""
推导
对象是由类名加括号产生的 __init__
类是由元类加括号产生的 __init__
"""
"""所有的类必须首字母大写 否则无法产生"""
class MyMetaClass (type ):
def __init__ (self, what, bases=None , dict =None ):
if not what.istitle():
raise TypeError('你是不是python程序员 懂不懂规矩 类名首字母应该大写啊!!!' )
super ().__init__(what, bases, dict )
class myclass (metaclass=MyMetaClass):
desc = '元类其实很有趣 就是有点绕'
class Student (metaclass=MyMetaClass):
info = '我是学生 我很听话'
print (Student)
print (Student.__dict__)
元类定制对象的产生行为
"""
推导
对象加括号会执行产生该对象类里面的 __call__
类加括号会执行产生该类的类里面的 __call__
"""
"""给对象添加独有数据的时候 必须采用关键字参数传参"""
class MyMetaClass (type ):
def __call__ (self, *args, **kwargs ):
if args:
raise TypeError("你怎么回事 Jason要求对象的独有数据必须按照关键字参数传参 我看你是不想干了!!!" )
return super ().__call__(*args, **kwargs)
class Student (metaclass=MyMetaClass):
def __init__ (self, name, age, gender ):
self.name = name
self.age = age
self.gender = gender
obj = Student(name='jason' ,age= 18 ,gender= 'male' )
print (obj.__dict__)
魔法方法之双下new
class MyMetaClass (type ):
def __call__ (self, *args, **kwargs ):
obj = self.__new__(self)
self.__init__(obj,*args, **kwargs)
return obj
class Student (metaclass=MyMetaClass):
def __init__ (self, name ):
self.name = name
obj = Student('jason' )
print (obj.name)
"""
__new__可以产生空对象
"""
设计模式简介
1. 设计模式
前人通过大量的验证创建出来解决一些问题的固定高效方法
2. IT行业
23 种
创建型
结构型
行为型
ps:课下感兴趣可以简单看看
3. 单例模式
类加括号无论执行多少次永远只会产生一个对象
目的:
当类中有很多非常强大的方法 我们在程序中很多地方都需要使用
如果不做单例 会产生很多无用的对象浪费存储空间
我们想着使用单例模式 整个程序就用一个对象
面向对象大回顾
1. 面向过程
...
2. 面向对象
...
3. 类的定义和对象的产生
class Student ():
pass
"""
定义类发生的事情有那些?
1.会立即执行类体代码
2.产生类的名称空间,把类中得名字丢到类的名称空间去
3.把类的名称空间绑定给__dict__,类名.__dict__
"""
调用类:类名()
得到一个对象,这个对象默认是空对象
obj = Student()
'''得到的对象也有他自己的名称空间'''
obj.__dict__
4. 定制对象自己独有的属性
class Student ():
def __init__ (self,name,age,gender ):
self.name = name
self.age = age
self.gender = gender
obj = Student('kevin' ,20 ,'female' )
print (obj.name,obj.age,obj.gender)
print (obj.__dict__)
5. 属性的查找顺序
1. 类属性
2. 对象属性
6. 绑定方法和非绑定方法
1. 绑定给对象的方法:
2. 绑定给类的方法:
在方法的上面加一个装饰器 @classmethod
7. 非绑定方法
在方法上面加一个装饰器 @staticmethod
对象和类在调用的时候都不用传参数
8. 隐藏属性
'''
1.在类的定义阶段隐藏的,名字发生了变形:_类名__名字
2.在类的外部原则上不能用了(对外不对内),非要用可以点_类名__名字的方式使用
'''
在类的内部开放一个接口来返回类中隐藏的属性,可以更好的对属性做限制
9. property 装饰器
'''把方法伪装成属性之后,以后在调用的时候可以不加括号直接调用'''
1. @property
2. @方法名字.setter
3. @方法名字.deleter
class Foo :
xxx = property ('get_country' ,'set_country' ,'del_country' )
obj = Foo
obj.xxx
obj.xxx = 'a'
del obj.xxx
10. 继承
"""
1. 继承就是新建类的一种方式,被创建出来的类是子类或者叫派生类,被继承的类称为父类或者基类
2. 继承解决类与类之间的代码冗余问题
3. 在子类括号里面写上继承的父类名字
4. 子类可以继承父类的所有属性和方法
5. python中,支持单继承和多继承
"""
class Foo ():
pass
class Bar (Foo ):
pass
"""
1. 多继承分菱形继承和非菱形继承
2. 经典类和新式类的查找
在Python2中会出现经典类
3. 经典类的属性查找顺序:深度优先
4. 新式类的属性差债:广度优先
"""
11. super 和mro
'''遇到多继承中出现了super关键字,如何更准确的知道它的查找顺序,我们需要打印出类的mro列表'''
print (类名.mro())
12. 多态
'''
抽象类的特点:只能被继承,不能被实例化
'''
import abc
class Student (metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak (self ):
print (123 )
class People (Student ):
def tall (self ):
print (12345 )
def speak (self ):
print (123 )
class Dog (Student ):
def speak (self ):
print (123 )
'''不能实例化抽象类 只能实例化抽象类的子类 产生的对象去调用子类的方法'''
obj = People()
obj.tall()
class People ():
def speak (self ):
raise Exception('必须实现该方法' )
class Student (People ):
def speak (self ):
pass
def tall (self ):
pass
'''如果子类中没有speak方法则会主动报异常'''
obj = Student()
obj.speak()
13. 组合
...
14. 魔法
1. __init__
对象添加独有数据的时候自动触发
2. __str__
对象被执行打印操作的时候自动触发
3. __call__
对象加括号调用的时候自动触发
4. __getattr__
对象点不存在的名字的时候自动触发
5. __getattribute__
对象点名字就会自动触发 有它的存在就不会执行上面的__getattr__ 优先级高
6. __setattr__
给对象添加或者修改数据的时候自动触发 对象.名字 = 值
7. __enter__
当对象被当作with 上下文管理操作的开始自动触发 并且该方法返回什么 as 后面的变量名就会接收到什么
8. __exit__
with 上下文管理语法运行完毕之后自动触发(子代码结束)
15. 反射
1. hasattr () 重点
判断对象是否含有某个字符串对应的属性名或方法名
2. getattr () 重点
根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3. setattr ()
根据字符串给对象设置或者修改数据
4. delattr ()
根据字符串删除对象里面的名字
16. 异常
raise
assert
day34——软件开发架构、OSI七层协议
软件开发架构
规定了程序的请求逻辑、功能分块
1. C/S架构
Client:客户端
Server:服务端
"""
我们使用计算机下载下来的一个个app本质是各大互联网公司的客户端软件
通过这些客户端软件我们就可以体验到各个互联网公司给我们提供的服务
eg:
下载淘宝客户端 打开 体验淘宝服务端提供的购物服务
下载抖音客户端 打开 体验抖音服务端提供的视频服务
ps:
一般情况下客户端与服务端交互需要互联网 但是有些不需要(因为客户端和服务端都在一台计算机上)
客户端:即将要去消费的客人
服务端:给客人提供服务的店
作为服务端必备的多个条件
1.24小时不间断提供服务
2.固定的地址
3.能够服务多个客人(搞并发)
"""
2. B/S架构
Browser:浏览器
Server:服务器/端
"""
浏览器可以充当所有服务端的客户端
ps:B/S架构本质还是C/S架构
"""
'''
B/S架构
优势:不同公司的客户端由不同公司独立开发 可以高度定制化客户端功能
劣势:需要下载才能使用
C/S架构
优势:不用下载直接访问
劣势:无法高度定制化 并且需要遵守很多规则
'''
架构总结
ATM:三层架构
选课系统:三层架构
本质上也属于软件开发架构的范畴
软件设计的大方向>>>:统一接口
微信小程序
支付宝小程序
网络编程前戏
1. 什么是网络编程
基于网络编写代码 能够实现数据的远程交互
2. 学习网络编程的目的
能够开发cs架构的软件
3. 网络编程的起源
"""
最早起源于美国军事领域
想实现计算机之间数据的交互
最早的时候只能用硬盘拷贝
之后发明了网络编程
"""
4. 网络编程必备条件
数据的远程交互
1. 早期的电话
电话线
2. 早期的大屁股电脑
网线
3. 笔记本电脑、移动电话
网卡
ps:实现数据的远程交互必备的基础条件是物理连接介质
OSI七层协议简介
"""
OSI七层协议:规定了所有的计算机在远程数据交互的时候必须经过相同的处理流程、在制造过程中必须拥有相同的功能硬件
"""
应用层
表示层
会话层
传输层
网络层
数据链路层
物理连接层
pa:应、表、会、传、网、数、物
'''常见的是整合之后五层或者四层'''
应用层
传输层
网络层
数据链路层
物理连接层
应用层
传输层
网络层
网络接口层
"""
接收网络消息 数据由下往上传递
发送网络消息 数据由上往下传递
"""
OSI七层协议之物理连接层
主要用于确保计算机之间的物理连接介质 接收数据(bytes 类型、二进制)
OSI七层协议之数据链路层
1. 规定了电信号的分组方式
2. 以太网协议
规定了计算机在出厂的时候都必须有一块网卡网卡上有一串数字
该数字相当于是计算机的十分钟号码 是独一无二的
该数字的特征:12 位16 进制数据
前6 位产商编号 后6 位流水线号
该数字也成为:以太网地址/MAC地址
网络相关专业名词
计算机之间要想实现数据交互必须要'连接' 到一起
1. 交换机
能够将所有接入交换机的计算机彼此互联起来
2. 广播
首次查找接入同一个交换机的其他计算机 需要朝交换机里面吼一嗓子
3. 单播
首次被查找的计算机回应查找它的计算机 并附带自己的mac地址
4. 广播风暴
接入同一台交换机的多台计算机同时发广播
5. 局域网
可以简单的理解为有单个交换机组成的网络
在局域网内可以直接使用mac地址通信
6. 广域网
可以简单的理解为范围更大的局域网
7. 互联网
由所有的局域网、广域网连接到一起形成的网络
8. 路由器
不同的局域网计算机之间是无法直接实现数据交互的 需要路由器连接
OSI七层协议之网络层
IP协议:规定了所有接入互联网的计算机都必须有一个IP地址 类似于身份证号码
mac地址是物理地址可以看成永远无法修改
IP地址是动态分配的 不同的场所IP是不同的
IP地址特征:
IPV4:点分十进制
0.0 .0 .0
255.255 .255 .255
IPV6:能够给地球上的每一粒沙一个IP地址
IP地址可以跨局域网传输
ps:IP地址可以用来标识全世界独一无二的一台计算机
OSI七层协议之传输层
PORT协议(端口协议)
用来标识一台计算机上面的某一个应用程序
范围:0 -65535
特征:动态分配(类似洗浴中心号码牌)
建议:
0 -1024 系统默认需要使用
1024 -8000 常见软件的端口号
8000 之后的
URL:统一资源定位符(网址)
网址本质是有IP和PORT组成的!!!
IP+PORT:能够定位全世界独一无二的一台计算机上面的某个应用程序
域名解析:将网址解析成IP+PORT
我们之所以不直接使用IP+PORT的原因是太难记 所以发明了域名(网址)
IP:PORT 实际使用冒号连接
eg:114.55 .205 .139 :80
day35——OSI七层协议剩余、socket模块、半链接池
传输层之TCP与UDP协议
TCP与UDP都是用来规定通信方式的
通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊
随心所欲的聊:文字 图片 视频 小油腻话 你侬我侬
遵循一些协议:开头带尊称 首行空两个 只准用官话 不能打情骂俏
ps:不遵循上述协议也可以通信 只不过遵循了更合规合法合理!!!
1. TCP协议(重要)(SOCK_STREAM)
三次握手建链接
1. TCP协议也称为可靠协议(数据不容易丢失)
造成数据不容易丢失的原因不是因为有双向通道 而是因为有反馈机制
给对方发了消息之后会保留一个副本 直到对方回应消息收到了才会删除
否则会在一定的时间内反复发送
2. 洪水攻击
同一时间有大量的客户端请求建立链接 会导致服务端一致处于SYN_RCVD状态
3. 服务端如何区分客户端建立链接的请求
可以对请求做唯一标识
四次挥手断链接
1. 四次不能合并为三次
因为中间需要确认消息是否发完(TIME_WAIT)
"""
三次握手和四次挥手也可以看成是小情侣谈恋爱的过程
三次握手:表白在一起
四次挥手:决裂要分手
"""
ps:课下可以深入研究一下TCP图片上每个状态的具体情况
2. UDP协议
也称为数据报协议、不可靠协议
早期的qq使用的是纯生的(没有加任何额外给你)UDP协议
现在的qq自己添加了很多技术和功能
使用UDP的原因就是因为很简单 快捷 粗暴 只要制定对方的地址就可以发消息了
"""
TCP我们可以看成是打电话:双方你侬我侬
UDP我们可以看成是发短信:只要发了就行 不管对方看不看
"""
三次握手流程图
四次挥手流程图
应用层
应用层相当于是程序员自己写的应用程序 里面的协议非常的多
常见的有:HTTP、HTTPS、FTP
ps:后续框架部分再做介绍
socket模块
如果我们需要编写基于网络进行数据交互的程序 意味着我们需要自己通过代码来控制我们之前所学的OSI七层(很繁琐 很复杂 类似于我们自己编写一个操作系统)
socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口
socket也叫套接字
基于文件类型的套接字家族(单机)
AF_UNIX
基于网络类型的套接字家族(联网)(我们使用的是基于网络的)
AF_INET
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket基本使用
import socket
socket.socket() 产生socket对象
bind() 绑定地址
listen() 半连接池
accept() 等待客户端链接
send() 发送消息
recv() 接收消息
connect() 链接
socket代码简介
import socket
"""
以后要养成查看源码编写代码的思路
"""
server = socket.socket()
server.bind(('127.0.0.1' ,8080 ))
server.listen(5 )
sock,addr = server.accept()
print (sock,addr)
data = sock.recv(1024 )
print (data.decode('utf8' ))
sock.send('尊敬的客人 您说什么就是什么 一切按照您的要求来' .encode('utf8' ))
sock.close()
server.close()
import socket
client = socket.socket
client.connect(('127.0.0.1' ,8080 ))
client.send('大爷有钱 把你们店最好的给我叫出来' .encode('utf8' ))
data = client.recv(1024 )
print (data.decode('utf8' ))
client.close()
代码优化
1. 聊天内容自定义
针对消息采用input 获取
2. 让聊天循环起来
将聊天的部分用循环包起来
3. 用户输入的消息不能为空
本质其实是两边不能都是recv或者send 一定是一放收一方发
4. 服务端多次重启可能会报错
Address already in use 主要是mac电脑会报
方式1 :改端口号
方式2 :博客里面代码拷贝即可
from socket import SOL_SOCKET,SO_REUSEADDR
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1 )
5. 当客户端异常断开的情况下 如果让服务器继续服务其他人
windows服务端会直接报错
mac服务端会有一段时间安抚接收空消息延迟报错
加上异常处理、空消息判断
'''服务端'''
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
server = socket.socket()
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1 )
server.bind(('127.0.0.1' ,8080 ))
server.listen(5 )
while True :
sock, addr = server.accept()
while True :
try :
data = sock.recv(1024 )
if len (data) == 0 :
break
print (f'接收到来自客户端{addr} 的消息>>>:' ,data.decode('utf8' ))
msg = input ('请输入你要发送给客户端的消息(不能发送空消息)>>>:' ).strip()
sock.send(msg.encode('utf8' ))
except BaseException:
break
'''客户端'''
import socket
client = socket.socket()
client.connect(('127.0.0.1' ,8080 ))
while True :
msg = input ('请输入你要发送给服务端的消息>>>:' ).strip()
if len (msg) == 0 :
print ('不能发送空消息' )
continue
client.send(msg.encode('utf8' ))
data = client.recv(1024 )
print ('收到了来自服务端的消息>>>:' ,data.decode('utf8' ))
半连接池的概念
server.listen(5 )
当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发问题)
假设服务端只有一个人的情况下
在测试办理案件池的时候 可以不用input 获取消息 直接把消息写死即可
day36——黏包、UDP协议、进程理论(并发和并行)
黏包现象
1. 服务端连续执行三次recv
2. 客户端连续执行三次sebd
问题:服务端一次性接收到了客户端三次的消息 该现象称为"黏包现象"
黏包现象产生的原因
1. 不知道每次的数据到底多大
2. TCP也称为流式协议:数据像流水原因绵绵不绝没有间隔(TCP会针对数据量较小且发送间隔较短的多条数据一次性合并打包发送)
避免黏包现象的核心思路\关键点
如何明确即将接收的数据具体多大
ps:如何将长度变化的数据全部制作成固定长度的数据
struct模块
import struct
"""
解决黏包问题初次版本
客户端
1.将真实数据转成bytes类型并计算长度
2.利用struct模块将真实长度制作一个固定长度的报头
3.将固定长度的报头先发送给服务端 服务端只需要在recv括号内填写固定长度的报头数字即可
4.然后再发送真实数据
服务端
1.服务端先接收固定长度的报头
2.利用struct模块反向解析出真实数据长度
3.recv接收真实数据长度即可
"""
'''问题1:struct模块无法打包数据量较大的数据 就算换更大的模式也不行'''
'''问题2:报头能否传递更多的信息 比如电影大小 电影名称 电影评价 电影简介'''
'''终极解决方案:字典作为报头打包 效果更好 数字更小'''
"""
黏包问题终极方案
客户端
1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
2.利用struct模块制作字典的报头
3.发送固定长度的报头(解析出来是字典的长度)
4.发送字典数据
5.发送真实数据
服务端
1.接收固定长度的字典报头
2.解析出字典的长度并接收
3.通过字典获取到真实数据的各项信息
4.接收真实数据长度
"""
黏包代码实战
import socket
import struct
import json
server = socket.socket()
server.bind(('127.0.0.1' , 8081 ))
server.listen(5 )
sock, addr = server.accept()
data_dict_head = sock.recv(4 )
data_dict_len = struct.unpack('i' , data_dict_head)[0 ]
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)
'''接收真实数据的时候 如果数据量非常大 recv括号内直接填写该数据量 不太合适 我们可以每次接收一点点 反正知道总长度'''
import socket
import os
import struct
import json
client = socket.socket()
client.connect(('127.0.0.1' , 8081 ))
'''任何文件都是下列思路 图片 视频 文本 ...'''
file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt' )
data_dict = {
'file_name' : '有你好看.txt' ,
'file_size' : file_size,
'file_desc' : '内容很长 准备好吃喝 我觉得营养快线挺好喝' ,
'file_info' : '这是我的私人珍藏'
}
data_dict_bytes = json.dumps(data_dict).encode('utf8' )
data_dict_len = struct.pack('i' , len (data_dict_bytes))
client.send(data_dict_len)
client.send(data_dict_bytes)
with open (r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt' , 'rb' ) as f:
for line in f:
client.send(line)
UDP协议(了解)
1. UDP服务端和客户端'各自玩各自的'
2. UDP不会出现多个消息发送合并
并发编程理论
理论非常多 实战很少 但是一定要好好听
研究网络编程其实就是在研究计算机的底层原理及发展史
"""
计算机中真正干活的是CPU
"""
操作系统发展史
1. 穿孔卡片阶段
计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多时候计算机都不工作
好处:程序员独占计算机 为所欲为
坏处:计算机利用率太低 浪费资源
2. 联机批处理系统
提前使用磁带一次性录入多个程序员编写的程序 然后交给计算机执行
CPU工作效率有所提升 不用反复等待程序录入
3. 脱机批处理系统
极大地提升了CPU的利用率
总结:CPU提升利用率的过程
多道技术
"""
在学校并发编程的过程中
"""
单道技术
所有的程序排队执行 过程中不能重合
多道技术
利用空闲时间提前准备其他数据 最大化提升CPU利用率
多道技术详细
1. 切换
计算机的CPU在两种情况下会切换(不让你用 给别人用)
1. 程序有IO操作
输入\输出操作
input 、time.sleep、read、write
2. 程序长时间占用CPU
我们得雨露均沾 让多个程序都能被CPU运行一下
2. 保存状态
CPU每次切换走之前都要保存当前操作的状态 下次切换回来基于上次的进度继续执行
"""
开了一家饭店 只有一个服务员 但同时来了五桌客人
请问:如何让五桌客人都感觉到服务员在服务他们
让服务员化身为闪电侠 只要客人有停顿 就立刻切换到其他桌 如此往复
"""
单道技术、多道技术流程图
进程理论
进程与程序的区别
程序:一堆死代码(还没有被运行起来)
进程:正在运行的程序(被运行起来了)
进程的调度算法(重要)
1. FCFS(先来先服务)
对短作业不友好
2. 短作业优先调度
对长作业不友好
3. 时间片轮转法+多级反馈队列(目前还在用)
将时间均分 然后根据进程时间长短再分多个等级
等级越靠下表示耗时越长 每次分到的时间越多 但是优先级越低
进程的并发与并行
并行
多个进程同时执行 必须要有多个CPU参与 单个CPU无法实现并行
并发
多个进程看上去像同时执行 单个CPU可以实现 多个CPU肯定也可以
判断下列两句话孰对孰错
我写的程序很牛逼,运行起来之后可以实现14 个亿的并行量
并行量必须要有对等的CPU才可以实现
我写的程序很牛逼,运行起来之后可以实现14 个亿的并发量
合情合理 完全可以实现 以后我们的项目一般都会追求高并发
ps:目前国内可以说是最牛逼的>>>:12306
进程的三状态
就绪态
所有的进程在被CPU执行之前都必须先进入就绪态等待
运行态
CPU正在执行
阻塞态
进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态
进程三状态转换图
day37——同步异步、阻塞非阻塞、进程
同步与异步
用来表达任务的提交方式
同步
提交完任务之后原地等待任务的返回结果 期间不做任何事
异步
提交完任务之后不原地等待任务的返回结果 直接去做其他事 有结果自动通知
阻塞与非阻塞
用来表达任务的执行状态
阻塞
阻塞态
非阻塞
就绪态、运行态
综合使用
同步阻塞
同步非阻塞
异步阻塞
异步非阻塞(*****)
创建进程的多种方式
"""
1.鼠标双击软件图标
2.python代码创建进程
"""
"""
在不同的操作系统中创建进程底层原理不一样
windows
以导入模块的形式创建进程
linux/mac
以拷贝代码的形式创建进程
"""
from multiprocessing import Process
import time
class MyProcess (Process ):
def __init__ (self, name, age ):
super ().__init__()
self.name = name
self.age = age
def run (self ):
print ('run is running' , self.name, self.age)
time.sleep(3 )
print ('run is over' , self.name, self.age)
if __name__ == '__main__' :
obj = MyProcess('jason' , 123 )
obj.start()
print ('主' )
进程间数据隔离
同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)
rom multiprocessing import Process
import time
money = 1000
def task ():
global money
money = 666
print ('子进程的task函数查看money' , money)
if __name__ == '__main__' :
p1 = Process(target=task)
p1.start()
time.sleep(3 )
print (money)
进程join方法
from multiprocessing import Process
import time
def task (name ):
print ('%s is running' % name)
time.sleep(3 )
print ('%s is over' % name)
if __name__ == '__main__' :
p = Process(target=task,args=('jason' ,))
p.start()
'''主进程代码等待子进程代码运行结束再执行'''
p.join()
print ('主' )
IPC机制
IPC:进程间通信
消息队列:存储数据的地方 所有人都可以存 也都可以取
from multiprocessing import Queue
q = Queue(3 )
q.put(111 )
q.put(222 )
q.put(333 )
print (q.get())
print (q.get())
print (q.get())
print (q.get_nowait())
"""
full() empty() 在多进程中都不能使用!!!
"""
from multiprocessing import Process, Queue
def product (q ):
q.put('子进程p添加的数据' )
def consumer (q ):
print ('子进程获取队列中的数据' , q.get())
if __name__ == '__main__' :
q = Queue()
p1 = Process(target=consumer, args=(q,))
p2 = Process(target=product, args=(q,))
p1.start()
p2.start()
print ('主' )
生产者消费者模型
"""回想爬虫"""
生产者
负责产生数据的'人'
消费者
负责处理数据的'人'
该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)
进程对象的多种方法
1. 如何查看进程号
from multiprocessing import Process,current_process
current_process()
current_process().pid
import os
os.getpid()
os.getppid()
2. 终止进程
p1.terminate()
ps:计算机操作系统都有对应的命令可以直接杀死进程
3. 判断进程是否存活
p1.is_alive()
4. start()
5.j oin()
守护进程
守护进程会随着守护的进程结束而立刻结束
eg:小狗是小猫的守护进程 一旦小猫嗝屁了 小狗立刻嗝屁
from multiprocessing import Process
import time
def task (name ):
print ('德邦总管:%s' % name)
time.sleep(3 )
print ('德邦总管:%s' % name)
if __name__ == '__main__' :
p1 = Process(target=task,args=('xxx' ,))
p1.daemon = True
p1.start()
print ('恕瑞玛皇帝嗝屁了' )
僵尸进程与孤儿进程
僵尸进程
进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来
比如进程号、进程执行时间、进程消耗功率等给父进程查看
ps:所有的进程都会变成僵尸进程>>>True
孤儿进程
子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理
多进程数据错乱问题
模拟抢票软件
from multiprocessing import Process
import time
import json
import random
def search (name ):
with open ('data.json' ,'r' ,encoding='utf8' ) as f:
data = json.load(f)
print ('%s正在查票 当前票数为%s' % (name,data['ticket_num' ]))
def buy (name ):
with open ('data.json' ,'r' ,encoding='utf8' ) as f:
data = json.load(f)
time.sleep(random.randint(1 ,3 ))
if data.get('ticket_num' ) > 0 :
data['ticket_num' ] -= 1
with open ('data.json' ,'w' ,encoding='utf8' ) as f:
json.dump(data,f)
print ('%s买票成功' % name)
else :
print ('%s很倒霉 没有抢到票' % name)
def run (name ):
search(name)
buy(name)
if __name__ == '__main__' :
for i in range (10 ):
p = Process(target=run,args=('用户%s' % i,))
p.start()
"""
多进程操作数据很可能会造成数据错乱>>>:互斥锁
互斥锁
将并发变成串行 牺牲了效率但是保障了数据的安全
"""
day38——线程、GIL锁、死锁、进程池与线程池、协程
多进程实现TCP服务端并发
import socket
from multiprocessing import Process
def get_server ():
server = socket.socket()
server.bind(('127.0.0.1' ,8080 ))
server.listen(5 )
return server
def get_tall (sock ):
while True :
data = sock.recv(1024 )
print (data.decode('utf8' ))
sock.send(data.upper())
if __name__ == '__main__' :
server = get_server()
while True :
sock,addr = server.accept()
p = Process(target=get_tall,args=(sock,))
p.start()
互斥锁代码实操
锁:建议只加载操作数据的部分 否则整个程序的效率会极低
from multiprocessing import Process, Lock
import time
import json
import random
def search (name ):
with open (r'data.json' , 'r' , encoding='utf8' ) as f:
data = json.load(f)
print ('%s查看票 目前剩余:%s' % (name, data.get('ticket_num' )))
def buy (name ):
with open (r'data.json' , 'r' , encoding='utf8' ) as f:
data = json.load(f)
time.sleep(random.randint(1 , 3 ))
if data.get('ticket_num' ) > 0 :
with open (r'data.json' , 'w' , encoding='utf8' ) as f:
data['ticket_num' ] -= 1
json.dump(data, f)
print ('%s 买票成功' % name)
else :
print ('%s 买票失败 非常可怜 没车回去了!!!' % name)
def run (name, mutex ):
search(name)
mutex.acquire()
buy(name)
mutex.release()
线程理论
进程
进程其实是资源单位 表示一块内存空间
线程
线程才是执行单位 表示真正的代码指令
我们可以将进程比喻是车间 线程是车间里面的流水线
一个进程内部至少含有一个线程
1. 一个进程内可以开设多个线程
2. 同一个进程下的多个线程数据是共享的
3. 创建进程与线程的区别
创建进程的消耗要远远大于线程
创建线程的两种方式
from threading import Thread
import time
def task ():
print ('task is running' )
time.sleep(1 )
print ('task is over' )
if __name__ == '__main__' :
start_time = time.time()
t_list = []
for i in range (100 ):
t = Thread(target=task)
t.start()
t_list.append(t)
for j in t_list:
j.join()
print (time.time() - start_time)
"""
创建线程无需考虑反复执行的问题
"""
class MyThread (Thread ):
def run (self ):
print ('run is running' )
time.sleep(1 )
print ('run is over' )
if __name__ == '__main__' :
obj = MyThread()
obj.start()
print ('主线程' )
线程的诸多方法
1.j oin方法
2. 同进程内多个线程数据共享
3. current_thread()
4. active_count()
GIL全局解释器锁
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.
"""
1.在python解释器中存在全局解释器锁简称GIL
python解释器有很多类型
CPython JPython PyPython(常用的是CPython解释器)
2.GIL本质也是一把互斥锁 用来阻止同一个进程内多个线程同时执行(重要)
3.GIL的存在是因为CPython解释器中内存管理不是线程安全的(垃圾回收机制)
垃圾回收机制
引用计数、标记清除、分代回收
"""
1. python有GIL锁的原因,同一个进程下多个线程实际上同一时刻,只有一个线程在执行
2. 只有在python上开进程用的多,其他语言一般不开多进程,只开多线程就够了
3. cpython解释器开多线程不能利用多核优势,只有开多进程才能利用多核优势,其他语言不存在这个问题
4. 8 核cpu电脑,充分利用起我这个8 核,至少起8 个线程,8 条线程全是计算--->计算机cpu使用率是100 %,
5. 如果不存在GIL锁,一个进程下,开启8 个线程,它就能够充分利用cpu资源,跑满cpu
6. cpython解释器中好多代码,模块都是基于GIL锁机制写起来的,改不了了---》我们不能有8 个核,但我现在只能用1 核,----》开启多进程---》每个进程下开启的线程,可以被多个cpu调度执行
7. cpython解释器:io密集型使用多线程,计算密集型使用多进程
验证GIL的存在
from threading import Thread
num = 100
def task ():
global num
num -= 1
t_list = []
for i in range (100 ):
t = Thread(target=task)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print (num)
GIL与普通互斥锁
既然CPython解释器中有GIL 那么我们以后写代码是不是就不需要操作锁了!!!
"""
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱
并不能确保程序里面的数据是否安全
"""
import time
from threading import Thread,Lock
num = 100
def task (mutex ):
global num
mutex.acquire()
count = num
time.sleep(0.1 )
num = count - 1
mutex.release()
mutex = Lock()
t_list = []
for i in range (100 ):
t = Thread(target=task,args=(mutex,))
t.start()
t_list.append(t)
for t in t_list:
t.join()
print (num)
python多线程是否有用
需要分情况
情况1
单个CPU
多个CPU
情况2
IO密集型(代码有IO操作)
计算密集型(代码没有IO)
1. 单个CPU
IO密集型
多进程
申请额外的空间 消耗更多的资源
多线程
消耗资源相对较少 通过多道技术
ps:多线程有优势!!!
计算密集型
多进程
申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)
多线程
消耗资源相对较少 通过多道技术(总耗时+切换)
ps:多线程有优势!!!
2. 多个CPU
IO密集型
多进程
总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
多线程
总耗时(单个进程的耗时+IO)
ps:多线程有优势!!!
计算密集型
多进程
总耗时(单个进程的耗时)
多线程
总耗时(多个进程的综合)
ps:多进程完胜!!!
from threading import Thread
from multiprocessing import Process
import os
import time
def work ():
res = 1
for i in range (1 , 100000 ):
res *= i
if __name__ == '__main__' :
start_time = time.time()
t_list = []
for i in range (12 ):
t = Thread(target=work)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print ('总耗时:%s' % (time.time() - start_time))
"""
计算密集型
多进程:5.665567398071289
多线程:30.233906745910645
"""
def work ():
time.sleep(2 )
if __name__ == '__main__' :
start_time = time.time()
p_list = []
for i in range (100 ):
p = Process(target=work)
p.start()
for p in p_list:
p.join()
print ('总耗时:%s' % (time.time() - start_time))
"""
IO密集型
多线程:0.0149583816528320
多进程:0.6402878761291504
"""
死锁现象
acquire()
release()
from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()
class MyThread (Thread ):
def run (self ):
self.func1()
self.func2()
def func1 (self ):
mutexA.acquire()
print (f'{self.name} 抢到了A锁' )
mutexB.acquire()
print (f'{self.name} 抢到了B锁' )
mutexB.release()
print (f'{self.name} 释放了B锁' )
mutexA.release()
print (f'{self.name} 释放了A锁' )
def func2 (self ):
mutexB.acquire()
print (f'{self.name} 抢到了B锁' )
time.sleep(1 )
mutexA.acquire()
print (f'{self.name} 抢到了A锁' )
mutexA.release()
print (f'{self.name} 释放了A锁' )
mutexB.release()
print (f'{self.name} 释放了B锁' )
for i in range (10 ):
obj = MyThread()
obj.start()
信号量
在python并发编程中信号量相当于多把互斥锁(公共厕所)
from threading import Thread, Lock, Semaphore
import time
import random
sp = Semaphore(5 )
class MyThread (Thread ):
def run (self ):
sp.acquire()
print (self.name)
time.sleep(random.randint(1 , 3 ))
sp.release()
for i in range (20 ):
t = MyThread()
t.start()
event事件
from threading import Thread, Event
import time
event = Event()
def light ():
print ('红灯亮着的 所有人都不能动' )
time.sleep(3 )
print ('绿灯亮了 油门踩到底 给我冲!!!' )
event.set ()
def car (name ):
print ('%s正在等红灯' % name)
event.wait()
print ('%s加油门 飙车了' % name)
t = Thread(target=light)
t.start()
for i in range (20 ):
t = Thread(target=car, args=('熊猫PRO%s' % i,))
t.start()
进程池与线程池
进程和线程能否无限制的创建 不可以
因为硬件的发展赶不上软件 有物理极限 如果我们在编写代码的过程中无限制的创建进程或者线程可能会导致计算机奔溃
'''进程池和线程池都是提供的是异步调用,只需要往池子里丢任务,我们不需要等待结果,等池子里面的任务完成之后,内部做了一个回调,告诉我们执行后的结果!!!'''
池
降低程序的执行效率 但是保证了计算机硬件的安全
进程池
提前创建好固定数量的进程供后续程序的调用 超出则等待
线程池
提前创建好固定数量的线程供后续程序的调用 超出则等待
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread
pool = ProcessPoolExecutor(5 )
def task (n ):
print ('task is running' )
return '我是task函数的返回值'
def func (*args, **kwargs ):
print ('from func' )
if __name__ == '__main__' :
for i in range (20 ):
线程池爬取网页
import requests
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def get_page (url ):
res = requests.get(url)
name = url.rsplit('/' )[-1 ] + '.html'
return {'name' : name, 'text' : res.content}
def call_back (fut ):
print (fut.result()['name' ])
with open (fut.result()['name' ], 'wb' ) as f:
f.write(fut.result()['text' ])
if __name__ == '__main__' :
pool = ThreadPoolExecutor(5 )
urls = ['http://www.baidu.com' , 'http://www.cnblogs.com' , 'http://www.taobao.com' ]
for url in urls:
pool.submit(get_page, url).add_done_callback(call_back)
协程
"""
进程:资源单位
线程:执行单位
协程:单线程下实现并发(效率极高)
在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作
实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
(该技术完全是程序员自己弄出来的 名字也是程序员自己起的)
核心:自己写代码完成切换+保存状态
"""
import time
from gevent import monkey;
monkey.patch_all()
from gevent import spawn
def func1 ():
print ('func1 running' )
time.sleep(3 )
print ('func1 over' )
def func2 ():
print ('func2 running' )
time.sleep(5 )
print ('func2 over' )
if __name__ == '__main__' :
start_time = time.time()
s1 = spawn(func1)
s2 = spawn(func2)
s1.join()
s2.join()
print (time.time() - start_time)
协程实现并发
import socket
from gevent import monkey;monkey.patch_all()
from gevent import spawn
def communication (sock ):
while True :
data = sock.recv(1024 )
print (data.decode('utf8' ))
sock.send(data.upper())
def get_server ():
server = socket.socket()
server.bind(('127.0.0.1' , 8080 ))
server.listen(5 )
while True :
sock, addr = server.accept()
spawn(communication, sock)
s1 = spawn(get_server)
s1.join()
如何不断的提升程序的运行效率
多进程下开多线程 多线程下开协程
协程实现高并发
服务端
import socket
'''猴子补丁'''
from gevent import monkey;
monkey.patch_all()
'''协程实现并发:其实是欺骗CPU的行为'''
'''并发:1, 遇到IO操作,会切换'''
from gevent import spawn
def talk (sock ):
while True :
try :
data = sock.recv(1024 )
if len (data) == 0 : break
print (data)
sock.send(data + b'ly is handsome!' )
except ConnectionResetError as e:
print (e)
sock.close()
break
from multiprocessing import Process
def servers ():
server = socket.socket()
server.bind(('127.0.0.1' , 8080 ))
server.listen()
while True :
sock, addr = server.accept()
spawn(talk, sock)
'''协程是单线程下的并发!!!'''
g1 = spawn(servers)
g1.join()
客户端
from threading import Thread, current_thread
from socket import *
def client ():
client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1' , 8080 ))
n = 0
while True :
msg = '%s say hello %s' % (current_thread().name, n)
n += 1
client.send(msg.encode('utf-8' ))
data = client.recv(1024 )
print (data.decode('utf-8' ))
if __name__ == '__main__' :
for i in range (5000 ):
t = Thread(target=client)
t.start()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)