Python基础

day01

计算机内部存储数据的原理

"""计算机内部只认识01二进制"""
是因为计算机是基于电工作的,而电是有高低电频之分
00000001   0000000100000001
# 存储单位
比特位(bit) 
8位= 1bytes(字节)
1024bytes = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
...

编程语言的发展史

机器语言:内部使用01二进制表示
	# 计算机直接能够识别的
    优势:执行速度快
    劣势:学习难度大
汇编语言
	# 用简单的字母代表一串二进制
    优势:执行速度快
    劣势:学习难度大
高级语言
	Python C# C++ Java Go
	# 人类能够读懂的语言
    优势:学习难度大大降低,编程效率大大提高
    劣势:执行速度相比较慢了
'''技术是服务产品的'''

# 高级语言的存在必须要有翻译官的存在

编程语言的分类

1. 编译型语言   # C++
	类似于谷歌翻译,翻译一次永久使用
    优点:方便
    劣势:不方便修改bug
2. 解释型语言   # python
	类似于同声传译 一次翻译一句
    劣势:执行效率低
    
'''
	问:如何让我的计算机能够识别编译型语言或者解释型语言?
		1. 识别C++语言
			安装C++编译器
		2. 识别python语言
			安装python解释器
'''

python解释器版本

'''在IT软件行业不能轻易的升级软件,尤其是跟程序相关的升级更新'''

1. 创作者:龟叔
2. 版本
	python1.X:最原始的版本,我们可以忽略
    python2.x:最高2.7版本
    	# 还有不少企业老项目在使用2.7版本
    python3.x
    	# 目前比较稳定的版本是3.6(推荐)
        '''学习阶段可以使用3.6以上的版本都行'''
        # 我们使用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: 洋哥在中国上海浦东周浦澄天伟业5512

环境变量(重点)

# 每个同学必须掌握

# 其实就是一个公共的仓库
'''原理: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软件的基本使用

# 1. 下载与安装
# 2. pycharm的语言,一定要使用英文的

# 修改主题背景
File
	settings
    		Apperance
        			Theme
# 修改字体大小
File
	settings
    	Editor
        	Font
# 切换解释器(必会!!!)
File
	settings
    	Project
        	Interpreter
# 如何新建python文件
	'''后缀名是让我们看出文件的本质,所以,我们python也有自己独特的后缀名.py'''
	1. 如果没有项目,我们应该先新建一个项目,其实就是新建一个文件夹
# 如何运行python文件

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. 驼峰体
	# 1.1 大驼峰
    	UserNameFromDb  # 按照单词进行分割,每个单词的首字母大写,其余小写
    # 1.2 小驼峰
    	userNameFromDb  # 按照单词进行分割,只有第一个单词的首字母是小写的,其余单词的首字母大写
2. 下划线
	user_name_from_db   # 全部使用小写字母,每个单词使用下划线进行分割
    """python中推荐使用下划线的形式"""

常量

# 一般不变的量

'''python中没有真正意义上的常量,我们约定俗成认为全大写的变量就是常量'''
eg: HOST='127.0.0.1'

# 在其他语言中,是有真正意义上的常量的,eg:JavaScript
	const PI=3.14

变量的三要素

name = 'tony'

1. 变量值
2. 内存地址编号
3. 数据类型

"""
	一个变量名只能指向一个内存地址
	一个内存地址可以有多个变量指向
"""

python底层优化

'''小整数池'''
# 当数据量很小的时候,如果同时有多个变量使用,那么python就会让多个变量指向同一个内存地址

垃圾回收机制

1. 什么是垃圾数据?
	# 就是没有变量指向的数据
2. python开发出了一套自动回收方案
	1. 引用计数
    	# 当有变量指向内存中得数据时候,会同时加一个计数,当计数为零的时候,就说明是垃圾数据
    2. 标记清楚
    	# 当内存快被沾满的时候,那么python就会终止程序的运行,从头到位进行扫描数据,对垃圾数据进行标记,之后,统一进行清楚
    3. 分代回收
    	# 通过分不同的时间段,来降低被监管的频率

数据类型(重点)

# 生活中常见的数据类型
视频文件  音频文件 表格 图像 图片  文本...

# 在程序中也有千变万化的数据格式
整型  浮点型  字符串 列表 元组 集合 字段...

整型int

# 大白话:整数
作用:记录年龄、身高、电话号码
eg:
    age = 18

浮点型float

# 大白话:小数
作用:记录薪资、bmi
eg:
	salary = 100.1
	a = 1.0

总结

# 我们把整型和浮点型统称为数字类型

day04——数据类型

字符串str

# 用来记录描述性信息 eg:姓名 地址 邮箱...

定义:
	方式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']]]

# 1. kevinNB在大列表的第几个位置
l1 = l[3]  # [666, 777, 888, 'tony', [22, 33, 'kevinNB']]

# 2. kevinNB在大列表的第几个位置
l2 = l1[4] # [22, 33, 'kevinNB']

# 3. kevinNB在大列表的第几个位置
l3 = l2[2]  # kevinNB
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'}
# 字典不能通过索引取值,通过字典的K取值
print(d['username'])
print(d['age'])
print(d['hobby'])


# 小练习
info = {
    'username':'tony',
    'addr':{
        '国家':'中国',
        'info':[666, 999, {'编号':466722, 'hobby':['read', 'study', 'music']}]
    }
}

# 1. music在大字典里的位置
d1 = info['addr'] # {'国家': '中国', 'info': [666, 999, {'编号': 466722, 'hobby': ['read', 'study', 'music']}]}

# 2. music在小字典里的位置
d2 = d1['info']  # [666, 999, {'编号': 466722, 'hobby': ['read', 'study', 'music']}]

# 3. music在列表里的位置
d3 = d2[2]  # {'编号': 466722, 'hobby': ['read', 'study', 'music']}

# 4. music在小字典里的位置
d4 = d3['hobby']  # ['read', 'study', 'music']

# 5. music在列表里的位置
d5 = d4[2] # music
print(d5)

# 整合
d6 = info['addr']['info'][2]['hobby'][2]
print(d6)

布尔值bool

# 判断是否正确
'''只有两种情况'''
True   对  正确的  可行的...
False  错误  不可行的

# 布尔值的变量命名规范:以is开头
is_right
is_delete
...
'''结果可能是布尔值的情况,我们都采用is开头命名'''
# 扩展
# 补充
'''在python中,所有的数据类型都可以转为布尔值'''
# 记忆
在python中,什么是真,什么是假?
	哪些是假的情况:
    	0, None, '', [], {}...
    其他都是真

元组tuple

# 作用跟列表一样,它是不可变的列表
# 定义:小括号括起来,内部可以存放多个元素,元素与元素之间使用逗号隔开,元素不能更改
	t1 = (11, 22, 33, 44)

集合set

# 特点:无序 去重 集合运算
# 定义:大括号括起来,内部可以存放多个元素,元素与元素之间逗号隔开,但是不是K:V键值对的形式
	s = {11,22,33,44}
  '''集合中得元素不能直接取出'''

与用户进行交互

# 输入input

password = input('请输入你的密码:>>>')
print(password, type(password)) # input接受的所有数据类型都是str类型
# print(password == '123')  # python中==比较的是数据值和数据类型
print(int(password) == 123)  # python中==比较的是数据值和数据类型
print(password == str(123))  # python中==比较的是数据值和数据类型

# 输出print
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)

基本运算符

# 菜鸟教程:https://www.runoob.com/python/python-operators.html

# 算术运算符
% ** //

'''补充'''
# python中拼接字符串使用 +
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, ll2, ll3, ll4 = names_list
# ll1, ll2, ll3, ll4 = ['kevin', 'tony', 'tank', 'tom']

# 了解
ll1, *a, ll3, ll4 = ['kevin', 'tony', 'tank','a','b', 'tom']

# print(l1, l2, l3, l4)
print(ll1, a, ll4)

day05——运算符、if、while循环

逻辑运算符

and# 
# 可以用and连接多个条件,会按照从左到右的顺序依次判断,一旦某一个条件为False,则无需再往右判断,可以立即判定最终结果就为False,只有在所有条件的结果都为True的情况下,最终结果才为True。

# 练习题:
	 2 > 3 and 1 != 1 and True and 3 > 2
or# 可以用or连接多个条件,会按照从左到右的顺序依次判断,一旦某一个条件为True,则无需再往右判断,可以立即判定最终结果就为True,只有在所有条件的结果都为False的情况下,最终结果才为False
# 练习题:
	 2 > 1 or 1 != 1 or True or 3 > 2 
not  非(取反)
	not True

成员运算符

# 判断某个个体是否在某个群体中
符号:in(在)  not in(不在)
name_list = ['kevin', 'jack', 'tony', 'tom']

# print('kevin' in name_list)  # True
# print('aaa' not in name_list)
# print('bbb' in name_list)

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:遇到冒号就要回车换行,缩进
"""

# 1. 单if判断
关键字:if
"""
	语法格式:
		if 判断条件:
    		print('小姐姐好')
"""

# 2. 双分支结构
	"""
		语法格式:
			if 判断条件:
                条件成立执行的子代码块
            else:
                条件不成立执行的子代码块
	"""
# 3. 多分支结构
	"""
		语法格式:
			if 条件1:
                条件1成立执行的子代码块
            elif 条件2:
                条件1不成立条件2成立执行的子代码块
            elif 条件3:
                条件1、2不成立条件3成立执行的子代码块
            elif 条件4:
                条件1、2、3不成立条件4成立执行的子代码块
            else:
            	以上条件都不成立的时候执行的代码块
	"""
    
    # 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

# count = 0
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. 拔高要求
		三次之后再次询问是否继续输入
"""
# 三次机会,猜对了,就直接结束
# 先写主体功能

# 1. 先定义一个年龄出来
real_age = 18

# 定义一个计数器
count = 1
while count < 4:
    # 2. 用户输入年龄
    guess_age = input('请输入你猜测的年龄:>>>')  # 数字
    # 2.1 把字符串的数字转为整型,int,可能会出现bug
    guess_age = int(guess_age)
    
    # 3. 判断输入的年龄和真实年龄是否一样
    if guess_age > real_age:
        print('猜大了')
        # count+=1
    elif guess_age < real_age:
        print('猜小了')
        # count+=1
    else:
        print('哎呀,猜对了')
        break
    # 优化
    count+=1
    
'''拔高'''

# 1. 先定义一个年龄出来
real_age = 18

# 定义一个计数器
count = 1
while count < 4:

    # 2. 用户输入年龄
    guess_age = input('请输入你猜测的年龄:>>>')  # 数字
    # 2.1 把字符串的数字转为整型,int,可能会出现bug
    guess_age = int(guess_age)

    # 3. 判断输入的年龄和真实年龄是否一样
    if guess_age > real_age:
        print('猜大了')
        # count+=1
    elif guess_age < real_age:
        print('猜小了')
        # count+=1
    else:
        print('哎呀,猜对了')
        break
    # 优化
    count+=1
    if count == 4:
        # 询问:是否再次输入
        cmd = input('三次机会使用完了,是否再次输入yes/no')
        if cmd == 'yes':
            count=1
        else:
            print('结束')
            break

while+continue

# 1. 使用循环打印出0-9的数字

# 2. 打印0-10的数字不打印6
count=0
while count < 11:
    if count ==3:
        count+=1
        continue #
    print(count)
    count+=1
    
'''continue结束本次循环,并且回到while循环的条件处从新判断'''

while+else(了解)

# count=0
# while count < 10:
#     print(count)
#     count+=1
# else:
#     print('哈哈哈哈')

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循环

# 1. 使用while循环打印以下列表中得数据
name_list = ['kevin', 'tony', 'jack', 'tom']

# i=0
# while i<4:
#     print(name_list[i])
#     i+=1

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循环使用
第一种玩法:
for i in range(10) # 一个参数, 打印数字列表,从0开始
	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))  # 0b1010
print(oct(10))  # 0o12
print(hex(10))  # 0xa

# 记忆:0b代表二进制  0o代表八进制  0x代表十六进制

print(int('0b1010', 2))
print(int('0o12', 8))
print(int('0xa', 16))
2. 
# float同样可以用来做数据类型的转换
>>> s = '12.3'
>>> res=float(s)
>>> res,type(res)
(12.3, <class 'float'>)

字符串的内置方法(重点)

# 1. 把其他数据类型转为字符串
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}))

# 记忆
# s = '  hello  '
# print(s)
# print(s.strip())  # 默认什么都不写,去除的是空格
# print(s.lstrip()) # left
# print(s.rstrip()) # right

# s1 = '@@kevin@##'
# print(s1.strip('@'))
# print(s1.strip('#'))


username = input('username:>>>').strip()
password = input('password:>>>').strip()
if username == 'kevin' and password == '123':
    print('登录成功')
else:
    print('登录失败')

    >>> str1 = 'hello python!'


# 1.按索引取值(正向取,反向取):
# 1.1 正向取(从左往右)
>>> str1[6]
p
# 1.2 反向取(负号表示从右往左)
>>> str1[-4]
h
# 1.3 对于str来说,只能按照索引取值,不能改
>>> str1[0]='H' # 报错TypeError


# 2.切片(顾头不顾尾,步长)
# 2.1 顾头不顾尾:取出索引为0到8的所有字符
>>> str1[0:9]  
hello pyt
# 2.2 步长:0:9:2,第三个参数2代表步长,会从0开始,每次累加一个2即可,所以会取出索引0、2、4、6、8的字符
>>> str1[0:9:2]  
hlopt 
# 2.3 反向切片
>>> str1[::-1]  # -1表示从右往左依次取值
!nohtyp olleh

# 3.长度len
# 3.1 获取字符串的长度,即字符的个数,但凡存在于引号内的都算作字符)
>>> len(str1) # 空格也算字符
13

# 4.成员运算 in 和 not in	
# 4.1 int:判断hello 是否在 str1里面
>>> 'hello' in str1  
True
# 4.2 not in:判断tony 是否不在 str1里面
>>> 'tony' not in str1 
True

# 5.strip移除字符串首尾指定的字符(默认移除空格)
# 5.1 括号内不指定字符,默认移除首尾空白字符(空格、\n、\t)
>>> str1 = '  life is short!  '
>>> str1.strip()  
life is short!

# 5.2 括号内指定字符,移除首尾指定的字符
>>> str2 = '**tony**'  
>>> str2.strip('*')  
tony

# 6.切分split
# 6.1 括号内不指定字符,默认以空格作为切分符号
>>> str3='hello world'
>>> str3.split()
['hello', 'world']
# 6.2 括号内指定分隔字符,则按照括号内指定的字符切割字符串
>>> str4 = '127.0.0.1'
>>> str4.split('.')  
['127', '0', '0', '1']  # 注意:split切割得到的结果是列表数据类型


# 7.循环
>>> str5 = '今天你好吗?'
>>> for line in str5:  # 依次取出字符串中每一个字符
...     print(line)
...
今
天
你
好
吗
?

作业

1. 整理今日内容
2. 熟悉今天上午讲的练习题
3. 预习下周内容

day07——内置方法

练习题讲解

温馨提示:
 ⽤户名与密码源于字符串 source_data='kevin|123'
 想办法从中拆分出⽤户名和密码⽤于后续账户信息⽐对
 普通要求:
 	1. 验证失败情况下可⼀直循环验证,成功则直接退出
 拔⾼练习:
     1. 只允许三次失败机会
     2. 登录成功后进⼊内层循环,⽤户输⼊任何指令利⽤格式化
输出
 	打印正在执⾏该⽤户指令即可,直到⽤户输⼊字⺟q退出内层
循环

字符串的内置方法(重要)

# 1. lower upper
res = 'keViN123 oldBoY'
print(res.lower())  # kevin123 oldboy
print(res.upper())  # KEVIN123 OLDBOY

"""
    图片验证码不区分大小写
        思路:把用户输入的验证码全部转为大写或者小写,跟原来的验证码都转为大写或者小写进行比较
"""
# old_code = 'oldBoY'
# print('返回给你的验证码是:%s' % old_code)
# # 用户输入的验证码
# real_code=input('请输入验证码:')
# # if old_code.lower() == real_code.lower():
# if old_code.upper() == real_code.upper():
#     print('验证码输入正确')
# else:
#     print('验证码错误')

# 补充:
print(res.islower())  # False
print(res.isupper())  # False

a ='hello'
print(a.islower())  # True


res = 'Kevin123 OldGirl'
print(res.startswith('K'))  # True
print(res.startswith('kev'))  # True
print(res.startswith('Keva'))  # True

print(res.endswith('Girl'))
print(res.endswith('rl'))


# 格式化输出:%s %d
"""format方法格式化"""
'''第一种玩法'''
# s = 'my name is %s, my age is %s' % ('kevin', 18)
# s = 'my name is {}, my age is {},{}{}{}'
# print(s.format('kevin', 20, 'a', 'b', 'c'))  # my name is kevin, my age is 20

'''第二种玩法'''
# s = '{0}my name is {1}, my age is {0}{0}{0}{0}{0}, {2}'
# print(s.format('tony', 22, 'helloworld'))

'''第三种玩法'''
s = '{age}my name is {name}, my age is {age}{name}{name}{name}{name}{name}{name}'
print(s.format(name='kevin', age=20))

# join的用法
l = ['tony', 'kevin', 'jack', 'tom']
# |
print(l[0] + '|' + l[1] + '|' + l[2] + '|' + l[3]) # tony|kevin|jack|tom
print('|'.join(l))

# replace替换字符串
s = 'my name is kevin kevin kevin kevin'
print(s.replace('kevin', 'jack', 2))

s = '123'
# print(s.isdigit())
guess_age = input('请输入你的年龄:')
if guess_age.isdigit():
    if int(guess_age) == 18:
        print('猜对了')
else:
    print('你输入的年龄不合法')

字符串需要了解的方法

'''字符串内置方法需要了解的'''

msg='tony say hello hello hello'
# print(msg.find('s'))  # 5
# print(msg.find('hello'))  # 9
# print(msg.find('helloa'))  # -1

'''find:是用来判断一个子字符串是否在另外一个字符串中'''
# in

# print(msg.index('s')) # 5
# print(msg.index('hello')) # 5a
# print(msg.index('helloa'))  # 如果查不到字符串直接报错
# print(msg.count('hello'))  # 3
# print(msg.count('helloa'))  # 0

# res = 'kevin'
# print(res.center(16, '@'))  # 居中展示
# print(res.ljust(16, '@'))  # 左对齐
# print(res.rjust(16, '@'))  # 右对齐

# 4.captalize,swapcase,title
res = 'my name is kevin'
print(res.title())  # My Name Is Kevin
print(res.capitalize())  # My name is kevin
print(res.swapcase())  # MY NAME IS KEVIN

列表的内置方法(重点)

1. 类型转换
# print(list(123))  不可
# print(list(1.11)) 不可
print(list('hello')) # ['h', 'e', 'l', 'l', 'o']
print(list([11, 22, 33, 44])) # [11, 22, 33, 44]
print((list({'username':"kevin", "age":20})))  # ['username', 'age']
print(list((11, 22, 33, 44)))  # [11, 22, 33, 44]
print(list({11, 22, 33, 44}))  # [33, 11, 44, 22]

2. 列表的增加和修改
# my_friends = ['tony', 'jason', 'tom', 4, 5]
# print(my_friends[0])
# 1. 修改值
# my_friends[0] = 'jack'
# print(my_friends)

'''列表的最大索引长度:len(l)-1'''
# 2. 增加值:第一种方式
# my_friends[5] = 666 # 不能这样增加
# my_friends.append(666)
# my_friends.append(777)
# my_friends.append(888)  # 追加到末尾位置
# print(my_friends)

# 增加值:第二种方式
# my_friends.insert(0, 999)
# my_friends.insert(1, 999)
# print(my_friends)


# 增加值:第三种方式
# my_friends.append([11, 22, 33, 44])  # 把列表的整体添加到末尾
# my_friends.extend([11, 22, 33, 44])    # 列表的合并
# print(my_friends)

# l = ['kevin', 'jack', 'tom']
# l1 = ['a', 'b', 'c', 'd']
# l.extend(l1)  # for循环+append
# print(l)

# for i in l1:
#     l.append(i)
#
# print(l)

my_friends = ['tony', 'jason', 'tom', 4, 5, 6, 7, 8]
print(my_friends[0:3])  # (顾头不顾尾,步长)
print(my_friends[0:5:2])  # (顾头不顾尾,步长)

#  3.长度
print(len(my_friends))

列表的删除

'''删除'''
# del # delete
# 第一种方式
# del my_friends[0]  # ['jason', 'tom', 4, 5, 6, 7, 8]
# del my_friends[0]
# del my_friends[4]
# print(my_friends)

# 第二种方式
my_friends = ['tony', 'jason', 'tom', 4, 5, 6, 7, 8]

# aa = my_friends.remove('jason')  # 括号中直接写删除的元素。可不是索引
# print(aa) # None 空
# print(my_friends)

# 第三种方式
# my_friends.pop()  # 弹出元素
# my_friends.pop()
# my_friends.pop()
# aa = my_friends.pop()
# print(aa)
s = my_friends.pop(1)  # 用的也比较多
print(s)
print(my_friends)

可变类型与不可变类型

"""
	可变类型:列表
		值改变,内存地址没有改变
	不可变类型:整型 浮点型 字符串
		值改变,内存地址也改变
"""

作业

# 数据结构
1. 队列:先进来的先出去,先进先出
2. 栈:先进来的后出来,先进后出

'''使用列表的内置方法实现以上两种数据结构'''

day08——列表剩余、字典、元组、集合内置方法

练习题讲解

# 1. 实现队列的数据结构
# 1.1 定义一个空列表
# l = []
# 1.2 先进去
# l.append(11)
# l.append(22)
# l.append(33)
# l.append(44)
# print(l)

# 1.3 先出来
# print(l.pop(0))
# print(l.pop(0))
# print(l.pop(0))
# print(l.pop(0))

'''栈'''
# 2.1 先定义一个空列表
stack = []

# 2.2 先进去
stack.append(666)
stack.append(777)
stack.append(888)
print(stack)
# 2.3 后出来
print(stack.pop())
print(stack.pop())
print(stack.pop())

列表的剩余部分

# l = [11, 22, 33, 44]

# l.reverse()  # [44, 33, 22, 11]
# print(l[::-1])
# print(l)



l = [11,22,3,42,7,55]
# 列表排序
# l.sort()  # [3, 7, 11, 22, 42, 55]  # 从小到大排序 (前提是:纯数字比较)
# l.sort(reverse=True)  # [55, 42, 22, 11, 7, 3]  # 从大到小排序 (前提是:纯数字比较)
# print(l)


# 了解的知识
# 列表比较大小
# l1 = [666,777]
# l2 = [111, 222, 333, 444 ,555, 666]
# print(l1 > l2)  # True  列表的比较是比较索引对应位置的元素值,只要有一个比较出来,都不在往下比较


s1 = 'hello'
s2 = 'world'
print(s1 > s2) # 字符之间的大小取决于它们

字典的内置方法dict

'''
	大括号括起来,内部存放多个元素,元素之间逗号隔开,K:V键值对的形式
		k: 是对V的描述性信息,一般是字符串类型和不可变类型(整数,字符串)
		V: 才是真正的数据值,其实就是变量值,可以是任意的数据类型
'''
# 定义:
	dic = {'username':'kevin', 'age':18}

元组tuple

"""
	小括号括起来,内部存放多个元素,元组之间逗号隔开,元素不可改变,元素类型不能是任意的,
"""

定义:t1 = (11, 22, 33, 44)
# 数据类型转变
# 但凡能被for循环的遍历的数据类型都可以传给tuple()转换成元组类型
# print(tuple(123))
# print(tuple(123.11))
print(tuple('hello'))  # ('h', 'e', 'l', 'l', 'o')
print(tuple([11, 22, 33, 44]))  # (11, 22, 33, 44)
print(tuple((1,2,3,4)))   # (1, 2, 3, 4)
print(tuple({'username':'kevin', 'age':19}))   # ('username', 'age')
print(tuple({111,222,33,444}))  # (33, 444, 222, 111)


# 1. 取值
# print(tuple1[0])
# print(tuple1[3])

print(tuple1[-1])
print(tuple1[-2])
print(tuple1[-3])

# 3、长度
>>> len(tuple1)  

'''元组笔试题'''
t = (111)
t1 = (111.11)
t2 = ('hello')
t3 = ('hello', )  # 当元组内部哪怕是一个元素,也要加一个逗号隔开,否则就不是元组类型
l = ['hello',]  # list
'''容器类型:可以存放多个元素的数据类型都是容器类型
    推荐:如果是容器类型,内部就算有一个元素,也最后都加上一个逗号隔开
'''
print(type(t)) # <class 'int'>
print(type(t1)) # <class 'float'>
print(type(t2)) # <class 'str'>
print(type(t3)) # <class 'tuple'>
print(type(l)) # <class 'tuple'>

集合类型set

"""
	作用:去重、关系运算
	大括号括起来,内部存放多个元素,元素之间逗号隔开,数据类型只能是不可变的类型
	不能直接取值
"""
定义:
	s = {11,22,33,44}
    s = {}
# 如何定义一个空集合
s = {11,22,33,44}
print(type(s))  # set
# 定义空集合
print(type({}))  # dict
s1 = set()
print(type(s1))  # set

d = {}
d1=dict()
l = list()
l = []
print(type(d1))  # dict
print(type(l))  # list

# 类型转换
s = set([1,2,3,4])
print(s)  # {1, 2, 3, 4}

print(set((1,2,3,4)))  # {1, 2, 3, 4}

print(set({'name':'jason','age':20}))  # {'name', 'age'}
 
print(set('hello'))  # {'e', 'o', 'l', 'h'}

作业

1. 整理上午内容
2. 阅读文档资料中得练习题,并试着理解逻辑
3. 预习明日内容

day09——字符编码、文件操作

练习题讲解

a = 'abcdefccdd'
展示的结果:{'a':2, 'b':3, 'd':4}

# s = 'abadefccdd'

# 1. 定义一个空字典,用来存储字符对应的个数
# data_dict={}
#
# # 2. 变量整个字符串
# for i in s:
#     # 3. 判断当前字符是不是已经存在字典中
#     if i not in data_dict:
#         data_dict[i] = 1
#     else:
#         data_dict[i] += 1  # data_dict[i] = data_dict[i] + 1
#
# print(data_dict)

'''遇到字符串相关的题目,大多数情况都是转为列表,字典等常见数据类型来处理'''
name_list = 'kevin tony jack jack tom kevin tom jack tony tom kevin tom jack tony'
# 1. 定义一个空字典
data_dict = {}

# 2. 把字符串切分
lst = name_list.split()

# 3. 循环变量整个列表
for i in lst:
    # 4. 判断当前单词是否在大字典中
    if i in data_dict:
        data_dict[i] += 1
    else:
        data_dict[i] = 1

print(data_dict)

'''bug debug'''

debug补充

1. 在当前行的代码左侧点击一下,会出现一个红点(打断点)
2. 在代码编辑区域右键选择debug,不要在选择run

员工管理系统

# 1. 如何打印让用户直接看到带序号的功能

# 定义一个列表来存储添加的用户数据
user_data_list = []
# {'user_id':1,'username':'kevin', 'age':20, 'salary':2000},{'user_id':2,'username':'kevin', 'age':20, 'salary':2000}
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:
            # user_data_dic:{'user_id':1,'username':'kevin', 'age':20, 'salary':2000}
            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('输入的数据不合法')

字符编码

# 字符编码只跟文本文件和字符串有关,跟音频文件,视频文件之类的都没有关系
什么是字符编码?
	计算机内部只认识二进制,但是,我们人类语言有各种各样的样式,为了让计算机能够认识人类的语言,内部有一种机制就是讲人类语言转为计算机语言

# 字符编码:记录了人类语言与数据之间的对应关系

字符编码的发展史

# 1. 一家独大
	由于计算机最开始是由美国人发明的,美国人为了让计算机识别英文字符
     ASCII码表:使用一个字节来记录一个英文字符
        	"""
        		所有字符加起来一共127个字符
        		使用一个字节(8位)可以记录255种字符
        	"""
      # 记忆:
    	    A-Z:65-90
        	a-z:97-122
            0-9:48-57
            '''小写字母对应的数字一定大于大写字母'''
      # 补充:
    	s = 'hello'
        s1 = 'world'
        print(s > s1)
# 2. 群雄割据
	中国人:我们中国人也要使用计算机,中国人也发明了一张编码表
    		GBK码:记录中文英文与数据之间的对应关系
            '''
                我们使用两个字节或者三个字节来记录中文字符
            '''
            2 ** 16 = 65535
    韩国人: 韩国人也要使用计算机,我们也发明了一张编码表、
    	Euc-kr表:只有韩文字符、英文字符与数字的一一对应关系
    日本人:
    	 Shift_JIS表: 1、只有日文字符、英文字符与数字的一一对应关系
       ...
# 3. 一统天下
	unicode表(万国码): # 存在所有语言中的所有字符与数字的一一对应关系,即兼容万国字符
	'''结论:unicode码只在内存中起作用'''
   	# 使用至少2个字节保存数据
    
    utf8编码
'''我们日常使用的字符编码都是utf8编码,但是,utf系列还有utf16 utf32...  utf8mb4'''
# 一个字节保存一个英文字符
# 中文字符还是使用两个字节或者三个字节保存

# 补充:utf8只能存储正常的字符,utf8mb4可以存储表情

字符编码的使用

1. 如何解决乱码问题?
	文本文件使用什么字符编码保存,打开的时候就要使用对应的字符编码
2. python解释器版本不同带来的差异
	# python2 解释器使用的字符编码还是ASCII表
    # python3 使用的是utf8编码
3. 添加文件模板
	settings
    	Editor
        	File and code template
            	python script
4. 编码与解码(核心)
	  # 编码:
    		将人类能够读懂的语言转为计算机能够读懂的语言
    		
      # 解码:
    		将计算机能够读懂的语言转为人类能够读懂的语言

    ss = '趁年轻,学技能,养活自己'

    # 编码:

    # bytes 字节,看成是二进制
    s1 = ss.encode('utf8')
    print(s1, type(s1)) # b'\xb3\xc3\xc4\xea\xc7\xe1\xa3\xac\xd1\xa7\xbc\xbc\xc4\xdc\xa3\xac\xd1\xf8\xbb\xee\xd7\xd4\xbc\xba'

    # 解码
    print(s1.decode('utf8'))

s = b'kevin123' # 只有英文字符和数字,要想编码的话,直接使用前缀b
print(s.encode('utf8'))

文件介绍

# 文件的类型:txt word excel py
'''python操作文件'''

# 三步骤(关键字:open)
	1. 打开文件
    2. 操作文件
    3. 关闭文件

 # 1. 打开文件
# open('文件路径', 'r', '字符编码')

f=open('a.txt', 'r', encoding='utf-8')
# print(f)  # <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf8'>  文件句柄
print(f.read())
f.close()  # 关闭文件

"""
	语法格式:
		open('文件路径', '读写模式', '字符编码')
"""

# r的作用
'''当路径中可能存在有转移的字符时,字符串前面加上r来解决转义的问题'''
r'D:\python25\day09\a.txt'

文件的读写模式

1. r  >>> read: 只读 # 只能读,不能写
2. w  >>> write:只写 # 只能写,不能读
3. a  >>> append: 追加
# 读写模式
# 1. r
# 路径不存在,直接报错
# with open(r'a.txt', 'r', encoding='utf-8') as f:
#     print(f.read())


# 2. w
# 如果路径不存在,会新建一个文件出来
# with open('b.txt', 'w', encoding='utf-8') as f:
#     pass

# 1. 会先清空文件中得内容 2. 在进行写内容
'''写文件的是一定要小心,它会清空文件的'''
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')

# a:追加模式
# 路径不存在的时候,也会新建一个文件出来
# with open('a.txt', 'a', encoding='utf-8') as f:
#     f.write('oldgirl\n')

作业

1. 整理今日内容
2. 练习员工管理系统
3. 预习明日内容

day10——文件操作

文件的读写模式

r w a
'''以上三种模式只能操作文本'''

a w的区别
w: write 
	1. 文本不存在,会新建一个空文件
    2. 文件存在,会先清空文件数据,在从新写
    3. 写的数据类型一定是字符串类型
a: append 追加
	# 不会清空原来的数据

文件的操作方法

1. 读方法
    # 1. 读系列
    # 句点符
    with open(r'a.txt', 'r', encoding='utf-8') as f:
        # print(f.read())  # 一次性读取文件内所有的数据
        # print(f.read())  # 一次性读取文件内所有的数据
        # print(f.read())  # 一次性读取文件内所有的数据
        # data = f.read()
        '''一次性读取文件所有的数据就会存在漏洞:导致内存溢出'''
        # print(f.readline())  # 一次读取一行
        # print(f.readline())  # 一次读取一行
        # print(f.readline())  # 一次读取一行

        # print(f.readable())  # True

        '''把文件内的一行一行数据组装成列表元素返回,注意末尾的换行符'''
        # print(f.readlines())  #  ['你好洋哥\n', '你好洋哥\n', '你好洋哥\n']
2. 写方法
	
with open('a.txt', 'w', encoding='utf-8') as f:
    # f.write('hello\n')
    # f.write('hello\n')
    # f.write('hello\n')
    # print(f.writable()) # True
    # print(f.readable())  # False
    # print(f.writelines(['你好洋哥1\n', '你好洋哥2\n', '你好洋哥3\n']))
    pass

文件读操作优化

# with open('a.txt', 'r', encoding='utf-8') as f:
    # print(f.read())

    '''文件句柄f支持for循环'''
    # for line in f:
    #     print(line)  # 一次代码一行数据

    '''以后读取文件数据的时候,都使用for循环去一行一行的读取,不会出现内存溢出的情况'''

# with open('a.txt', 'w', encoding='utf-8') as f:
#     f.write('hello')
#     f.flush() # 把内存中得数据立刻刷到硬盘中

课堂练习

# 注册 登录
1. 简易版本的注册功能
	 # 把用户的用户名和密码保存到我们的数据文件中
     要求:'kevin|123'
        把用户的用户名和密码组织成你自己喜欢的格式保存到文件中
        # 1. 接收用户名和密码
        username = input('username>>>:')
        password = input('password>>>:')

        # 2. 组织用户名和密码成固定格式 kevin|123
        data = '%s|%s' % (username, password)
        # data = username + '|' + password

        # 3. 把用户数据保存到文件中
        with open('userinfo.txt', 'w', encoding='utf-8') as f:
            f.write(data)

        print('%s: 注册成功' % username)
2. 简易版本的登录功能
		# 2. 判断用户名和密码是否正确
        with open('userinfo.txt', 'r', encoding='utf-8') as f:
            # kevin|123
            data = f.read()  # 由于文件内的数据很少,我们就直接read读取完了

            # 根据|把用户名和密码拆分
            # res=data.split('|')  # ['kevin', '123']
            real_username, real_password = data.split('|')  # ['kevin', '123']

        # 进行判断
        # if username == res[0] and password == res[1]:
        if username == real_username and real_password == password:
            print('登录成功')
        else:
            print('登录失败')

进阶版本

1. 支持多用户注册
'''快捷键:tab缩进, shift+tab 反向缩进'''
'''多用户注册'''
while True:
    # 1. 接收用户名和密码
    username = input('username>>>:')
    password = input('password>>>:')

    # 3. 判断用户是否已经注册过
    # 3.1 先取出文件中得数据
    with open('userinfo.txt', 'r', encoding='utf-8') as f1:
        # 3.2 把文件数据一行一行的读出来
        for line in f1:
            # print(line)  # kevin|123\n
            real_username, *_ = line.split('|')
            # res = line.split('|')
            if real_username == username:
                print('该用户已经存在,请从新输入')
                break
        else:
            # 2. 组织用户名和密码成固定格式 kevin|123
            data = '%s|%s\n' % (username, password)
            # data = username + '|' + password

            # 3. 把用户数据保存到文件中
            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()
    
    # cmd的验证
    if cmd == '1':
        while True:
            # 1. 接收用户名和密码
            username = input('username>>>:')
            password = input('password>>>:')

            # 3. 判断用户是否已经注册过
            # 3.1 先取出文件中得数据
            with open('userinfo.txt', 'r', encoding='utf-8') as f1:
                # 3.2 把文件数据一行一行的读出来
                for line in f1:
                    # print(line)  # kevin|123\n
                    real_username, *_ = line.split('|')
                    # res = line.split('|')
                    if real_username == username:
                        print('该用户已经存在,请从新输入')
                        break
                else:
                    # 2. 组织用户名和密码成固定格式 kevin|123
                    data = '%s|%s\n' % (username, password)
                    # data = username + '|' + password

                    # 3. 把用户数据保存到文件中
                    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>>>:')

        # 2. 读取文件数据,得到用户的真实用户名和密码
        with open('userinfo.txt', 'r', encoding='utf-8') as f:
            # 一行一行的读取用户名和密码
            for line in f:
                real_username, real_pwd = line.split('|')  # kevin|123\n  ['kevin', '123\n']
                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', 'wb') as f:
#     s = '你好'
#     f.write(s.encode('utf8'))


with open('b.txt', 'rb') as f:
    print(f.read().decode('utf8'))

作业

1. 整理今日内容
2. 熟练掌握课堂练习
3. 做一个简易的拷贝功能

day11——文件指针、函数前戏

练习题讲解

# 描述一下需要做的功能
"""
	1. 先定义一个源文件路径
	2. 先定义一个目标文件路径
	3. 读取源文件的数据,然后写入到目标文件
"""

# 简易版本:
# 1.先定义一个源文件路径
source_file_path = 'a.txt'

# 2. 定义一个目标文件的路径
target_file_path = 'b.txt'

# 3. 读取源文件,写入到目标文件
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:
        # 4. 读取源文件,
        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,0)  # 控制指针的移动
    # f.seek(3,1)  # 控制指针的移动
    f.seek(-3,1)  # 控制指针的移动
    print(f.read().decode('utf-8'))
"""
    f.seek总共有3种模式
        1. offset参数
            偏移量,移动的位置
            如果是整数,从左往右读取
            如果是负数,从右往左读取
        2. whence参数
            # 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
            # 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
            # 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
"""

文件的修改(了解)

# 方式一:
# 1. 读取b.txt文件数据
with open('b.txt', 'r', encoding='utf-8') as f:
    data = f.read()
print(data)  # kevin kevin kevin kevin say hello world
new_data = data.replace('kevin', 'jack')

# 2. 把新的数据再次写入文件
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,]
# print(len(l))  # len不能用了

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. 内置函数
	# 在python解释器中提前存在的,我们可以直接使用
   	eg:len, print,之前学习的数据类型的内置方法都是内置函数
2. 自定义函数
	# 我们自己写的函数
    2.1. 无参函数:没有参数的函数
    2.2. 有参函数:带参数的函数
    2.3. 空函数:啥都没有
    #1.无参函数
# def my_len():
#     print(123)
# my_len()

# 2. 有参函数
def my_len(a,b):
    print('hello')
    print(a,b)
# my_len(1,2)
my_len('a','b')

# 3. 空函数
def my_func():
    # 1. 注册
    pass
    ...

def login():
    # 登录功能
    pass

def chongqian():
    # 这里是充值的逻辑
    pass

函数的返回值(重要)

def my_func1():
    print('helloworld')
    # return 123, 345, 567  # (123, 345, 567)
    # return [1, 2, 3]
    # return {'username':'kevin','age':20}
    return '123'


# 返回值:函数执行完毕之后,返回的结果
res = my_func1()  # 把函数的执行结果赋值给res变量,结果就是None
print(res)  # None
# 1. 当函数没有返回值的时候,没有return的时候,返回的是None
# 2. 当函数中只要return关键字的时候,返回的结果仍然是None
# 3. 当return后有数据的时候,写什么就返回什么
# 4. 当return后面跟多个数据的时候,会组织成元组形式返回
# 5. 如果你想返回多个值得时候,最后使用容器类型包一下
# 6. 在函数中,只要遇到return关键字,代码立刻终止执行,一个函数最多只有有一个return关键字
# 7. return 只能出现在函数中。if else中绝对不能出现

作业

1. 整理今日内容
2. 继续练习拷贝功能
3. 继续连续多用户注册和登陆功能
4. 尝试把多用户注册和登录功能封装成函数版本(可选)

day12——参数、名称空间

函数参数的两大分类

1. 形式参数
	# 在函数定义阶段,括号中填入的变量名,就是形式参数,简称:形参
    	def my_func(a, b):
            pass
		# 此时,a,b就是函数my_func的形参
2. 实际参数
	# 在函数调用阶段,括号中传入的数据值,就是实际参数,简称:实参
    	my_func(1,2)
        # 此时,数据1,2就是函数my_func的实参
        
"""
	形参和实参的关系
		实际参数以变量名=数据值的形式进行传递
	
	形参和实参的表现形式
		形参的表现形式只有一种,就是变量名
		实参的表现形式有多种... 核心:都是数据值
"""

# 在函数调用阶段,实参和形参临时绑定,函数调用完毕,二者断开

位置参数

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. 在调用阶段如果你不传数据值,那就使用默认的

# 练习:定义一个函数,功能是注册(name, age, gender)
def register(name, age, gender='male'):
    print("%s:%s:%s" % (name, age, gender))

register('kevin', 19)
register('kevin1', 19)
register('kevin2', 19, 'female')

'''注意:'''
1. 当有了默认参数,就可以传值和不传值了

# 练习题1:
# 参数的默认值如果给的是一个列表,那么,后续的数据都会指向同一个列表,(同一个内存空间)
# def my_func(name, age, hobby=[]):
#     hobby.append(age)
#     print("%s:%s:%s" % (name, age, hobby))
#
# my_func('kevin1', '20', [])
# my_func('kevin2', '18', [])
# my_func('kevin3', '39', [])


# 练习题2:
x = 100


def index(a, b, c=x):
    print(a, b, c)

x = 200

index(1,2)

可变长参数

# 1. 不管传入多少个位置参数,函数都可以正常运行
def index(x, y, *a):
    print(x,y)
    print(a) # (1, 2, 3, 4, 5, 6)

"""
    *在形参中得使用:接收多余的位置参数,并组装成元素的形式一一返回
"""
index(1, 2, 3, 4, 5, 6)

# 2. 不管传入多少个关键字参数,函数都可以正常运行
def func( **a):
    print(a)

func(age=20, name='kevin', salary=1000, gender='male')  # {'salary': 1000, 'gender': 'male'}

"""
**在形参中得使用:接收对于的关键字参数,并且把关键字参数组织成字典的形式
"""

# 3. 不管传入多少个位置参数和关键字参数,函数都可以正常运行
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(a, b, *args ):
#     print(a, b, args)

# index(1, 2, 3)
# l = [11, 22, 33,44,55]
# t = (11, 22, 33,44,55)
# index(l[0], l[1], l[2])
"""
*在实参中得使用:把列表、元组内的各个元素打散,然后一一传值
"""
# index(*l) # index(11, 22, 33,44,55)
# index(*t) # index(11, 22, 33,44,55)
# print(*t)


def index(**kwargs):
    print(kwargs)  # {'username': 'kevin', 'age': '18', 'gender': 'male'}


# index(1, 2, name='kevin')

dict = {'username': 'kevin', 'age': '18', 'gender': 'male'}
# index(username='kevin', age='age', gender='male')
"""
**在实参中得使用:把字典内的元素打散,然后以关键字的形式一一传入
"""
index(**dict)
index(**{'username': 'kevin', 'age': '18', 'gender': 'male'}) # index(username='kevin', age='age', gender='male')

名称空间的概念(namespace)

# 存放变量名与变量值关系的地方

# 名称空间的分类
1. 内置的名称空间
	# 在python解释器中存在
2. 全局名称空间
	# 在py文件中,顶格写的代码都在全局名称空间中
    name = 'kevin'

    if  True:
        a = 1

    while True:
        x = 10
3. 局部名称空间
	# 在函数体代码执行完产生的数据
'''只要在函数中出现的都是局部的'''


4. 存活周期
	# 内置的:伴随python解释器的启动/关闭而产生/回收
	# 全局的:伴随python文件的开始执行/执行完毕而产生/回收
    # 局部的:伴随函数的调用/结束而临时产生/回收

名字的查找顺序

# 首先先确定你所在的位置
1. 如果在局部中:
	局部  >>>  全局 >>> 内置
2. 如果在全局中:
	全局 >>> 内置


# index()

# x = 888
def index():
    # x = 666

    def index1():
        # x = 666

        def index2():
            # x = 666

            def index3():
                # x = 666

                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关键字声明一下
"""

函数对象(函数名)

# 1. 函数名可以当成变量赋值
# def index():
#     print('from index')

'''函数名不加括号就是函数的内存地址'''


# a  = index
# a()
# print(a)

# 2. 函数名可以当成函数的实参
# def index():
#     print('from index')
#
# def func(a):
#     # print(a) # a => index
#     a()
#     print('from func')
# func(index)
# func(index())

# 3. 函数名可以当成函数的返回值
# def index():
#     print('from index')
#
# def func():
#     print('from func')
#     return index  # 返回的就是index函数的内存地址
#
# res=func()  # res就是index函数的内存地址
# res()

# 4. 函数名可以当成容器类型的元素

# def index():
#     print('from index')
#
#     return 'from index'
#
# # l = [11, 22, 33, index]
# l = [11, 22, 33, index()]
# print(l)


'''函数名只要加括号,就会执行!!!!!!!!!'''
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()
    # 判断用户输入的编号在不在fund_dict这个字典里面
    if choice in func_dict:
        func_name=func_dict.get(choice)  # 函数的内存地址
        func_name()
    else:
        print('不存在')

函数的嵌套调用

# def index():
#     print('from index')
#
# def func():
#     index()
#     print('from func')
#
# func()

def my_max(a, b):
    if a > b:
        return a
    return b

# 判断4个数的大小,返回大的
def many_max(a, b, c, d):
    res=my_max(a, b) # 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):
    # url = 'http://www.baidu.com'
    def get_content():
        # url = 'http://www.jd.com'
        # res=requests.get('http://www.baidu.com')
        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() # get_content
# res('http://www.jd.com')

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')

# 1. 在函数调用之前打印此时的时间
start_time = time.time()
index()

# 2. 在函数调用完毕之后的时间
end_time = time.time()

print('index函数一共执行了%s秒' % (end_time - start_time))

作业

1. 整理今日内容
2. 预习明天内容装饰器

day14——装饰器

装饰器简易版本

def outer(func_name):
	# func_name = index
	def get_time():
	# 1. 在函数执行之前,要记录一下此时的时间
	start——tim = time.time()
	# 2. 开始执行函数
	func_name()
	# 3. 函数执行完毕之后,再次记录此时的时间
	end_time = time.time()
	# 4. 统计函数的执行时间
	print('执行了%s秒' % (end_time - start_time))
return get_time

index = outer(index) # get_time的内存地址
index()

装饰器解决参数问题

def outer(func_name)
	# func_name = index
	def get_time(*args,**kwargs)
	# 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        func_name(*args, **kwargs)
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
    return get_time
    
index = outer(index)  # get_time的内存地址
index()

解决返回值问题

def outer(func_name):
    # func_name = index
    def get_time(*args, **kwargs):
        # 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        res=func_name(*args, **kwargs)  # index()
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
        return res  # 才是真正的函数的返回结果
    return get_time
# index = outer(index)  # get_time的内存地址
# res=index()
# print(res)  # None

home=outer(home)
print(home())

课堂练习题

flag':False}
def login_auth(func_name):
    def auth(*args,**kwargs):
        if dict.get('flag'):
            res = func_name(*args,**kwargs)
            return res
        username = input('>>>:').strip()
        password = input('>>>:').strip()
        if username == 'jason' and password == '123':
            res = func_name(*args,**kwargs)
            dict['flag'] = True
            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  # index=outer(index)
def index():
    print('from index')
index()

@outer
def home():
    print('from home')
home()

双层语法糖

import time

def outer(func_name):
    # func_name = index
    def get_time(*args, **kwargs):
        # 1. 在函数执行之前,要记录一下此时的时间
        start_time = time.time()
        # 2. 开始执行函数
        func_name(*args, **kwargs)
        # 3. 函数执行完毕之后,再次记录此时的时间
        end_time = time.time()
        # 4. 统计函数的执行时间
        print('执行了%s秒' % (end_time - start_time))
    return get_time


def login_auth(func_name):
    # func_name = index
    def auth():
        # 1. 让用户输入用户名和密码
        username = input('username:').strip()
        password = input('password:').strip()

        # 2. 要认证,判断用户名和密码是否正确
        if username == 'kevin' and password == '123':
            # 才是正常执行的函数
            res=func_name()
            return res
        else:
            print('认证失败,不能执行函数')
    return auth

@login_auth # index=login_auth(get_time)
@outer  # get_time=outer(index)
def index():
    time.sleep(1)
    print('from index')

index()  # auth()

装饰器修复技术

from functools import wraps
def login_auth(func_name):
    # func_name = index
    @wraps(func_name)  # 修复技术是为了让被装饰对象不容易被察觉装饰了
    def auth():
        # 1. 让用户输入用户名和密码
        username = input('username:').strip()
        password = input('password:').strip()

        # 2. 要认证,判断用户名和密码是否正确
        if username == 'kevin' and password == '123':
            # 才是正常执行的函数
            res=func_name()
            return res
        else:
            print('认证失败,不能执行函数')
    return auth

课堂练习题

# 判断七句print执行顺序
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):
        # func_name = index
        def auth(*args, **kwargs):
            # 1. 让用户输入用户名和密码
            username = input('username:').strip()
            password = input('password:').strip()
    
            # 2. 要认证,判断用户名和密码是否正确
            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,) # @login_auth
def index():
    print('from index')

day15——递归函数、二分法、匿名函数、列表字典集合生成式

递归函数

# 什么是递归函数?
	就是直接或者间接的调用自己
# 递归函数的使用场景
# 伪代码:
# age(5) = age(4) + 2
# age(4) = age(3) + 2
# age(3) = age(2) + 2
# age(2) = age(1) + 2
# age(1) = 18

# age(n) = age(n-1)+2
# gae(1) = 20

# 真实代码实现
# def age(n):
# 	 if == 1:
#		 return 18
#    res = age(n-1) + 2
#	 return res
	
# print(age(50))

"""
递归:
	1.递推
		逐层寻找答案
	2.回溯
		根据最终的答案推导出最原始的答案
	3.递归函数必须有结束条件!!!
"""

# 练习题
lst = [1, [2, [3, [4, [5, [6, [7, ]]]]]]]
# for i in lst:
#     # 判断此时的i值是整数还是列表,如果是整数直接打印,如果是列表,直接for循环
#     if type(i) is int:
#         print(i)
#     else:
#         # 如果是列表,直接for循环
#         for j in i:
#             if type(j) is int:
#                 print(j)
#             else:
#                 # 如果是列表,直接for循环
#                 for m in j:
#                     if type(m) is int:
#                         print(m)

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]
# 判断这个列表中是否有20这个数字
'''二分法原则: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)

# is_beautiful = True
# res = '漂亮' if is_beautiful else ' 不漂亮'
# print(res)

cmd = input('请选择是否输入:(y/n)')
res = '继续' if cmd == 'y' else '不继续'
print(res)

列表生成式

name_list = ['kevin', 'jack', 'ly', 'tony']
# 需求是:把列表中的名字都添加一个后缀:_NB

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)

# 需求:除了jack不加,其他都加,如果是jack直接去掉
name_list = ['kevin', 'jack', 'ly', 'tony']
# new_list = []
# for name in name_list:
#     if name == 'jack':
#         continue
#     else:
#         res = '%s_NB' % name
#         new_list.append(res)
# print(new_list)

# 列表生成式的使用
# res = ['%s_NB' % name for name in name_list if name != 'jack']

'''特殊用法'''
name_list = ['kevin', 'jack', 'ly', 'tony']
res = [f'{name}_NB' if name != 'jack' else '111' for name in name_list]
print(res)

字典生成式、集合生成式

'''补充:'''
# count=0
# for i in l1:
#     print(count, i)
#     count+=1

# 记忆
"""
enumerate:
	使用for循环的时候,可以解压赋值出来两个值,一个是索引,一个是元素
stret:
	控制的是起始位置,默认是从0开始
"""
# for i, j in enumerate(l1, start=2):
#     print(i ,j)   

# 字典生成式
# res = {i:j for i, j in enumerate(l1)}
# print(res)

# 集合
# res = {i for i in l1}
# print(res)

# 元组
# 迭代器
res = (i for i in l1)
print(res)  # generator

匿名函数

# 没有名字的函数
def index():
    pass

index()
"""
语法格式:
    lambda 形参:返回值
    lambda x:x**2

匿名函数一般步单独使用,会配合其他函数使用
    map()
"""

# def index(x):
#     return x ** 2

# res=lambda x:x**2  # 内存地址
# print(res(2)) # 4

l = [1,2,3,4,5,6,7,8,9]
# res=list(map(index, l))  # <map object at 0x0000017D935721F0>
res=list(map(lambda x:x**2, l))  # <map object at 0x0000017D935721F0>
print(res) # [1, 4, 9, 16, 25, 36, 49, 64, 81]

day16——迭代器、生成器、异常处理

配合匿名函数使用的方法

1.map(函数名,要遍历的数据) # 内部本质就是for循环,再记住两个参数的位置和作用
# 2.zip拉链
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']

# d1 = {'username': 'tony'}
# d2 = {'username1': 'kevin'}
# [(11,'name'), (22, 'age'), (33, 'hobby')]

# lst = []
# for i in range(len(l)):
#     lst.append((l[i], ll1[i]))
# print(lst)

res = zip(l, ll1, ll2, ll3)  # <zip object at 0x000001646C22FE00> [(11, 'name'), (22, 'age'), (33, 'hobby')]
print(list(res))

# 3. max
# l = [1, 2, 10, 30, 5, 6, 7, 8]
# 求出列表中最大值
# print(max(l))

# 求出最小值
# print(min(l))

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]))

# 4. 过滤
l = [1,10,20,55,66,70]
# l1 = []
# for i in l:
#	 if i > 20:
# 		l1.append(i)
# print(l1)
def infex(x)
	return x>20
res = filter(lambda x:x>20,l)
print(list(res)) #[55,66,77]

可迭代对象

# 什么是迭代
	迭代就算每一次的结果必须依赖于上一次的结果
v1.0 v2.0 v3.0

# 可迭代对象
	内置有_iter_方法的对象都是可迭代对象
    '''内置的意思是python自带的,解释器中已经存在的,我们可以直接使用的'''
# 目前我所学的数据类型中,有哪些是可迭代对象,都支持for循环
	str,list,dict,tuple,set,文件对象
  	print(c)
    print(c.__iter__()) # <str_iterator object at 0x0000017E0C21C610>
    print(iter(c))  # <str_iterator object at 
    0x000001B18D4DC610>
	# print(c.__len__())  # 5
	# print(len(c))
    
    '''一般双下滑先开头的方法都有一个简便的写法,就是函数名()'''

迭代器对象

# 什么是迭代器对象
	既有_iter_方法, 也含有_next_方法
    
'''文件对象即是可迭代对象又是迭代器对象'''

# 如果生成迭代器对象
	可迭代对象调用_next_方法
    
注意:
	迭代给我们提供了一种不依赖索引取值的方法
    
# 练习题:hello
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循环,__next__ next()

# 1.可迭代对象要转为迭代器
# res = l.__iter__()
# 
# while True:
#     print(res.__next__())

# for i in l:
#     print(i)

'''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) 
elsetry的子代码正常运行结束没有任何的报错 再执行else子代码
8.结合finally使用
try:
    待监测的代码(可能会出错的代码)
except Exception as e:
    针对各种常见的错误类型全部统一处理
   	print(e) 
elsetry的子代码正常运行结束没有任何的报错 再执行else子代码
finally:
	无论try的子代码是否报错 最后都要执行finall子代码
"""
	try except异常捕捉需要注意
    	1. try里面被监测的代码尽量少
    	2. 在明显没有错误的代码不要被捕捉
"""

异常处理补充

1.断言
    name = 'jason'
    # assert isinstance(name,int)
    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]
    # 1.先将列表调用__iter__转变成迭代器对象
    iter_l1 = l1.__iter__()
    # 2.while循环让迭代器对象反复执行__next__
    while True:
        try:
            print(iter_l1.__next__())
        except Exception as e:
            break	

生成器对象

1.本质
	还是内置有__iter__和__next__的迭代器对象
2.区别
	迭代器对象是解释器自动提供的
    	数据类型\文件对象>>>:迭代器对象
    生成器对象是程序员编写出来的
    	代码、关键字>>>:迭代器对象(生成器)
3.创建生成器的基本语法
	函数体代码中填写yield关键字
    # def my_iter():
    #     print('hahaha')
    #     yield
    """
    1.函数体代码中有yield关键字 那么函数名加括号并不会执行函数体代码 会生成一个生成器对象(迭代器对象)
    """
    # res = my_iter()
    """
    2.使用加括号之后的结果调用__next__才会执行函数体代码
    """
    # res.__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)
    # 1.生成器
    def my_range(strat_num, end_num=None, step=1):
        # 判断end_num是否有值 没有值说明用户只给了一个值 起始数字应该是0 终止位置应该是传的值
        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('大便')  # 1.将括号内的数据传给yield前面的变量名 2.再自动调用双下next

生成器表达式

说白了就是生成器的简化写法

l1 = (i ** 2 for i in range(100))  # 生成器对象
print(l1)  # <generator object <genexpr> at 0x000002720772DFC0>
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)  # 20
res = list(g)
print(res)  # [20, 21, 22, 23]

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.importfrom...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
    
    # 表示二维坐标系
    # point = namedtuple('point',['x','y'])
    # 生成点信息
    # p1 = point(1,2)
    # print(p1)  # 点(x=1,y=2)
    # print(p1.x)  # 1
    # print(p1,y)  # 2
    
    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.time())  # 1666150097.5481427
# print(time.localtime(())
# time.struct_time(tm_year=2022, tm_mon=10, tm_mday=19, tm_hour=11, tm_min=32, tm_sec=50, tm_wday=2, tm_yday=292, tm_isdst=0)
# print(time.strftime(%Y-%m-%d))  # 2023-03-09
# print(time.strftime(%Y/%m/%d))  # 2023/03/09
# print(time.strftime(%Y/%m/%d %H:%M:%S))
# 2023/03/09 19:12:20
print(time.strftime('%Y/%m/%d %X'))
# 2023/03/09 19:12:20
time.sleep(10) # 让程序原地阻塞指定的秒数


import datetime
print(datetime.datetime.now())  # 2023-03-09 19:17:54.260699
print(datetime.datetime.today())  # 2023-03-09 19:17:54.260700
print(datetime.date.today())  # 2023-03-09
"""
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)
# 指定日期: 2023-03-09 19:22:00
from datetime import datetime
# d=datetime.strptime('2017/9/30','%Y/%m/%d')
# print(d)  # 2017-09-30 00:00:00
# e=datetime.strptime('2017年9月30日星期六','%Y年%m月%d日星期六')
# print(e)
# f=datetime.strptime('2017年9月30日星期六8时42分24秒','%Y年%m月%d日星期六%H时%M分%S秒')
# print(f)
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)
# 2023-03-09 19:29:33.260416
# 2023-03-09 19:49:33.260416

常用内置模块之随机数模块

import random
# print(random.random())  # 随机产生0到1之间的小数
# print(random.randint(1, 6))  # 随机产生1到6之间的整数
# print(random.randrange(1, 100, 2))  # 随机产生指定的整数
# print(random.choice(['一等奖', '二等奖', '三等奖', '谢谢惠顾']))  # 随机抽取一个样本  '二等奖'
# print(random.choices(['一等奖', '二等奖', '三等奖', '谢谢惠顾']))  # 随机抽取一个样本   ['二等奖']
# print(random.sample(['jason', 'kevin', 'tony', 'oscar', 'jerry', 'tom'], 2))  # 随机抽指定样本数  ['tom', 'jerry']
# l1 = [2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A']
# random.shuffle(l1)  # 随机打乱数据集
# print(l1)

'''产生图片验证码:每一位都可以是大写字母 小写字母 数字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.创建目录(文件夹)'''
# os.mkdir(r'd1')  # 相对路径 在执行文件所在的路径下创建目录
# os.mkdir(r'd2\d22\d222')  # 不可以创建多级目录
# os.makedirs(r'd2\d22\d222')  # 可以创建多级目录
# os.makedirs(r'd3')  # 也可以创建单级目录

'''2.删除目录(文件夹)'''
# os.rmdir(r'd1')  # 可以删除单级目录
# os.rmdir(r'd2\d22\d222')  # 不可以一次性删除多级目录
# os.removedirs(r'd2\d22')  # 可以删除多级目录
# os.removedirs(r'd2\d22\d222\d2222')  # 只能删除空的多级目录
# os.rmdir(r'd3')  # 只能删空的单级目录

'''3.列举指定路径下文件名称'''
# print(os.listdir())  # ['.idea', '01 os.py', 'd2', 'd3', '__pycache__']
# print(os.listdir(r'F:\\'))

'''4.删除/重命名文件'''
# os.rename(r'a.txt',r'aaa.txt')
# os.remove(r'aaa.txt')

'''5.获取/切换当前工作目录'''
# print(os.getcwd())  # F:\ProjectImport
# os.chdir('..')  # 切换到上一级目录
# print(os.getcwd())  #F:\
# os.mkdir(r'hei')

'''6.动态获取项目根路径(重要)'''
# print(os.path.abspath(__file__))  # 获取执行文件的绝对路径 F:\ProjectImport\01 os.py
# print(os.path.dirname(__file__))  # 获取执行文件所在的目录路径  F:/ProjectImport

'''7.判断路径是否存在(文件、目录)'''
# print(os.path.exists(r'01 os.py'))  # 判断文件路径是否存在
# print(os.path.exists('F:\ProjectImport'))  # 判断目录是否存在
# print(os.path.isfile(r'01 os.py'))  # True 判断路径是否是文件
# print(os.path.isfile('F:\ProjectImport'))  # Flase 判断路径是否是文件
# print(os.path.isdir(r'01 os.py'))  # Flase 判断路径是否是目录
# print(os.path.isdir('F:\ProjectImport'))  # True 判断路径是否是目录

'''8.路径拼接'''
# s1 = r'F:\ProjectImport'
# s2 = r'01 os.py'
# print(f'{s1}\{s2}')
"""
涉及到路径拼接一定不要自己做 因为不同的操作系统路径分隔符不一样
"""
# print(os.path.join(s1, s2))
# 9.获取文件大小(字节)
# print(os.path.getsize(r'a.txt'))

sys模块

import sys

print(sys.path)  # 获取执行文件的sys.path
print(sys.getrecursionlimit())  # 获取python解释器默认最大递归深度
sys.setrecursionlimit(2000)  # 修改python解释器默认最大递归深度
print(sys.version) 
(tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)]
# print(sys.platform)  # 平台信息 win32(了解即可)
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

# 注册功能
# 1.获取执行文件所在的目录路径
base_dir = os.path.dirname(__file__)  # D:/pythonProject03/day19
# 2.拼接出db目录的路径
db_dir = os.path.join(base_dir, 'db')  # D:/pythonProject03/day19/db
# 3.创建db目录
if not os.path.isdir(db_dir):
    os.mkdir(db_dir)
# 4.获取用户数据
# username = input('username>>>:').strip()
# password = input('password>>>:').strip()
# 4.1.判断用户名是否已存在
# print(os.listdir(db_dir))  # ['jason.json', 'kevin.json', 'tony.json']  方式1
# user_file_path = os.path.join(db_dir, f'{username}.json')  方式2
# 5.构造用户字典
# user_dict = {
#     'username': username,
#     'password': password,
#     'account': 15000,  # 账户余额
#     'shop_car': []  # 购物车
# }
# 6.拼接存储用户数据的文件路径
# user_file_path = os.path.join(db_dir, f'{username}.json')  # D:/pythonProject03/day19/db/jason.json
# 7.写入文件数据
# with open(user_file_path,'w',encoding='utf8') as f:
#     json.dump(user_dict, f)

username = input('username>>>:').strip()
# 1.拼接上述用户名组成的文件路径
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()
    # 2.获取用户真实数据字典
    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)  # {"name": "json\u8001\u5e08", "pwd": 123}
res = json.dumps(d, ensure_ascii=False)
print(res)  # {"name": "json老师", "pwd": 123}

作业讲解

1.编写一个统计指定文件类型的脚本工具
 输入指定类型的文件后缀
     eg:.txt
 并给出一个具体路径 之后统计该类型文件在该文件下的个数
  ps:简单实现即可 无需优化
import os

# 1.获取目录路径
dir_path = input('请输入目录路径>>>:').strip()  # aaa
# 2.获取指定后缀名
ends_name = input('请输入想要统计的文件后缀名>>>:').strip()  # .txt
# 3.列举目录路径下所有内容的名称
path_list = os.listdir(dir_path)  # ['a.txt', 'a1', 'a2', 'b.txt', 'c.txt', 'd.py', 'e.py']
# 4.循环获取每一个内容名称 判断是否以用户指定的文件后缀名结尾
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

# 1.获取执行文件所在的路径
base_dir = os.path.dirname(__file__)  # D:/pythonProject03/day20
# 2.拼接存储文本文件的目录路径
data_dir = os.path.join(base_dir, 'data')  # D:/pythonProject03/day20/data
# 3.列举目录下所有内容名称
file_name_list = os.listdir(data_dir)  # ['B老师视频合集.txt', 'J老师视频合集.txt', 'K老师视频合集.txt', 'R老师视频合集.txt', 'Z老师视频合集.txt']
while True:  # 添加循环让项目更加的合理
    # 4.循环打印文件名供用户选择查看
    for num, file_name in enumerate(file_name_list, start=1):  # 枚举 主要用于给数据添加编号便于查看和选取
        print(num, file_name)
    # 5.等待for循环展示完毕之后 获取用户想要查看的编号
    choice_num = input('请输入您想要查看的文件编号(q)>>>:').strip()  # 获取到的用户输入是字符串 而列表索引取值需要用数字
    if choice_num == 'q':
        print('拜拜 下次再见 等你哟~')
        break
    # 6.判断用户输入是否是纯数字
    if not choice_num.isdigit():
        print('你眼瞎啊 编号只能是数字 你妹的!')
        continue
    choice_num = int(choice_num)  # 100
    # 7.判断数字是否在列表的索引范围内
    if choice_num not in range(1, len(file_name_list) + 1):  # range(1, 6)
        print('我看你是真瞎 编号不在范围内 看清楚好好输')
        continue
    # 8.索引取值获取用户想要查看的文件名称
    target_name = file_name_list[choice_num - 1]  # 'B老师视频合集.txt'
    # 9.拼接文件的绝对路径(因为站在执行文件的角度找不到文本文件)
    target_file_path = os.path.join(data_dir, target_name)
    # 10.利用文件操作打开文件并读取内容
    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:
        # 1.获取用户相关信息
        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:
        # 1.获取商品信息(目前是写死的 后期可以动态获取)
        good_list = [
            ['挂壁面',3],
            ['印度飞饼',22],
            ['极品木瓜',666],
            ['土耳其土豆',999],
            ['伊拉克版面',1000],
            ['董卓戏张飞公仔',2000],
            ['仿真玩偶',10000]
        ]
        # 2.循环打印商品信息供用户选择
        for num,good_data in enumerate(good_list,start=1):
            print(f"商品编号:{num}    |    商品名称:{good_data[0]}    |    商品价格:{good_data[1]}")
        # 3.获取用户输入的编号
        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')  # {'印度飞饼':[10,22]}
            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
        # 4.判断是否是纯数字
        if not choice_num.isdigit():
            print('您输入的不是数字 请重新输入')
            continue
        choice_num = int(choice_num)
        # 5.判断编号是否超出范围
        if choice_num not in range(1,len(good_list)+1):
            print('您输入的编号超出范围 请重新输入')
            continue
        # 6.根据编号获取商品信息
        target_good_list = good_list[choice_num-1]
        # 7.获取想要购买的商品个数
        good_num = input(f'请输入您想要购买的{target_good_list[0]}的商品个数>>>:').strip()
        if not good_num.isdigit():
            print('商品数量必须是纯数字')
            continue
        good_num = int(good_num)
        # 8.写入临时小字典
        """
        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():
    # 1.拼接登录用户的文件路径
    user_file_path = os.path.join(new_dir,f'{is_login.get("username")}.json')
    # 2.读取文件中用户的数据
    with open(user_file_path,'r',encoding='utf8') as f:
        user_data_dict = json.load(f)
    # 3.获取当前用户的账户余额及购物车数据
    shop_car = user_data_dict.get('shop_car')  # {'印度飞饼':[10,22],'公仔':[100,100]}
    if not shop_car:
        print('您的购物车没有商品')
        return
    current_balance = user_data_dict.get('balance')
    # 4.统计购物车商品总价
    total_money = 0
    for g_list in shop_car.values():  # [10,22] [100,100]
        total_money += g_list[0] * g_list[1]
    # 5.比较余额是否充足
    if total_money > current_balance:
        print('gun 账户余额不够')
        return
    user_data_dict['balance'] -= total_money
    # 6.清空购物车
    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:
    # 1.获取用户输入的手机号
    phone_num = input('请输入您的手机号>>>:').strip()
    # 2.先判断是否是十一位
    if len(phone_num) == 11:
        # 3.再判断是否是纯数字
        if phone_num.isdigit():
            # 4.判断手机号的开头
            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
# 常见操作方法
# res = re.findall('a', 'jason apple eva')
# print(res)  # 查找所有符合正则表达式要求的数据 结果直接是一个列表

# res = re.finditer('a', 'jason apple eva')
# print(res)  # 查找所有符合正则表达式要求的数据 结果直接是一个迭代器对象

# res = re.search('a', 'jason apple eva')
# print(res)  # <re.Match object; span=(1, 2), match='a'>
# print(res.group())  # a  匹配到第一个符合条件的数据就立刻结束

# res = re.match('a', 'jason apple eva')
# print(res)  # None  匹配字符串的开头 如果不符合后面不用看了
# print(res.group())  # 匹配开头符合条件的数据 一个就结束

# obj = re.compile('\d{3}')  # 当某一个正则表达式需要频繁使用的时候 我们可以做成模板
# res1 = obj.findall('23423422342342344')
# res2 = obj.findall('asjdkasjdk32423')
# print(res1, res2)

ret = re.split('[ab]', 'abcd')  # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret)  # ['', '', 'cd']

ret = re.sub('\d', 'H', 'eva3jason4yuan4', 1)  # 将数字替换成'H',参数1表示只替换1个
print(ret)  # evaHjason4yuan4

ret = re.subn('\d', 'H', 'eva3jason4yuan4')  # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret)  # ('evaHjasonHyuanH', 3)

re模块补充说明

1.分组优先
	# res = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
    # print(res)  # ['oldboy']
    # findall分组优先展示:优先展示括号内正则表达式匹配到的内容
    # res = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
    # print(res)  # ['www.oldboy.com']
    
    # res = re.search('www.(baidu|oldboy).com', 'www.oldboy.com')
    # print(res.group())  # www.oldboy.com 
    # search直接展示
    
    # res = re.match('www.(baidu|oldboy).com', 'www.oldboy.com')
    # print(res.group())  # www.oldboy.com
    # match直接展示
    
2.分组别名
	res = re.search('www.(?P<content>baidu|oldboy)(?P<hei>.com)', 'www.oldboy.com')
    print(res.group())  # www.oldboy.com
    print(res.group('content'))  # oldboy
    print(res.group('hei'))  # .com
    print(res.group(0))  # www.oldboy.com
    print(res.group(1))  # oldboy
    print(res.group(2))  # .com

网络爬虫简介

网络爬虫:通过编写代码模拟浏览器发送请求获取数据并按照自己指定的要求筛选出想要的数据
小练习:利用正则表达式筛选出红牛所有的分公司数据并打印或保存
    公司名称:
   	 公司地址:
    公司邮编:
    公司电话:

day24——ATM

day27——面向对象前戏、类与对象

人狗大战

# 编写代码简单的实现人打狗 狗咬人的小游戏(剧情需要)
"""推导步骤1:代码定义出人和狗"""
# person1 = {
#     'name': 'jason',
#     'age': 18,
#     'gender': 'male',
#     'p_type': '猛男',
#     'attack_val': 8000,
#     'life_val': 99999999
# }
# person2 = {
#     'name': 'kevin',
#     'age': 28,
#     'gender': 'female',
#     'p_type': '淑女',
#     'attack_val': 1,
#     'life_val': 100
# }
# dog1 = {
#     'name': '小黑',
#     'd_type': '泰迪',
#     'attack_val': 100,
#     'life_val': 8000
# }
# dog2 = {
#     'name': '小白',
#     'd_type': '恶霸',
#     'attack_val': 2,
#     'life_val': 80000
# }
# ps:如果想要定义出多个人和多条狗 上述的字典需要反复编写很多次
"""推导步骤2:将产生人和狗的字典封装成函数并封装人和狗的攻击函数"""
# def create_person(name, age, gender, p_type, attack_val, life_val):
#     person_dict = {
#         'name': name,
#         'age': age,
#         'gender': gender,
#         'p_type': p_type,
#         'attack_val': attack_val,
#         'life_val': life_val
#     }
#     return person_dict
# def create_dog(name, d_type, attack_val, life_val):
#     dog_dict = {
#         'name': name,
#         'd_type': d_type,
#         'attack_val': attack_val,
#         'life_val': life_val
#     }
#     return dog_dict
# p1 = create_person('jason', 18, 'male', '猛男', 8000, 99999999)
# p2 = create_person('kevin', 28, 'female', '淑女', 100, 800)
# d1 = create_dog('小黑', '恶霸', 800, 900000)
# d2 = create_dog('小白', '泰迪', 100, 800000)
# print(p1, p2)
# print(d1, d2)
# 定义出人打狗的动作 狗咬人的动作
# 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')}")
# 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')}")
# person_attack(p1, d1)
# dog_attack(d2, p2)
"""推导步骤3:人和狗的攻击混乱"""
# person_attack(d1, p1)
# dog_attack(p1, d2)

面向对象核心思路前戏

"""推导步骤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('学生选课功能')
# 查看名称空间
# print(Student.__dict__)
# print(Student.__dict__.get('school_name'))
# print(Student.__dict__.get('choice_course'))
'''在面向对象中 类和对象访问数据或者功能 可以统一采用句点符'''
# print(Student.school_name)
# print(Student.choice_course)
# 类的调用>>>:产生对象
'''类名加括号就会产生对象 并且每执行一次都会产生应该全新的对象'''
obj1 = Student() # 变量名obj1接收类名加括号之后的返回值(结果)
obj2 = Student()
obj3 = Student()
# print(obj1,obj2,obj3)
# print(obj1.__dict__)  # 对象自己目前什么都没有
# print(obj2.__dict__)
# print(obj3.__dict__)
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:每个对象手动添加独有的数据'''
# print(obj1.__dict__)
# obj1.__dict__['name'] = 'jason'
# obj1.__dict__['age'] = 18
# obj1.__dict__['hobby'] = 'study'
# print(obj1.__dict__)
# print(obj1.name)
# print(obj1.age)
# print(obj1.hobby)
# print(obj2.__dict__)
# obj2.__dict__['name'] = 'kevin'
# obj2.__dict__['age'] = 28
# obj2.__dict__['hobby'] = 'music'
# print(obj2.__dict__)
# print(obj2.name)
# print(obj2.age)
# print(obj2.hobby)
'''推导流程2:将添加对象独有数据的代码封装成函数'''
# def init(obj, name, age, hobby):
#     obj.__dict__['name'] = name
#     obj.__dict__['age'] = age
#     obj.__dict__['hobby'] = hobby
# stu1 = Student()
# stu2 = Student()
# init(stu1, 'jason', 18, 'music')
# init(stu2, 'kevin', 29, 'read')
# print(stu1.__dict__)
# print(stu2.__dict__)
'''推导流程3:给学生对象添加独有数据的函数只有学生对象有资格调用'''
# class Student:
#     # 对象公共的数据
#     school_name = '清华大学'
#
#     # 专门给学生添加独有数据的功能
#     def init(obj, name, age, hobby):
#         obj.__dict__['name'] = name
#         obj.__dict__['age'] = age
#         obj.__dict__['hobby'] = hobby
#
#     # 对象公共的功能
#     def choice_course(self):
#         print('学生选课功能')
# stu1 = Student()
# Student.init(stu1, 'jason', 18, 'music')
# stu2 = Student()
# Student.init(stu2, 'kevin', 29, 'read')
# print(stu1.__dict__, stu2.__dict__)
'''推导步骤4:init方法变形'''
# class Student:
#     # 对象公共的数据
#     school_name = '清华大学'
#
#     # 专门给学生添加独有数据的功能  类产生对象的过程中自动触发
#     def __init__(obj, name, age, hobby):
#         obj.__dict__['name'] = name
#         obj.__dict__['age'] = age
#         obj.__dict__['hobby'] = hobby
#
#     # 对象公共的功能
#     def choice_course(self):
#         print('学生选课功能')
#
# stu1 = Student('jason', 18, 'read')
# print(stu1.__dict__)
# print(stu1.name)
# print(stu1.school_name)
'''推导步骤5:变量名修改'''
class Student:
    # 对象公共的数据
    school_name = '清华大学'

    # 专门给学生添加独有数据的功能  类产生对象的过程中自动触发
    def __init__(self, name, age, hobby):
        self.name = name  # self.__dict__['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.__dict__['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')
# 1.直接在全局定义功能  该函数就不是学生对象独有的了
# def eat():
#     print('吃东西')
# stu1.eat = eat
# print(stu1.__dict__)
# stu1.eat()
# 2.只能将函数放在类中 但是类中的函数又是对象公共的
'''定义在类中的功能 默认就是绑定给对象使用的 谁来调谁就是主人公'''
# Student.choice_course(123)  # 类调用需要自己传参数
# stu1.choice_course()  #  choice_course(stu1)  对象调用会自动将对象当做第一个参数传入

# stu1.choice_course()
# stu2.choice_course()

# 对象修改数据值
stu1.name = 'tony'  # 当点的名字已经存在的情况下 则修改对应的值
# 对象新增数据值
stu1.pwd = 123  # 当点的名字不存在的情况下 则新增数据
print(stu1.__dict__)

day28——动静态方法、继承、派生方法

动静态方法

在类中定义的函数有多种特性
class Student:
    school_name = '摆烂大学'
    # 1.类中直接定义函数 默认绑定给对象 类调用有几个参数就传几个 对象调用第一个参数就是对象自身 
    def func1(self):
        print('看谁最能摆烂 真的好棒棒哦!!!')
        
    # 2.被@classmethod修饰的函数 默认绑定给类 类调用第一个参数就是类自己 对象也可以调用并且会自动将产生对象的类当作第一个参数传入
    @classmethod
    def func2(cls):
        print('嘿嘿嘿 猜猜我是干嘛滴',cls)
     
    # 3.普通函数 无论谁调用都得传参数
    @staticmethod
    def func3(a):
        print('哈哈哈 猜猜我又是什么',a)
    
obj = Student()
# 1.绑定给对象的方法
# obj.func1()
# Student.func1(obj)
# 2.绑定给类的方法
# Student.func2()
# obj.func2()
# 3.静态方法
# Student.func3(123)
# obj.func3(123)

面向对象之继承的概念

"""
面向对象三大特性
	封装 继承 多态
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()
        # print(C1.name) # 类肯定找的自己的
        obj.name = '你迷了吗' # 由于对象原本没有name属性 该语法会在对象名称空间中创		建一个新的'键值对'
    	print(obj.__dict__)
    	print(obj.name)  # 你迷了吗
    	print(C1.name)
        """
    	对象查找名字的顺序
        1.先从自己的名称空间中查找
        2.自己没有再去产生该对象的类中查找
        3.如果类中也没有 那么直接报错
    	对象自身 >>>    产生对象的类
   		"""
2.单继承情况下名字的查找顺序
	 # class F1:
    #     name = 'jason'
    # class S1(F1):
    #     name = 'kevin'
    # obj = S1()
    # obj.name = 'oscar'
    # print(obj.name)
    '''
    对象自身   >>>   产生对象的类     >>>    父类
    '''
    # class F3:
    #     # name = 'jerry'
    #     pass
    #
    # class F2(F3):
    #     # name = 'tony'
    #     pass
    #
    # class F1(F2):
    #     # name = 'jason'
    #     pass
    #
    # class S1(F1):
    #     # name = 'kevin'
    #     pass
    # obj1 = S1()
    # # obj1.name = '嘿嘿嘿'
    # print(obj1.name)

    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 F1:
    #     # name = 'jason'
    #     pass
    #
    #
    # class F2:
    #     # name = 'oscar'
    #     pass
    #
    # class F3:
    #     # name = 'jerry'
    #     pass
    #
    # class S1(F1, F2, F3):
    #     # name = '嘿嘿嘿'
    #     pass
    # obj = S1()
    # # obj.name = '想干饭'
    # print(obj.name)
    '''
        对象自身   >>>   产生对象的类     >>>    父类(从左往右)
    '''
    class G:
        name = 'from G'
        pass
    class A:
        # name = 'from A'
        pass
    class B:
        # name = 'from B'
        pass
    class C:
        # name = 'from C'
        pass
    class D(A):
        # name = 'from D'
        pass
    class E(B):
        # name = 'from E'
        pass
    class F(C):
        # name = 'from F'
        pass

    class S1(D,E,F):
        pass
    obj = S1()
    # print(obj.name)

    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__)  # {'name': 'jason', 'age': 18, 'course': 'python'}
tea = Teacher('kevin',28,'99')
print(tea.__dict__)  # {'name': 'kevin', 'age': 28, 'level': '99'}

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'
}
# res = json.dumps(d)
# print(res)
"""
序列化报错
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          |
    +-------------------+---------------+
"""
# 1.转换方式1:手动转类型(简单粗暴)
# d = {
#     't1': str(datetime.date.today()),
#     't2': str(datetime.datetime.today())
# }
# res = json.dumps(d)
# print(res)
# 2.转换方式2:派生方法(儒雅高端)
"""
查看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):  # # 判断是否是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 MyClass:
#     school_name = '老女孩大学'
#     _ = '嘿嘿嘿'
#     _name = 'tony'
#     '''类在定义阶段 名字前面有两个下划线 那么该名字会被隐藏起来 无法直接访问'''
#     __age = 18
#     """在python中其实没有真正意义上的隐藏 仅仅是换了个名字而已 _类名__名字"""
#     def __choice_course(self):
#         print('老北鼻正在选课')


# print(MyClass.school_name)
# obj = MyClass()
# print(obj.school_name)
# print(MyClass._)
# print(MyClass._name)
# MyClass.__hobby = 'JDB'  # 无法隐藏
# print(MyClass.__hobby)
# obj = MyClass()
# obj.__addr = '派出所'
# print(obj.__addr)
# print(MyClass.__dict__)
# print(MyClass._MyClass__age)


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('tony')
# 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)
# obj.BMI()  # BMI应该作为人的基本数据而不是方法
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
# f.name = 'jason'  # 触发name.setter装饰器对应的函数name(f,’jason')
# f.name = 123  # 触发name.setter对应的的函数name(f,123),抛出异常TypeError
# del f.name  # 触发name.deleter对应的函数name(f),抛出异常PermissionError

三大特性之多态

多态:一种事物的多种形态
	水:液态 固态 气态
	动物:人 猪 猫 狗
# class Animal:
#     def spark(self):
#         '''叫的方法'''
#         pass
#
#
# class Cat(Animal):
#     # def miao(self):
#     #     print('喵喵喵')
#     def spark(self):
#         print('喵喵喵')
#
#
# class Dog(Animal):
#     # def wang(self):
#     #     print('汪汪汪')
#     def spark(self):
#         print('汪汪汪')
#
#
# class Pig(Animal):
#     # def heng(self):
#     #     print('哼哼哼')
#     def spark(self):
#         print('哼哼哼')

"""
面向对象中多态意思是 一种事物可以又多种形态但是针对相同的功能应该定义相同的方法
这样无论我们拿到的是哪个具体的事务 都可以通过相同的方法调用功能
"""

# s1 = 'hello world'
# l1 = [11, 22, 33, 44]
# d = {'name': 'jason', 'pwd': 123}
# print(s1.__len__())
# print(l1.__len__())
# print(d.__len__())

"""
鸭子类型:只要你看上去像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
是一种动态的编程风格,我们更多关注的是对象的行为而不是对象的类型,我们不用再使用type或者isinstance查出它的类型了
"""

# linux系统
"""
文件      能够读取数据也能够保存数据
内存      能够读取数据也能够保存数据
硬盘      能够读取数据也能够保存数据
......
一切皆文件
"""
# class File:
#     def read(self): pass
#
#     def write(self): pass
#
#
# class Memory:
#     def read(self): pass
#
#     def write(self): pass
#
#
# class Disk:
#     def read(self): pass
#
#     def write(self): pass

'''python永远提倡自由简介大方 不约束程序员行为 但是多态提供了约束的方法'''

import abc

# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod  # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self):  # 抽象方法中无需实现具体的功能
        pass

class Cat(Animal):  # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def xxx(self):
        pass
    def talk(self):
        pass

cat = Cat()  # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化

面向对象之反射

利用字符串操作对象的数据和方法
1.hasattr()	重点
	判断对象是否含有某个字符串对应的属性名或方法名
2.getattr() 重点
	根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3.setattr()
	根据字符串给对象设置或者修改数据
4.delattr()
	根据字符串删除对象里面的名字
    
# class C1:
#     school_name = '小姐姐学院'
#
#     def choice_course(self):
#         print('大宝贝们正在选课')
#
# obj = C1()
'''判断某个名字对象是否可以使用(存在)'''
# 推导思路
# try:
#     obj.xxx
# except AttributeError:
#     print('你木有这个名字')
'''判断用户随意指定的名字对象是否可以使用(存在)'''
# target_name = input('请输入对象可能使用的名字>>>:').strip()
# try:
#     obj.target_name
# except AttributeError:
#     print('你木有这个名字')
"""
字符串的名字跟变量名区别大不大
    'school_name'
    school_name
非常大 完全不一样
"""

# 反射:利用字符串操作对象的数据和方法
# print(hasattr(obj, 'school_name'))  # True
# print(getattr(obj, 'school_name'))  # 小姐姐学院
# print(getattr(obj, 'choice_course'))  # <bound method C1.choice_course of <__main__.C1 object at 0x00000248C0B65A30>>

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.一切皆对象
    	  # 利用反射保留某个py文件中所有的大写变量名及对应的数据值
        import settings
        print(dir(settings))  # dir列举对象可以使用的名字

        useful_dict = {}
        for name in dir(settings):
            if name.isupper():
                useful_dict[name] = getattr(settings, name)
        print(useful_dict)

        # while True:
        #     target_name = input('请输入某个名字')
        #     if hasattr(settings, target_name):
        #         print(getattr(settings, target_name))
        #     else:
        #         print('该模块文件中没有该名字')

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)
        print(obj.name)
        print(obj.pwd)
        print(obj.hobby)
        # print(obj)
        # print(obj)  # 字典存储的数据  {}
        # print(obj.__dict__)  # 字典对象名称空间  {'name': 'jason'}

        print(type(obj))

元类简介

"""推导步骤1:如何查看数据的数据类型"""
# s1 = 'hello world'  # str()
# l1 = [11, 22, 33, 44]  # list()
# d1 = {'name': 'jason', 'pwd': 123}  # dict()
# t1 = (11, 22, 33, 44)  # tuple()
# print(type(s1))  # <class 'str'>
# print(type(l1))  # <class 'list'>
# print(type(d1))  # <class 'dict'>
# print(type(t1))  # <class 'tuple'>
"""推导步骤2:其实type方法是用来查看产生对象的类名"""
# class Student:
#     pass
# obj = Student()
# print(type(obj))  # <class '__main__.Student'>
"""推导步骤3:python中一切皆对象 我们好奇type查看类名显示的是什么"""
class Student:
    pass
obj = Student()
print(type(obj))  # <class '__main__.Student'>
print(type(Student))  # <class 'type'>
class A:pass
class B:pass
print(type(A), type(B))
"""结论:我们定义的类其实都是由type类产生的>>>:元类(产生类的类)"""

创建类的两种方式

# 方式1:使用关键字class
class Teacher:
    school_name = '老女儿'
    def func1(self):pass
print(Teacher)
print(Teacher.__dict__)
# 方式2:利用元类type  type(类名,类的父类,类的名称空间)
cls = type('Student', (object,), {'name':'jason'})
print(cls)
print(cls.__dict__)
"""
了解知识:名称空间的产生
1.手动写键值对
    针对绑定方法不好定义
2.内置方法exec
    能够运行字符串类型的代码并产生名称空间
"""

元类定制类的产生行为

"""
推导
	对象是由类名加括号产生的  	__init__
	类是由元类加括号产生的		__init__
"""
"""所有的类必须首字母大写 否则无法产生"""
# 1.自定义元类:继承type的类也称之为元类
class MyMetaClass(type):
    def __init__(self, what, bases=None, dict=None):
        # print('what', what)
        # print('bases', bases)
        # print('dict', dict)
        if not what.istitle():
            raise TypeError('你是不是python程序员 懂不懂规矩 类名首字母应该大写啊!!!')
        super().__init__(what, bases, dict)


# 2.指定类的元类:利用关键字metaclass指定类的元类
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):
        # 1.产生一个空对象(骨架)
        # 2.调用__init__给对象添加独有的数据(血肉)
        # 3.返回创建好的对象
        # print(args)  # ('jason', 18, 'male')
        # print(kwargs)  # {}
        if args:
            raise TypeError("你怎么回事 Jason要求对象的独有数据必须按照关键字参数传参 我看你是不想干了!!!")
        return super().__call__(*args, **kwargs)


class Student(metaclass=MyMetaClass):
    def __init__(self, name, age, gender):
        # print('__init__')
        self.name = name
        self.age = age
        self.gender = gender


# obj = Student('jason', 18, 'male')
obj = Student(name='jason',age= 18,gender= 'male')
print(obj.__dict__)

魔法方法之双下new

class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        # 1.产生一个空对象(骨架)
        obj = self.__new__(self)
        # 2.调用__init__给对象添加独有的数据(血肉)
        self.__init__(obj,*args, **kwargs)
        # 3.返回创建好的对象
        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__)  # {'name':'kevin', 'age':20, 'gender':'female'}
5.属性的查找顺序
	1.类属性
    	# 直接在类中查找,找不到直接报错
    2.对象属性
    	# 先从自己的内存空间中找,再从产生这个对象的类中找,找不到就报错
6.绑定方法和非绑定方法
	# 绑定方法:绑定给类的方法和绑定给对象的方法
    1.绑定给对象的方法:
    	# 对象来调用 把对象自己当成第一个参数传给方法的第一个形参self
        # 类调用绑定给对象的方法 方法里有几个参数就传几个参数 包括self
    2.绑定给类的方法:
    	在方法的上面加一个装饰器 @classmethod
        # 类调用的时候会把类名当作第一个参数传给方法的第一形参cls
        # 对象在调用的时候会把产生该对象的类名当作第一个参数传给cls
7.非绑定方法
	在方法上面加一个装饰器 @staticmethod
    # 既不绑定给类也不绑定给对象 就是普普通通的函数
    对象和类在调用的时候都不用传参数
8.隐藏属性
	# 如何隐藏?在名字的前面加上__名字
    '''
    	1.在类的定义阶段隐藏的,名字发生了变形:_类名__名字
    	2.在类的外部原则上不能用了(对外不对内),非要用可以点_类名__名字的方式使用
    '''
    # 为什么要隐藏?
    	在类的内部开放一个接口来返回类中隐藏的属性,可以更好的对属性做限制
9.property装饰器
	# 把方法伪装成属性
    '''把方法伪装成属性之后,以后在调用的时候可以不加括号直接调用'''
    # 方法一:添加装饰器
    1.@property
    2.@方法名字.setter
    3.@方法名字.deleter
    # 方法二:在类的内部使用property()函数
    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
	# python2:super(Student,self).__init__()
    # python3:super().__init__()
    '''遇到多继承中出现了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()

# python崇尚的鸭子类型
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.以太网协议
	规定了计算机在出厂的时候都必须有一块网卡网卡上有一串数字
    该数字相当于是计算机的十分钟号码 是独一无二的
    该数字的特征:1216进制数据
    	前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我们可以看成是发短信:只要发了就行 不管对方看不看
"""

三次握手流程图

image-20230327205456425

四次挥手流程图

image-20230327221636919

应用层

应用层相当于是程序员自己写的应用程序 里面的协议非常的多
常见的有:HTTP、HTTPS、FTP
ps:后续框架部分再做介绍

socket模块

如果我们需要编写基于网络进行数据交互的程序 意味着我们需要自己通过代码来控制我们之前所学的OSI七层(很繁琐 很复杂 类似于我们自己编写一个操作系统)
socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口

socket也叫套接字
	基于文件类型的套接字家族(单机)
    AF_UNIX
    基于网络类型的套接字家族(联网)(我们使用的是基于网络的)
    AF_INET
    
 # 获取tcp/ip套接字
  tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
 # 获取udp/ip套接字
  udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

socket基本使用

import socket
socket.socket() 产生socket对象
bind()		    绑定地址
listen()		半连接池
accept()		等待客户端链接
send()			发送消息
recv()			接收消息
connect()		链接

socket代码简介

import socket

"""
以后要养成查看源码编写代码的思路
"""
# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket()  # 括号内不写参数默认TCP协议 family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('127.0.0.1',8080))  # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池(暂且忽略)
server.listen(5)
# 4.等待接客
sock,addr = server.accept()  # returan sock,addr 三次握手(可以看accept的源代码)
print(sock,addr)  # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024)  # 接收客户端发送过来的消息 最大1024字节
print(data.decode('utf8'))
sock.send('尊敬的客人 您说什么就是什么 一切按照您的要求来'.encode('utf8'))  # 给客户端发送消息 主要消息必须是bytes类型
# 6.关闭双向通道
sock.close()  # 四次挥手
# 7.关闭服务端
server.close()


import socket

# 1.生成socket对象指定类型和协议
client = socket.socket
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1',8080))
# 3.直接给服务端发送消息
client.send('大爷有钱 把你们店最好的给我叫出来'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
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

# info = b'hello big baby'
# print(len(info))  # 数据真实的长度(bytes)  14
# res = struct.pack('i', len(info))  # 将数据打包成固定的长度 i是固定的打包模式
# print(len(res))  # 打包之后长度为(bytes)   4           报头

# real_len = struct.unpack('i', res)
# print(real_len)  # (14,)             根据固定长度的报头 解析出真实数据的长度


# desc = b'hello my baby I will take you to play big ball'
# print(len(desc))  # 数据真实的长度(bytes)  46
# res1 = struct.pack('i', len(desc))
# print(len(res1))  # 打包之后长度为(bytes)  4           报头

# real_len1 = struct.unpack('i', res1)
# print(real_len1)  # (46,)              根据固定长度的报头 解析出真实数据的长度

"""
解决黏包问题初次版本
	客户端
		1.将真实数据转成bytes类型并计算长度
		2.利用struct模块将真实长度制作一个固定长度的报头
		3.将固定长度的报头先发送给服务端 服务端只需要在recv括号内填写固定长度的报头数字即可
		4.然后再发送真实数据
		
	服务端
		1.服务端先接收固定长度的报头
		2.利用struct模块反向解析出真实数据长度
		3.recv接收真实数据长度即可
"""

'''问题1:struct模块无法打包数据量较大的数据 就算换更大的模式也不行'''
# res = struct.pack('i', 12313213123)
# print(res)
'''问题2:报头能否传递更多的信息  比如电影大小 电影名称 电影评价 电影简介'''

'''终极解决方案:字典作为报头打包 效果更好 数字更小'''
# data_dict = {
#     'file_name': 'xxx老师教学.avi',
#     'file_size': 123132131232342342423423423423432423432,
#     'file_info': '内容很精彩 千万不要错过',
#     'file_desc': '一代神作 私人珍藏'
# }
# import json
# data_json = json.dumps(data_dict)
# print(len(data_json.encode('utf8')))  # 真实字典的长度  228
# res = struct.pack('i', len(data_json.encode('utf8')))
# print(len(res))

"""
黏包问题终极方案
    客户端 
        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()
# 1.接收固定长度的字典报头
data_dict_head = sock.recv(4)
# 2.根据报头解析出字典数据的长度
data_dict_len = struct.unpack('i', data_dict_head)[0]
# 3.接收字典数据
data_dict_bytes = sock.recv(data_dict_len)
data_dict = json.loads(data_dict_bytes)  # 自动解码再反序列化
# 4.获取真实数据的各项信息
# total_size = data_dict.get('file_size')
# with open(data_dict.get('file_name'), 'wb') as f:
#     f.write(sock.recv(total_size))
'''接收真实数据的时候 如果数据量非常大 recv括号内直接填写该数据量 不太合适 我们可以每次接收一点点 反正知道总长度'''
# total_size = data_dict.get('file_size')
# recv_size = 0
# with open(data_dict.get('file_name'), 'wb') as f:
#     while recv_size < total_size:
#         data = sock.recv(1024)
#         f.write(data)
#         recv_size += len(data)
#         print(recv_size)



import socket
import os
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1', 8081))

'''任何文件都是下列思路 图片 视频 文本 ...'''
# 1.获取真实数据大小
file_size = os.path.getsize(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt')
# 2.制作真实数据的字典数据
data_dict = {
    'file_name': '有你好看.txt',
    'file_size': file_size,
    'file_desc': '内容很长 准备好吃喝 我觉得营养快线挺好喝',
    'file_info': '这是我的私人珍藏'
}
# 3.制作字典报头
data_dict_bytes = json.dumps(data_dict).encode('utf8')
data_dict_len = struct.pack('i', len(data_dict_bytes))
# 4.发送字典报头
client.send(data_dict_len)  # 报头本身也是bytes类型 我们在看的时候用len长度是4
# 5.发送字典
client.send(data_dict_bytes)
# 6.最后发送真实数据
with open(r'/Users/jiboyuan/PycharmProjects/day36/xx老师合集.txt', 'rb') as f:
    for line in f:  # 一行行发送 和直接一起发效果一样 因为TCP流式协议的特性
        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每次切换走之前都要保存当前操作的状态 下次切换回来基于上次的进度继续执行
    
    """
    开了一家饭店 只有一个服务员 但同时来了五桌客人
    	请问:如何让五桌客人都感觉到服务员在服务他们
    		让服务员化身为闪电侠 只要客人有停顿 就立刻切换到其他桌 如此往复
    """

单道技术、多道技术流程图

image-20230329085914299

进程理论

进程与程序的区别
	程序:一堆死代码(还没有被运行起来)
    进程:正在运行的程序(被运行起来了)

进程的调度算法(重要)
	1.FCFS(先来先服务)
    对短作业不友好
    2.短作业优先调度
    对长作业不友好
    3.时间片轮转法+多级反馈队列(目前还在用)
    将时间均分 然后根据进程时间长短再分多个等级
    等级越靠下表示耗时越长 每次分到的时间越多 但是优先级越低

进程的并发与并行

并行
	多个进程同时执行 必须要有多个CPU参与 单个CPU无法实现并行
并发
	多个进程看上去像同时执行 单个CPU可以实现 多个CPU肯定也可以
判断下列两句话孰对孰错
	我写的程序很牛逼,运行起来之后可以实现14个亿的并行量
  		并行量必须要有对等的CPU才可以实现
  	我写的程序很牛逼,运行起来之后可以实现14个亿的并发量
  		合情合理 完全可以实现	以后我们的项目一般都会追求高并发
ps:目前国内可以说是最牛逼的>>>:12306

进程的三状态

就绪态
	所有的进程在被CPU执行之前都必须先进入就绪态等待
运行态
	CPU正在执行    
阻塞态
	进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态

进程三状态转换图

img

day37——同步异步、阻塞非阻塞、进程

同步与异步

用来表达任务的提交方式 

同步
	提交完任务之后原地等待任务的返回结果 期间不做任何事
异步
	提交完任务之后不原地等待任务的返回结果 直接去做其他事 有结果自动通知

阻塞与非阻塞

用来表达任务的执行状态

阻塞
	阻塞态
非阻塞
	就绪态、运行态

综合使用

同步阻塞
同步非阻塞
异步阻塞
异步非阻塞(*****)  # 效率最高

创建进程的多种方式

"""
1.鼠标双击软件图标
2.python代码创建进程
"""
# from multiprocessing import Process
# import time
#
#
# def task(name):
#     print('task is running',name)
#     time.sleep(3)
#     print('task is over',name)
#
# if __name__ == '__main__':
#     # p1 = Process(target=task, args=('jason',))  # 位置参数
#     p1 = Process(target=task, kwargs={'name':'jason123'})  # 关键字参数
#     p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
#     # task()  # 同步
#     print('主')

"""
在不同的操作系统中创建进程底层原理不一样
    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)  # 主进程代码等待3秒
    print(money)  # 主进程代码打印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('主')


# from multiprocessing import Process
# import time
#
# def task(name,n):
#     print('%s is running' % name)
#     time.sleep(n)
#     print('%s is over' % name)
#
# if __name__ == '__main__':
#     p1 = Process(target=task,args=('jason',1))
#     p2 = Process(target=task,args=('jason',2))
#     p3 = Process(target=task,args=('jason',3))
#     start_time = time.time()
#     p1.start()
#     p2.start()
#     p3.start()
#     p1.join()
#     p2.join()
#     p3.join()
#     print(time.time()-start_time)  # 3秒多

IPC机制

IPC:进程间通信
消息队列:存储数据的地方 所有人都可以存 也都可以取

from multiprocessing import Queue

q = Queue(3)  # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
# print(q.full())  # 判断队列是否已满
q.put(222)
q.put(333)
# print(q.full())  # 判断队列是否已满
# 从消息队列中取出数据
print(q.get())
print(q.get())
# print(q.empty())  # 判断队列是否为空
print(q.get())
# print(q.empty())  # 判断队列是否为空
# 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()
    # 主进程往队列中添加数据
    # q.put('我是主进程添加的数据')
    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.join()

守护进程

守护进程会随着守护的进程结束而立刻结束
	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()
    # time.sleep(1)
    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.join方法
2.同进程内多个线程数据共享
3.current_thread()
4.active_count()

GIL全局解释器锁

# 官方文档对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密集型使用多线程,计算密集型使用多进程
		# -io密集型,遇到io操作会切换cpu,假设你开了8个线程,8个线程都有io操作---》io操作不消耗cpu---》一段时间内看上去,其实8个线程都执行了
  
  # -计算密集型,消耗cpu,如果开了8个线程,第一个线程会一直占着cpu,而不会调度到其他线程执行,其他7个线程根本没执行,所以我们开8个进程,每个进程有一个线程,8个进程下的线程会被8个cpu执行,从而效率高
  
##########################理解记忆#######################################	

验证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__':
    # print(os.cpu_count())  # 12  查看当前计算机CPU个数
    start_time = time.time()
    # p_list = []
    # for i in range(12):  # 一次性创建12个进程
    #     p = Process(target=work)
    #     p.start()
    #     p_list.append(p)
    # for p in p_list:  # 确保所有的进程全部运行完毕
    #     p.join()
    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)   # 模拟纯IO操作


if __name__ == '__main__':
    start_time = time.time()
    # t_list = []
    # for i in range(100):
    #     t = Thread(target=work)
    #     t.start()
    # for t in t_list:
    #     t.join()
    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

# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)


def task(n):
    print('task is running')
    # time.sleep(random.randint(1, 3))
    # print('task is over', n, current_thread().name)
    # print('task is over', os.getpid())
    return '我是task函数的返回值'


def func(*args, **kwargs):
    print('from func')

if __name__ == '__main__':
    # 2.将任务提交给线程池即可
    for i in range(20):
        # res = pool.submit(task, 123)  # 朝线程池提交任务
        # print(res.result())  # 不能直接获取
        # pool.submit(task, 123).add_done_callback(func)

线程池爬取网页

import requests
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor


def get_page(url):
    # requests.get('http://www.baidu.com')
    res = requests.get(url)  # 爬取网页
    name = url.rsplit('/')[-1] + '.html'  # www.baidu.com.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()  # 固定编写 用于检测所有的IO操作(猴子补丁)
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()
    # func1()
    # func2()
    s1 = spawn(func1)  # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
    s2 = spawn(func2)
    s1.join()
    s2.join()
    print(time.time() - start_time)  # 8.01237154006958   协程 5.015487432479858

协程实现并发

import socket
from gevent import monkey;monkey.patch_all()  # 固定编写 用于检测所有的IO操作(猴子补丁)
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()  # IO操作
        spawn(communication, sock)

s1 = spawn(get_server)
s1.join()

如何不断的提升程序的运行效率
	多进程下开多线程 多线程下开协程

协程实现高并发

服务端
#######################################服务端########################################
import socket
'''猴子补丁'''
from gevent import monkey;
monkey.patch_all()  # 检测IO
'''协程实现并发:其实是欺骗CPU的行为'''
# 1. 遇到io,2. 切换

'''并发: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()
        # p = Process(target=talk)
        # p.start()
        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()

作者:XxMa

出处:https://www.cnblogs.com/XxMa/p/17337574.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   程序猿小Ma  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示