python基础总结
python基础
学了什么
基础数据类型
流程控制 if while for
文件操作
函数 (定义 调用 参数 返回值 命名空间和作用域 闭包 装饰器 生成器 迭代器 匿名函数 内置函数 递归)
模块 (常用模块 自定义模块 模块的导入 开发规范 包)
面向对象(基础 三大特性 三个装饰器 内置方法 反射)
网络编程(osi协议 socket模块 tcp的粘包 socketserver)
并发编程(操作系统概念 进程 线程 协程)
语言类型
编译型:一次性翻译,(代表语言C)
优点:运行速度快(编译器一般会有预编译的过程对代码进行优化。因为编译 只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。)
缺点:时间长(编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件)
解释型:一行一行翻译(代表语言python)
优点:开发效率高,(翻译时间短)(有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护)
缺点:运行速度慢(每次运行的时候都要解释一遍,性能上不如编译型语言)
python的语言类型
- python是一门动态解释型的强类型的语言
变量
变量由 变量名 赋值 值(字符串、数字、元组、字典、列表等) 组成
- 例 a = '123'
变量命名规则
-
变量名不能使用数字开头
-
变量名由数字、字母、下划线组成
-
变量名要有可描述性
-
变量名不能使用python关键字
-
变量命名不使用中文和拼音
-
变量名区分大小写
-
变量名推荐写法
- 驼峰体 : AgeOfoldBoy(在起类名时常用)
- 下划线:age_of_old_boy
常量
需要全部大写
注释
单行注释(当行注释)用 #表示
多行注释(三引号)
"""注释内容""" 或 '''注释内容'''
被注释的内容不会执行
用户交互
- input(输入)
- print(输出)
流程控制语句(if)
**1. 单 if **
if——如果(条件):条件成立执行
注意 : 缩进 tab或者四个空格(二选一),不能混用
2. if else(二选一)
格式
if条件:
缩进结果
else:
缩进结果
-
if elif elif elif多选一或不选
格式:
if 条件:
结果
elif 条件:
结果
elif 条件:
结果
4 if elif elif else多个选一个
5 if if if if多个条件选多个(相当于多选)
if 条件:
结果
if 条件:
结果
if 条件:
结果
**6. **if嵌套
if 条件:
if 条件:
结果
else:
结果
else:
结果
while 循环
死循环
while True:(都为真,死循环)
循环体
死循环:1.改变条件 2.break:终止当前循环 continue:跳出本次循环,继续下次循环(伪装成循环体中最后一行代码)
break终止当前循环,并且循环的代码不在执行
bresk 必须在循环里
continue ——跳出本次循环,继续下次循环,伪装成循环体中最后一行代码,下方代码不会执行,也必须在循环里使用
while else
while 条件:
循环体
else:
结果
当while循环体中出现了,break就不会再执行else
运算符
算数运算符
以下假设变量: a=10,b=20:
运算符 描述 实例
+加 两个对象相加 a + b 输出结果 30
-减 得到负数或是一个数减去另一个数 a - b 输出结果 -10
*乘 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 200
/ 除 x除以y b / a 输出结果 2
% 取模 返回除法的余数 b % a 输出结果 0
** 幂 返回x的y次幂 a**b 为10的20次方, 输出结果 100000000000000000000
// 取整除 返回商的整数部分 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0
比较运算符
以下假设变量a为10,变量b为20:
运算符 描述 实例
== 等于 比较对象是否相等 (a == b) 返回 False。
!= 不等于 比较两个对象是否不相等 (a != b) 返回 true.
<> 不等于 比较两个对象是否不相等 (a <> b) 返回 true。这个运算符类似 != 。
> 大于 返回x是否大于y (a > b) 返回 False。
< 小于 返回x是否小于y。所有比较运算符返回1表示真,返回0表示假。这分别与特殊的变量True和False等价。注意,这些变量名的大写。 (a < b) 返回 true。
>= 大于等于 返回x是否大于等于y。 (a >= b) 返回 False。
<= 小于等于 返回x是否小于等于y。 (a <= b) 返回 true。
赋值运算符
以下假设变量a为10,变量b为20:
运算符 描述 实例
= 简单的赋值运算符 c = a + b 将 a + b 的运算结果赋值为 c
+= 加法赋值运算符 c += a 等效于 c = c + a
-= 减法赋值运算符 c -= a 等效于 c = c - a
*= 乘法赋值运算符 c *= a 等效于 c = c * a
/= 除法赋值运算符 c /= a 等效于 c = c / a
%= 取模赋值运算符 c %= a 等效于 c = c % a
**= 幂赋值运算符 c **= a 等效于 c = c ** a
//= 取整除赋值运算符 c //= a 等效于 c = c // a
逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:
运算符 逻辑表达式 描述 实例
and x and y 布尔"与" -- 如果 x 为 False,x and y 返回False,否则它返回 y 的计算值。
(a and b) 返回 20。
or x or y 布尔"或" -- 如果 x 是非 0,它返回 x 的值,否则它返回 y 的计算值。
(a or b) 返回 10。
not not x 布尔"非" -= 如果 x 为 True,返回 False 。如果 x为 False,它返回 True。
not(a and b) 返回 False
规则:
and的运算,都为真才为真,有一个是假就是假
and的运算,都为真的时候选择and后边的内容
and的运算,都为假时选择and前边的内容
or的运算,只要有一个真就是真
or的运算,都为真时选择or前边的内容
or的运算,都为假时选择or后边的内容
非 ---不是
not True = False
优先级:
()>not>and>or
成员运算符
除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。
运算符 描述
in(在) 如果在指定的序列中找到值返回 True,否则返回 False。
x 在 y 序列中 , 如果 x 在 y 序列中返回 True。
not in(不在) 如果在指定的序列中没有找到值返回 True,否则返回False x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。
位运算符
按位运算符是把数字看作二进制来进行计算的。
下表中变量 a 为 60,b 为 13,二进制格式如下:
a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
运算符 描述 实例
& 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1, 否则为0 (a & b) 输出结果 12 ,二进制解释: 0000 1100
| 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1。 (a | b) 输出结果 61 ,二进制解释: 0011 1101
^ 按位异或运算符:当两对应的二进位相异时,结果为1
(a ^ b) 输出结果 49 ,二进制解释: 0011 0001
~ 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1 。
~x 类似于 -x-1 (~a ) 输出结果 -61 ,
二进制解释: 1100 0011,在一个有符号二进制数的补码形式。
<< 左移动运算符:运算数的各二进位全部左移若干位,
由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
a << 2 输出结果 240 ,二进制解释: 1111 0000
>> 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位
">>"右边的数指定移动的位数
编码初识
ascii码 支持英文,数字,符号,不支持中文 占一个字节
gbk(国标) 支持:英文,数字,符号 -- ascii 1字节 中文2个字节
Unicode(万国码) 支持:英文,数字,符号 -- ascii 4字节
支持:欧洲 4字节
支持:亚洲 4字节
utf-8 支持:英文,数字,符号 -- ascii 1字节
支持:欧洲 2字节
支持:亚洲 3字节
编码:
encode() # 编码
a = "今天"
s1 = a.encode("utf-8") # 编码
print(s1)
decode() # 解码
c=b'\xe4\xbb\x8a\xe5\xa4\xa9'
print(c.decode('utf-8'))
用什么编码就要用什么解码
网络传输一定是字节
单位转换
1字节8位 1Bytes8bit
1024byte = 1KB
1024KB = 1MB
1024MB = 1GB
1024GB = 1TB
1024TB = 1PB
1024TB = 1EB
1024EB = 1ZB
1024ZB = 1YB
1024YB = 1NB
1024NB = 1DB
基础数据类型
字符串
字符串用于存储少量数据
用"内容"或'内容'(单引号 或多引号)引起来的内容就是字符串
字符串中的每个字母或字符都称为元素
查找字符串中的元素
-
索引(下标)
1. 从左往右时排从零开始
2.从右向左排时从-1开始
索引的时候不能超出索引的最大值,否则报错
-
切片(顾头不顾尾)
a=[起始位置:终止位置]当省略时从0开始,到最后结束
-
步长
a=[起始位置:终止位置:方向和步长+代表方向]
字符串中的方法:
is系列 是进行判断 返回的是布尔值
isdigit()判断字符串中是否全是阿拉伯数字,结果是bool值
isdecimal()判断字符串是否是十进制的
isalnum()判断是不是数字字母汉字
ispha()判断是不是字母中文
upper 全部大写 lower全部小写 capitalize 首字母大写
title每个单词的首字母大写 swapcase 大小写转换
starswith 以..开头 endswith 以...结尾
center 居中 (填充)
find 查找(通过元素查找索引,找不到返回-1)
index 查找(通过元素查找索引,查找不到报错)
join 拼接 (将列表转化为字符串) print("_".join(["1","2","4"]))
count 统计
strip脱去两端的空格换行以及制表符
split 分割 (以空格和换行以及制表符进行分割,也可以以指定的方式进行分割)
replace 替换
format 字符串格式化
列表
定义:它是以[]括起来,每个元素以逗号隔开,可以存放各种数据类型
可变有序的数据类型 支持索引,切片,步长和增删改查
列表的增 append insert extend
insert 方法用于将对象插入到列表中,而append方法则用于在列表末尾追加新 的对象,
extend 把一个可迭代对象打开.每一项拿出来追加到list中 ,他们三个有个共同 点就是没有返回值。
列表的删:remove pop del clear
1、 pop. 向外弹出最后一个或指定元素的索引,有返回值,可用一个变量接受 被删除的内容,指定索引不存在时,会报错。
2、remove 指定元素进行删除 是元素不是索引,删除不存在的元素会报错。
3、 clear 清空列表
4、del 列表 将整个列表删除切片删除
del lst[1:3] 将指定切片范围内的元素进行删除
列表的改:索引、切片、步长
索引修改 li1 = [1, 2, 3, {'a':1, 'b':2}] li1[0] = 'a' # 改变1
切片修改(范围或区间)不带步长或者是步长为1的切片,则不用关心切片与 修改的元素个数是否相等。
注意:步长不为1,要注意切片内的元素个数是否与更改的值是否相等。
如果步长不是1, 要注意. 元素的个数必须相同
列表的查:
for循环
列表的嵌套:[......] 视为一个元素
列表的其他操作
1、count
查询列表中某个元素出现的次数
c = lst.count("太白") # 查询太白出现的次数
2、降序
lst.sort(reverse=True)
lis.sort(func=None, key=None, reverse=False(or True))
与sorted 的区别是sort是原地修改,只能用于列表,而sorted是返回一个新的 序列,可以用在任意一个可迭代对象中
3、reverse
原地翻转
lst.reverse()
4、len
列表的长度
5、index
返回从左到右列表中某个元素出现的第一个索引值
l st = [1,23,4,5,7,8,9]
print(lst.index(4)) # 通过元素查找索引
lst.sort() # 排序 默认是升序
lst.sort(reverse=True) # 降序
lst = [1, 23, 4, 5, 7, 8, 9]
lst.sort()
lst.reverse() # 人工降序
print(lst)
lst = [1, 23, 4, 5, 7, 8, 9]
lst.reverse()
print(lst) # 将源数据进行反转
lst = [1, 23, 4, 5, 7, 8, 9]
lst1 = lst[::-1]
print(lst)
print(lst1) # 不修改源数据进行反转
lst = [1, 2, 3, 4]
lst = lst + [1, 2, 3]
print(lst) # [1,2,3,4,[1,2,3],] [1,2,3,4,1,2,3]
lst = [1, 2, 3] * 5
print(lst)
print(id(lst[0]), id(lst[3]))
元组
关键字:tuple
元组就是一个不可变的列表
单个数字或字符需要加逗号才算是元组
- (3,)
有序,不可变数据类型
元组的方法(只有查):
-
统计
tu=(1,2,3,4,5,6,1,2,3)
print(tu.count(1))
-
获取索引
print(tu.index(2))
元组的用途:
保存不想更改的大量数据,多用于配置文件
range范围
range(1,10)[起始位置:终止位置]顾头不顾尾,可迭代对象
range(1,10,2)[起始位置:终止位置:步长]
(10代表的是终止位置,起始位置默认为0)
range的诞生是为了解决不能循环数字
字典
关键字(dict)
所有的操作都是通过键
键:必须是不可变数据类型
值:任意
字典是无序可变的数据类型
字典的增:
通过键值对添加(强加)
字典名['键]="值"
有则修改,无则不加
字典名.setdefault(键 , 值 )
字典的删:
pop clear del 字典中没有remove
字典名.pop() 通过字典的键进行删除
字典名clear() 清空整个列表
del.字典名[''键”] 通过键进行删除
字典的改
字典名['键]="值"
update(更新)
d={'x':5}
c={'12':65}
c.update(d)
print(c)
字典的查
dic.get("键") 查询不到返回None,查找不到的时候,返回自己制定的内容
dic.setdefault(" 键 ")查询不到返回None
print(dic.keys( )) 获取一个键高仿列表
print(dic.values())获取一个值高仿列表
高仿列表支持迭代,不支持索引
for i in dic :
print(i,dic[i]) 打印键值
lst.append(i,dic[i])
for i in dic.items():(键和值)
print(lst(dic.items()))
字典的其他:
dic = {"key":1,"key1":2,"key2":4,"key3":1}
print(dic.po
pitem()) # 随机删除 python3.6版删除最后一个键值对
popitem返回的是被删除的键值对
print(dic)
面试题:
dic = {}
dic.fromkeys("abc",[])
# 批量创建键值对 "a":[],"b":[],"c":[]
print(dic)
dic = {}
dic.fromkeys("abc", [])
# 批量创建键值对 "a":[],"b":[],"c":[]
print(dic)
dic = {}
dic = dic.fromkeys("abc", [])
print(dic)dic["b"] = 11
dic["a"].append(10)
print(dic)
fromkeys 第一个参数必须是可迭代对象,会将可迭代对象进行迭代,成为字典的键.第二个参数是值(这个值是共用的)
fromkeys 共用的值是可变数据类型就会有坑,不可变数据类型就没事
解构
a,b = b,a 一行代码进行数值交换
for k, v in dict.items():
print(k,v) 字典的键和值
集合
关键字:set 集合就是没有值的字典(天然去重)
可变无序的数据类型
一行代码去重
print(list(set(去重的内容))
集合的增:
add(直接添加) update(迭代添加)
集合的删:
remove(指定元素删除) clear(清空) pop(随机删,通常是最小的)
集合的改:
先删后加
集合的查:
只有for循环
集合的其他操作
s1 = {"刘能", "赵四", "⽪⻓⼭"}
s2 = {"刘科⻓", "冯乡⻓", "⽪⻓⼭"}
交集
两个集合中的共有元素
print(s1 & s2) # {'⽪⻓⼭'}
print(s1.intersection(s2)) # {'⽪⻓⼭'}
并集
print(s1 | s2)
{'刘科⻓', '冯乡⻓', '赵四', '⽪⻓⼭', '刘能'}
print(s1.union(s2))
{'刘科⻓', '冯乡⻓', '赵四', '⽪⻓⼭', '刘能'}
差集
print(s1 - s2) # {'赵四', '刘能'} 得到第⼀个中单独存在的
print(s1.difference(s2))
{'赵四', '刘能'}
反交集(对称差集)
print(s1 ^ s2) # 两个集合中单独存在的数据
{'冯乡⻓', '刘能', '刘科⻓', '赵四'}
print(s1.symmetric_difference(s2))
{'冯乡⻓', '刘能', '刘科⻓', '赵四'}
s1 = {"刘能", "赵四"}
s2 = {"刘能", "赵四", "⽪⻓⼭"}
⼦集
print(s1 < s2) # set1是set2的⼦集吗? True
print(s1.issubset(s2))
超集
print(s1 > s2) # set1是set2的超集吗? False
print(s1.issuperset(s2))
数据类型的各种转换
str -- int
int -- str
str -- bool
bool -- str
int -- bool
bool -- int
list -- tuple 元组
lst = [1,23,5,4]
print(tuple(lst))
tuple -- list
tu = (1,23,5,4)
print(list(tu))
list -- set
lst = [1,23,12,31,23]
print(set(lst))
set -- list
tuple -- set
tu = (1,2,3,4,5)
print(set(tu))
set -- tuple
目前字典转换,自己实现方法
例:有字符串"k: 1|k1:2|k2:3 |k3 :4" 处理成字典 {'k':1,'k1':2,'k3':4}
dic = {}
a=a.split('|')
print(a)
for i in a:
print(i)
i=i.split('😂
dic.setdefault(i[0],int(i[1]))
print(dic)
字典和集合在遍历(循环)时不能修改原来的大小(字典的长度),可以进行修改值
s = {1,2,3,4,5,6}
for i in s:
s.pop()
print(s)
列表转字符串
list -- str
lst = ["1","2","3"]
print("".join(lst))
字符串转列表
str -- list
s = "alex wusir 太白"
print(s.split())
基础数据类型总结:
可变、不可变、有序、无序
可变:list列表、 dict字典、set集合
不可变:int整型 str字符串 bool布尔值 tuple元组
有序:list 列表 tuple元组 str
无序: dict字典 set 集合
按取值方式:
1.索引:list 列表 tuple元组 str 字符串
2.键: dict字典
3.直接取值:int整型 bool布尔值 set 集合
驻留机制:节省空间,提高效率(减少了开辟空间和销毁空间的耗时间
小数据池和代码块同时存在时,先执行代码块
小数据池和代码块
小数据池支持的数据类型:int、str、bool(整型、字符串、布尔值)
代码块:一个函数,一个py文件,终端的每一行都是代码块
== 和 is的区别(id()查看内存地址)
== 是比较两端的值是否相等
is是比较两端的内存地址是否相等
print(id(a),id(b))查内存地址
小数据池中
-
int:-5~256
-
str:1. 字母数字长度任意符合驻留机制
2.字符串进行乘法的时候,总长度不能超过20位
3.特殊符号进行乘法的时候只能乘零
代码块中:
-
int:-5~正无穷
-
str:1.定义字符串的时候可以是任意的
2.字符串(字母,数字)进行乘法是总长度不能超过20
3.特殊符号(中文,符号)进行乘法的时候只能乘以0或1
驻留机制:节省空间,提高效率(减少了开辟空间和销毁空间的耗时间)
小数据池和代码块同时存在时,先执行代码块
深浅拷贝
赋值,=
浅拷贝:只拷贝第一层元素的地址,只有修改第一层(不可变类型)的时候原数据不受影响,给可变数量类型添加的时候原数量会受影响
==是修改 .append是添加
可变数据类型能够修改和添加,不可变数据类型只能修改
深拷贝:不可变数据类型共用,可变数据类型新开辟一个空间
数字和字符串
对于字符串和数字,元组等不可变数据类型,赋值和深浅拷贝毫无意义,因为同时指向的是同一个内存地址
其他数据类型
对于字典、列表其他可变数据类型进行赋值、深拷贝 和浅拷贝时,内存地址是不同的
-
赋值
赋值只是创建一个变量,该变量指向原来的内存地址
-
浅拷贝
在内存中只额外创建第一层数据
-
深拷贝
在内存中将所有的数据重新创建一份
python文件操作
例:
f = open('联系方式.txt',mode='r',encoding='utf-8')
content = f.read()
print(content)
f.close()
f 可写成任意变量等,它被称作:文件句柄,文件操作符,或者文件操作对象等。
open 是Python调用的操作系统(windows,linux,等)的功能,
mode为打开方式:常见的有r,w,a,r+,w+,a+.rb,wb,ab,等,默认不写是r。
encoding='utf-8'(编码集)
流程就是打开文件,产生一个文件句柄,对文件句柄进行相应操作,关闭文件。
r 模式(只读)
rb (读字节)
rb 读出来的数据是bytes类型,在rb模式下,不能encoding字符集
rb的作用:
在读取非文本文件的时候,比如要读取mp3,图像,视频等信息的时候就需要用到rb,因为这种数据是没办法直接显示出来的
这个字节的模式是用于传输和存储
r+模式(先读后写)
r+模式一定要记住是先读后写
对于读写模式,必须是先读后写,因为光标默认在开头位置,当读完了以后再进行写入.
深坑请注意: 在r+模式下. 如果读取了内容. 不论读取内容多少. 光标显示的是多少. 再写入 或者操作文件的时候都是在结尾进行的操作.
读的其他操作
print(f.read) 全部读取
print(f.read(11) ) 按照字符读取 (指定读取的内容数量)
print(f.readline()) 读取一行,默认尾部有一个\n
print(f.readline().strip) 将\n去除
print(f.readlines()) 一行一行读取,全部存储在列表中
(将所有内容全部读取出来)
路径
相对路径:相对于某个文件进行查找
绝对路径:从磁盘根部进行查找
w 覆盖写
注意:
1. 当选择使用w模式时,在打开文件的时候就会把文件的所有内容全部清空,然后进行操作
2. 如果文件不存在,使用w模式会创建文件,文件存在w模式是覆盖写
在写文件的时候,写完一个文件就要刷新一下(f.flush())
wb读字节(和rb相似)
w+写读
w+模式下 其实和w模式一样,把文件清空了,在写的内容.所以很少人用
a追加
只要是a或者ab,a+都是在文件的末尾写入,不论光标在任何位置.
在追加模式下,我们写入的内容后追加在文件的末尾
a模式如果文件不存在就会创建一个新文件
a+模式下,不论是先读还剩后读,都是读不到数据的
其他操作
seek()
seek(n)光标移动到n位置,注意: 移动单位是byte,所有如果是utf-8的中文部分要是3的倍数
seek() 移动光标
f.seek(0,0) # 移动光标到文件的头部,可以看成seek(0)
f.seek(0,1) # 移动光标到当前位置
f.seek(0,2) # 移动光标到文件末尾
f.seek(6) # 光标是按照字节移动
f=open("密码","r",encoding="utf-8")
print(f.read(5))
f.seek(0,0)
f.seek(0,1)
f.seek(0,2)
print(f.read())
f=open("密码","r",encoding="utf-8")
f.seek(6)
print(f.read(3))
f=open("密码","r",encoding="utf-8")
print(f.read(3))
print(f.tell())
f.close()
tell()
使用tell()可以帮我们获取当前光标在什么位置
f = open("小娃娃", mode="r+", encoding="utf-8")
f.seek(0) # 光标移动到开头
content = f.read() # 读取内容, 此时光标移动到结尾
print(content)
f.seek(0) # 再次将光标移动到开头
f.seek(0, 2) # 将光标移动到结尾
content2 = f.read() # 读取内容. 什么都没有
print(content2)
f.seek(0) # 移动到开头
f.write("张国荣") # 写入信息. 此时光标在9 中⽂文3 * 3个 = 9
print(f.tell()) # 光标位置9
f.flush()
f.close()
修改文件:
import os
f = open('a2',mode='r',encoding='utf-8')
f1 = open('a1',mode='r',encoding='utf-8')
for i in f:
i = i.replace('文件中的内容',"要替换的内容")
f1.write(i)
f.close()
f1.close()
os.rename("a2","a3")
os.rename("a1","a2")
文件操作的目的:持久化,永久存储
函数
函数式编程最重要的是增强代码的重用性和可读性,就是对一个代码块或者功能的封装. 什么时候用, 什么时候执行
函数初识
什么是函数:将某个功能封装到一个空间中,就是一个函数。
减少重复代码
# 1.函数的定义
# def 函数名():
# 函数体 # 定义函数的时候函数体不执行,函数体中存放的是代码 #
2.函数的调用
# 函数名() # 1.启动函数 # 2.接受返回值
# 3.函数的返回值
# return 关键字
# return "字符串" 返回的是字符串
# return [1,2,3,] 返回的是列表
# return 1,2,3,4 以元组的形式显示
# return 下方的代码不会执行,并且会终止当前的函数
# return 不写或者写了return没有写值 返回的是None
函数的定义主要有如下要点:
-
def:表示函数的关键字
-
函数名:函数的名称,日后根据函数名调用函数
-
函数体:函数中进行一系列的逻辑计算,如:发送邮件、计算出 [11,22,38,888,2]中的最大数等...
-
参数:为函数体提供数据
-
返回值:当函数执行完毕后,可以给调用者返回数据。
def func(): def 关键字 func函数名 ():格式(必须写) print('nihao') 函数体 return "你好" 返回值 func() 函数名() 调用函数 print(func()) 输出返回值
函数的返回值
函数在执行过程中或者执行完毕. 可以使用return返回给调用者一个结果。只要程序执行到return. 函数就会被停止 后面的内容就不会再执行。
相当于break 跳出循环
-
直接写个return或者不写return, 不返回任何内容, 接收的是None
-
return 一个变量或者值(返回值)
-
return 多个变量或者值. 返回的时候解释器会帮我们把多个返回值组装成一个tuple
接收的地方可以接收一个变量. 多个变量. 解构成多个变量
函数的参数
位置参数:
形参
一一对应
默认参数:参数定义时,括号中写好的就是默认参数
不进行传参是使用默认,进行传参是使用传递的参数。
位置参数在默认位置前
位置参数和默认参数不能同名
形参:函数定义阶段括号中的参数的参数叫形参
实参:函数调用阶段括号中的参数叫实参
关键字参数:按名字进行传参
混合传参;
传参:将实参传递给形参的过程
注意点: 位置参数和默认参数不能同名
优先级不能方反
位置参数一一对应
参数传递后可以不使用
return 不能终止循环
# 4.函数的参数
#形参:
# 函数定义阶段是形参
#实参:
# 函数调用阶段是实参
#传参:
# 将实参传递给形参的过程就是传参
# 形参:
# 位置参数
# 默认参数
# 混合参数
优先级: 位置参数 > 默认参数
# 实参:
# 位置参数
# 关键字参数
# 混合参数
优先级: 位置参数 > 关键字参数
# 注意点: 参数名字不能重复,优先级不能放反
# 位置参数是一一对应
# 参数传递后,可以不使用
# return 不能终止循环
三元运算(三目运算)只支持if 。。。else
编写结构:
条件成立的结果(a) 条件(if a>b else) 条件不成立的结果
def func(a,b)
return a if a>b else b
print(func(8,9))
动态参数
动态位置参数 *args
*args 空元组 大家都用的名字,不建议修改
def eat(*args) 函数定义阶段 * 是聚合(打包)
(*args) 函数体中的 *打散(解包) 输出都是元组
位置参数>动态位置参数>默认参数
动态关键字参数 ** kwargs
** kwargs 空字典 大家都用的名字,不建议修改
在函数定义阶段* 和** 都是聚合
在函数体中* 就是打散 ,*args是元组中的元素进行打散 *kwargs是字典的键获取
应用场景: 数据库(字典)
形参:
位置参数
动态位置参数先执行位置参数,位置参数接受后额外的参数动态位置进行接受,获取一个元组
默认参数
动态关键字参数
总结:
*args(聚合位置参数) 大家伙都用的名字, 可以进行修改但是不建议修改
**kwargs(聚合关键字参数) 大家伙都用的名字, 可以进行修改但是不建议修改
函数的定义阶段 * 和 ** 都是聚合
函数体中 * 就是打散, *args将元组中的元组进行打散 *kwargs将字典的键获取
形参:
位置参数:
动态位置参数: 先执行位置参数,位置参数接受后额外的参数动态位置参数进行接受 获取的 是一个元组
默认参数:
动态关键字参数(默认): 先执行默认参数,默认参数接受后额外的默认参数动态默认参数进行 接受,获取的是一个字典
实参和函数体:
*和** 都是打散
** 实参时能够使用目的是将字典一个一个添加到 **kwargs中
函数的注释
def b(a:int,b:int):
"""
求差
:param a: int
:param b: int
:return: int
"""
return a - b
print(a.__doc__)
print(b.__doc__) # 查看函数的注释
print(a.__name__) # 查看函数的名字
def cen(a:int,b:int):
"""
求积
:param a: int
:param b: int
:return: int
"""
return a*b
print(cen.__doc__)
print(cen.__name__)
命名空间
在python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内 存, 表示这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数 内部声明的变量来进⾏开辟变量的内部空间. 随着函数执⾏完毕, 这些函数内部变量占⽤的空 间也会随着函数执⾏完毕⽽被清空。
def fun():
a = 10
print(a)
fun()
print(a) # a不存在了已经..
我们给存放名字和值的关系的空间起⼀个名字叫: 命名空间. 我们的变量在存储的时候就 是存储在这片空间中的.
命名空间的分类
- 全局命名空间--> 我们直接在py⽂件中, 函数外声明的变量都属于全局命名空间
- 局部命名空间--> 在函数中声明的变量会放在局部命名空间
- 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序
- 内置命名空间
- 全局命名空间
- 局部命名空间(函数被执⾏行行的时候)
取值顺序
- 局部命名空间
- 全局命名空间
- 内置命名空间
作用域命名空间
作用域: 作⽤域就是作用范围, 按照生效范围来看分为全局作用域和局部作用域
全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循 从上到下逐行执⾏).
局部作用域: 在函数内部可以使⽤。
- 全局作用域: 全局命名空间 + 内置命名空间
- 局部作⽤域: 局部命名空间 我们可以通过globals()函数来查看全局作⽤域中的内容, 也可以通过locals()来查看局部作用域中的变量和函数信息
a = 10 # 全局
def func(): # 全局
a = 40 # 局部
b = 20 # 局部
def abc(): # 局部
d = 30 # 是局部
print("哈哈")
print(a, b) # 这⾥使⽤的是局部作⽤域
print(locals()) # {'d': 30, 'b': 20, 'a': 40}
abc()
print(globals()) # 打印全局作用域中的内容
print(locals()) # 打印局部作用域中的内容,locals()打印当前这个函数局部作用与中的内容
func()
print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x103ed2710>,
'__spec__': None, '__file__': '/Volumes/day10/day10/day10/04 命名空间.py', '__builtins__': <module 'builtins' (built-in)>, 'a': 10,
'func': <function func at 0x1052d6840>}
print(globals()) # 打印全局作用域中的内容
函数嵌套
-
只要遇到 函数名()就是调用,如果没有()就不是函数的调用
混合嵌套: # def f1(): # print(11) # # def f2(): # print(22) # f1() # # def f3(): # print(33) # f1() # # def run(): # f3() # f2() # f1() # run() # # def func(a): # print(a) # return f1(foo(a)) # # def foo(b): # print(b) # return b + 5 # # def f1(args): # return args + 10 # # print(func(5)) # def foo(a): # a = 10 # def f1(b): # c = b # def foo(c): # print(c) # print(foo.__doc__) # foo(c) # print(b) # f1(a) # print(a) # foo(25) # def foo(): # a = 10 # func(a) # # def func(a): # print(a) # # foo()
关键字 global和nonlocal
global : 只修改全局
nonlocal : 只修改局部,修改离nonlocal最近的一层,上一层没有继续向上上层查找.只限在局部
# a = 10
# def func():
# global a
# a = a - 6
# print(a)
#
# print(a)
# func()
# print(a)
# a = 100
# def func():
# b = 10#局部变量b=10
# def foo():
# b = a #将a的值赋给b为100后修改为105
# def f1():
# nonlocal b #修改局部变量b为105
# b = b + 5
# print(b) # 105
# f1()
# print(b) # 105
# foo()
# print(b) # 10
# func()
# print(a) # 100
函数的第一类名称和使用
函数名本质上就是函数的内存地址,是一个特殊的变量,与括号配合可以执行函数的变量
- 可以被引用,赋值给其他的变量
- 可以当做容器中的元素
- 可以将函数当做另一个函数的参数
- 可以当做函数的返回值
函数名可以当一个变量赋值给另一个变量
def cen():
print("呵呵")
print(cen)#函数的地址
a=cen#将函数的地址当一个变量赋给另一个变量
a() #相当于调用cen()函数
函数名可以当容器类的元素
def cen1():
print("呵呵")
def cen2():
print("呵呵")
def cen3():
print("呵呵")
def cen4():
print("呵呵")
ls=[cen1,cen2,cen3,cen4]
for i in ls:
i()
函数名可以当做函数的参数
def cen():
print("吃了吗")
def cen1(fn):
print("我是cen1")
fn()
print("我是cen1")
cen1(cen)
函数名可以当函数的返回值
def func_1():
print("这里是函数1")
def func_2():
print("这里是函数2")
print("这里是函数1")
return func_2
fn = func_1()
# 执行函数1. 函数1返回的是函数2, 这时fn指向的就是上面函数2
print (fn() ) # 执行func_2函数
def foo(a):
def func(a):
def f1(a):
print(a)
return "aelx"
return f1(a)
return func(a)
print(foo(5))
结果:
5
aelx
f-strings
name="小明"
sex="男"
age="18"
print(f"姓名:{ name} 性别:{sex} 年龄:{age}")
print(f"{3*4}")
name="asddf"
print(f"{name.upper()}")
def sum_a_b(a,b):
return a + b
a = 1
b = 2
print('求和的结果为' + f'{sum_a_b(a,b)}')
迭代器
只要有____iter____()方法的就是一个可迭代对象
具有____iter____()和_______next___()两个方法才是迭代器
可迭代对象用iter(可迭代对象)或 可迭代对象.iter_()都可以吧可迭代对象转化成迭代器。
重点 for循环本质
while True:
try:
print(l.__next__())
except StopIteration:
break
可迭代对象和迭代器的缺点:
-
占用内存。
-
可迭代对象不能迭代取值(除去索引,key以外)。
迭代器的优点:
节省内存。
迭代器在内存中相当于只占一个数据的空间:因为每次取值都上一条数据会在内存释放,加载当前的此条数据。
惰性机制。 next一次,取一个值,绝不过多取值。
迭代器的缺点:
-
不能直观的查看里面的数据。
-
一次性用完就没了
-
取值时不走回头路,只能一直向下取值。
-
可迭代对象:
是一个私有的方法比较多,操作灵活(比如列表,字典的增删改查,字符串的常用操作方法等),比较直观,但是占用内存,而且不能直接通过循环迭代取值的这么一个数据集。
应用:当你侧重于对于数据可以灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择。
-
迭代器:
是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)。
空间换时间: 容器存储大量的元素,取值时 取值时间短,但是容器占用空间较大
时间换空间: 迭代器就是节省了空间,但是取值时间较长
迭代器是基于上一次停留的位置,进行取值可迭代对象:
- 具有iter()方法就是一个可迭代对象迭代器:
- 具有iter()和next()方法就是一个迭代器
生成器
区别
生成器和迭代器实际功能没有区别
最大的区别就是:迭代器是python自带的
生成器是自己写的
生成器的构建方式
- 通过生成函数 yield()
- 通过生成器推导式
- python内置函数或者模块提供(其实1,3本质上差不多,都是通过函数的形式生成,只是通过1是自己写的生成器函数,3是python提供的生成器函数而已)
生成器函数
def cen():
print(11)
yield 22
ret=cen()
print(ret)
结果:<generator object cen at 0x000002301463DC00>
取值:
def cen():
print(11)
yield 22
ret=cen()
l=ret.__next__()
print(l)
结果:
11
22
当程序运行到最后一个yield.那么后面继续运行next()程序会报错,一个yield对应一个next,next超过yield数量就会报错,与迭代器一样。
yield和return的区别
-
return一般在函数中只设置一个,他的作用是终止函数,并且给函数执行者返回值。
-
yield在生成器函数中可设置多个,他的作用是暂停函数,next会获取对应yield生成的元素。
def cen():
lst=[]
for i in range(100):
lst.append("你好"+str(i))
return lst
e=cen()
print(e)
这个比较浪费空间,并且效率不高
def cen():
for i in range(100):
yield "你好"+str(i)
e=cen()
for i in range(10):
print( e.__next__())
结果:
这里的是产生几个我用几个
可迭代对象
优点:list,tuple,str 节省时间,取值方便,使用灵活(具有自己的是有方法)
缺点:大量消耗内存
迭代器
优点:节省空间
缺点:不能直接查看值,使用不灵活,消耗时间一次性不可逆
生成器:
优点:节省空间,可以自己定义
缺点:不能直接查看数据,消耗时间,一次性,不可逆行
生成器使用场景
-
当文件或容器的数据量较大时建议使用生成器
数据类型(python3:range()| python2:xrange())都是可迭代对象__iter__()
文件句柄是迭代器 iter()和__next__()
区别生成器和迭代器
-
生成器的函数体中有 yield 迭代器中没有yield
-
迭代器的地址 <list_iterator object at 0x000000987B6E97F0>
生成器的地址 <generator object func at 0x00000087C2A10CA8>
-
有 send 方法就是一个生成器,没有send方法的就是一个迭代器
yield from 将列表中的数据一个一个区出来
def func():
def foo():
print(11)
lst = {"key":1,"key1":2}
yield foo
print(func().__next__())
def func():
lst = [1,2,3,45,6]
lst1 = ["alex","wusir","taibi","baoyuan"]
yield from lst
yield from lst1
g = func()
for i in g:
print(i)
lst = [1,2,3,45,6]
lst1 = ["alex","wusir","taibi","baoyuan"]
for i in lst,lst1:
print(i)
yield 将可迭代对象逐个返回
yield from将可迭代对象一次性返回
推导式
列表推导式
普通循环
print([i for i in range(10)])
print([变量 for循环])
筛选
lst = []
for i in range(10):
if i % 2 == 0:
lst.append(i)
print(lst)
筛选模式
print([i for i in range(10) if i % 2 ==0])
print([i for i in range(10) if i > 2])
[加工后的变量 for循环 加工条件]
集合推导式:
普通循环
print({i for i in range(10)})
{变量 for循环}
筛选模式
print({i for i in range(10) if i % 2 == 1})
{加工后的变量 for循环 加工条件}
字典推导式:
普通循环
print({i: i+1 for i in range(10)})
{键:值 for循环}
筛选模式
print({i: i+1 for i in range(10) if i % 2 == 0})
{加工后的键:值 for循环 加工条件}
生成器推导式:
普通模式
tu = (i for i in range(10))
( 变量 for循环)
筛选模式
tu = (i for i in range(10) if i > 5)
(加工后的变量 for循环 加工条件)
for i in tu:
print(i)
内置函数
all() any() bytes() callable() chr() complex() divmod() eval() exec() frozenset() globals() hash() help() id() input() int() iter() locals() next() oct() ord() pow() repr() round()
enumerate() open() range() len() str() list() tuple() dict() set() print() sum() abs() dir() zip() format() reversed() filter() map() sorted() max() min() reduce()
classmethod() delattr() getattr() hasattr() issubclass() isinstance() object() property() setattr() staticmethod() super()
a = "88 + 99"
a = """
def func():
print(111)
func()
"""
print(type(a))
print(eval(a)) # 神器一
exec(a) # 神器二
注意:千万记住 禁止使用
exec(input("请输入内容:"))
print(hash("123"))
print(hash(12))
print(hash(-1))
print(hash(-10))
print(hash((2,1)))
dic = {[1,2,3]:2}
print(hash([1,2,3]))
hash() 作用就是区分可变数据类型和不可变数据类型
lst = [1,2,3]
help(list) 查看帮助信息
def func():
print(1)
lst = [1,23,4,]
print(callable(lst)) # 查看对象是否可调用
print(int("010101",16))
print(float(3))
print(int(3))
print(complex(20)) # 复数
print(bin(100)) # 十进制转二进制
print(oct(10)) # 十进制转八进制
print(hex(17)) # 十进制转十六进制
print(divmod(5,2)) # (商,余)
print(round(3.534232,2)) # 保留小数位
print(pow(2,2)) #幂 pow 两个参数是求幂
print(pow(2,2,3)) #幂 pow 两个参数是求幂后的余
s = "你好"
s1 = bytes(s,encoding="utf-8") # 将字符串进行编码
print(str(s1,encoding="utf-8"))
print(s.encode("utf-8"))
print(ord("你")) # 通过元素获取当前(unicode)表位的序号
print(chr(20320)) # 通过表位序号查找元素
a = 'alex'
print(repr(a)) #查看你数据的原生态 -- 给程序员使用的
print(a) # 给用户使用的
lst = [1,2,0,4,5]
print(all(lst)) # 判断容器中的元素是否都位真 and
lst = [1,2,3,0,1,23] # 判断容器中的元素是否有一个为真
print(any(lst))
a = 10
def func():
a = 1
print(locals()) # locals查看当前空间变量
print(1)
func()
print(globals()) # globals查看全局空间变量
str() 将字节转换成字符串
byte_str = bytes("你好",encoding="utf")
print(byte_str)
print(str(byte_str,encoding="utf-8"))
list() 将可迭代对象转换成列表
print(list("alex"))
tuple() 将可迭代对象转换成元组
print(tuple([1,2,3,4]))
dict() 将元组和列表转换成字典
print(dict([(1,2),(3,4)]))
print(dict(((1,2),(3,4))))
print(dict([(1,2),(3,4)]))
print(dict(k=1,v=2,c=3))
dic1 = {"key1":1,"key2":2}
dic2 = {"a":1,"b":2}
dic2.update(dic1)
print(dic2)
print(dict(**dic1,**dic2))
print(dict(((1,2),(3,33))))
for i in ((1,(2,3)),(3,33)):
k,v = i
dic = {}
dict(k=1)
dict([(1,2)])
set() 将可迭代对象转换成一个集合
print(set("alex"))
print() 屏幕输出
sep:每个元素之间分割的方法 默认 “ ”
print(1,2,3,4,sep=”|”)
end:print执行完后的结束语句默认:\n
print(1,2,3,end=””)
file:文件句柄,默认是显示到屏幕 flush 刷新
print(1,2,3,4,file=open(‘text’,”w”,encoding=’utf-8’)
abs() 绝对值
dir 查看当前对象的所有方法
print(dir(list)) 查看当前对象的所有方法,返回的是列表
zip 相当于拉链。。当长度不一致时选择最短的进行合并
l1=[1,2,3]
l2=[‘sa’,’sda’,’zc’]
print(list(zip(l1,l2))
面试题,一行代码将两个列表转化成字典
print(dict(zip(l1,l2)))
format:
print("网站名:{name}, 地址 {url}".format(name="菜鸟教程", url="www.runoob.com"))
通过字典设置参数
site = {"name": "菜鸟教程", "url": "www.runoob.com"}
print("网站名:{name}, 地址 {url}".format(**site))
# 通过列表索引设置参数
my_list = ['菜鸟教程', 'www.runoob.com']
print("网站名:{0[0]}, 地址 {0[1]}".format(my_list)) # "0" 是必须的
print(format("alex",">20")) # 右对齐
print(format("alex","<20")) # 左对齐
print(format("alex","^20")) # 居中
print(format(10,"b")) # bin 二进制
print(format(10,"08b")) # 取8个数,当前面数字不够是用0凑
print(format(10,"08o")) # oct 八进制
print(format(10,"08x")) # hex 十六进制
print(format(0b1010,"d")) # digit 十进制
reversed 反转
print(list(reversed("alex")))
lst = [1,2,3,4,5]
print(list(reversed(lst))) 反转
print(lst)
高阶函数:
filter---过滤
用法: print(list(filter(lambda x:x>1,lst)))
def func(a):
return a == 1
print(list(filter(func,[1,2,3,4,6])))
指定过滤规则(函数名[函数的内存地址] ) 要过滤的数据
解析原理:
lst=[1,2,3,4,5,6]
def f(func,args):
new_lst=[]
for i in args:
if func(i):
new_lst.append(i)
return new_lst
def func(a):
return a>1
print(f(func,lst))
print(list(filter(lambda x:x>2,[1,2,3,4,5])))
lst = [{'id':1,'name':'alex','age':18},
{'id':1,'name':'wusir','age':17},
{'id':1,'name':'taibai','age':16},]
print(list(filter(lambda x:x['age']>16,lst)))
map( ) 映射(将可迭代对象中的每个元素执行指定的函数) (可以同时进行多组)
长度不想等时,取短的执行
例:def func():
return a+b
print(list(map(func,[1,2,3,4,5],[1,2,3,4,56,7,8])))
一行代码执行
print(list(map(lambda x,y:x+y,[1,2,3,4,5],[33,22,44,55])))
sorted——排序 key==指定规则
print(sorted([1,2,3,4,5,6],reverse=True)) 降序
print(sorted([1,2,3,4,5,-6],reverse=True,key=abs)) key——指定的方法
lst = ["三国演义","红楼梦","铁道游击队","西游记","水浒传","活着"]
# print(sorted(lst,key=len)) 按长度排序
lst = [{'id':1,'name':'alex','age':18},
# {'id':2,'name':'wusir','age':17},
# {'id':3,'name':'taibai','age':16},] 按age 排序
# print(sorted(lst,key=lambda x:x['age']))
max 最大值 min 最小值
from functools import reduce # 累计算
从 functools工具箱中拿来了reduce工具
def func(x,y):
return x+y
print(reduce(func,[1,2,3,4,5]))
print(reduce(lambda x,y:x+y,[1,2,3,4,5]))
匿名函数————一行函数
匿名函数:一行函数
匿名函数的名字叫做 lambda
匿名函数的格式 print(lambda x:x)())
lambda 关键字
x:是普通数的形参 (位置,关键字。。。) 可以不接收参数(x:可以不写)
:x 是普通函数的函数值 (只能返回一个数据类型)(:x返回值必须写)
lst=[lambda:i for i in range(5)]
print(lst[0]()) 结果 4
拆解:lst=[]
for i in range(5):
def func():
return i
lst.append(func) 函数的内存地址
print(lst[0]())启动第一的函数
lst=[lambda x:x+1 for i in range(5)]
print(lst[0](5)) 结果 6
拆解: lst=[]
for i in range(5):
lst.append(lambda x:x+1)
print(lst[0](5)
tu = (lambda :i for i in range(3))
print(next(tu)()) 结果 0
拆解 def func( ):
for i in range( ):
def foo( ):
return i
yield foo
g=func( )
print(next(g)()) 调用
print(next(g)) 内存地址()
函数体中存放的是代码
生成器体中存放的也是代码
就是yield导致函数和生成器的执行结果不统一
lst = [lambda x:x+5 for i in range(2)]
print([i(2) for i in lst]) 结果[7,7]
拆解 lst=[]
for i in range(2): 循环两次
lst.append(lambda x:x+5)
new_lst=[]
for i in lst:
new_lst.append(i(2))
print(new_lst)
lst = (lambda x:x+5 for i in range(2))
print([i(2) for i in lst]) 结果 [7,7]
拆解:
def func():
for i in range(2):
f=lamdba x:x+5
yield f
g=func()
lst=[]
for i in g:
lst.append(i(2))
print(lst)
lst = (lambda x:x*i for i in range(2))
print([i(2) for i in lst]) 结果:[0,2]
拆解:
def func():
for i in range(2):
f=lambda x:x*i
yield f
g=func
lst=[]
for i in g:
lst.append(i(2))
print(lst)
abs() 绝对值 | divmod() | input() 输入python2中是raw_input() | open() 打开文件 | staticmethod() |
---|---|---|---|---|
商...余 | 返回函数的静态方法。 | |||
all()判断容器中的元素是否都为真 | enumerate( ) | int()转成整型 | ord() 通过元素取ascii码的值 | str() 转成字符串 |
枚举()函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在用于循环当中。 | 可转进制 int("要转的进制 ", 转的进制) | |||
二进制数字可以用 0b 者 0B 做前缀,八进制数字可以用 0o 或者 0O 做前缀,十六进制数字可以用 0x 或者 0X做前缀,前缀是可选的。 | ||||
any()判断容器中的元素是否有一个为真 | eval() | isinstance() | pow()幂运算(两个参数是求幂,三个参数是取余) | sum()求和 |
用来执行一个字符串表达式,并返回表达式的值. | 判断一个对象的类型 | |||
basestring() | execfile() | issubclass() | print()输出 | super() |
可以用来执行一个文件 | issubclass()方法用于判断参数类是否是类型参数class info的子类. | sep:每个元素之间的分割方法 默认:“ ” | 是用于调用父类(超类)的一个方法. | |
end:执行完后的结束语句默认:\n | ||||
bin()十进制转二进制print(bin(10)) | file()文件句柄 | iter()=iter | property() | tuple()转元组 |
bool()布尔值 | filter()过滤(高阶函数) | len()长度 | range()数字的范围 | type()查看类型 |
bytearray() | float()浮点数 | list()转列表 | raw_input() | unichr() |
返回一个新字节数组。这个数组里的元素是可变的,并且每个元素的值范围: 0 <= x < 256。 | chr()函数功能基本一样,只不过是返回unicode的字符。 | |||
callable()查看对象是否可调用 | format()字符串格式化(见下 ) | locals()查看当前空间变量 | reduce()累计算 | unicode() |
chr()通过表位序号查找对应元素 | frozenset() | long()长整型 | reload() | vars() |
返回一个冻结的集合,冻结后集合不能再添加或删除任何元素. | 用于重新载入之前载入的模块. | 返回对象的属性和属性值的字典对象. | ||
classmethod() | getattr() | map()映射 | repr()查看数据的原生态 | xrange() |
修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。 | 用于返回一个对象属性值 | (高阶函数) | 用法与范围完全相同,所不同的是生成的不是一个数组,而是一个生成器. | |
cmp() | globals()查看全局空间变量 | max()最大值 | reverse() 反转 | |
cmp(x,y) 函数用于比较2个对象,如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1。 | (高阶函数) | |||
compile() | hasattr() | memoryview() | round() 保留小数位 | import() |
将一个字符串编译为字节代码 | 用于判断对象是否包含对应的属性 | 返回给定参数的内存查看对象 | ||
complex() 复数 | hash()区分就是可变数据类型和不可变数据类型 | min()最小值 | set()转成集合 | zip()函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表. |
(高阶函数) | 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用*号操作符,可以将元组解压为列表. | |||
delattr() | help()查看帮助信息 | next()=next | setattr() | |
用于删除属性 | ||||
dict()转换成字典 | hex() 十进制转十六进制 | object() 类 | slice() | |
实现切片对象,主要用在切片操作函数里的参数传递 | ||||
dir() 查看当前对象的所有方法 | id() 查看内存地址 | oct()十进制转八进制 | sorted()排序(高阶函数) | exec 内置表达式 |
二进制 | 八进制 | 十进制 | 十六进制 | |
---|---|---|---|---|
二进制 | bin(int(x, 8)) | bin(int(x, 10)) | bin(int(x, 16)) | |
八进制 | oct(int(x, 2)) | oct(int(x, 10)) | oct(int(x, 16)) | |
十进制 | int(x, 2) | int(x, 8) | int(x, 16) | |
十六进制 | hex(int(x, 2)) | hex(int(x, 8)) | hex(int(x, 10)) |
高阶函数
filter筛选过滤
语法: filter(function,iterable)
function: 用来筛选的函数,在filter中会自动的把iterable中的元素传递给function,然后根据function返回的True或者False来判断是否保留此项数据
iterable:可迭代对象
lst = [{'id':1,'name':'alex','age':18},
{'id':1,'name':'wusir','age':17},
{'id':1,'name':'taibai','age':16},]
ls = filter(lambda e:e['age'] > 16,lst)
print(list(ls))
结果:
[{'id': 1, 'name': 'alex', 'age': 18},
{'id': 1, 'name': 'wusir', 'age': 17}]
map映射
映射函数
语法: map(function,iterable) 可以对可迭代对象中的每一个元素进映射,分别取执行function
计算列表中每个元素的平方,返回新列表
lst = [1,2,3,4,5]
def func(s):
return s*s
mp = map(func,lst)
print(mp)
print(list(mp))
改写成lambda
lst = [1,2,3,4,5]
print(list(map(lambda s:s*s,lst)))
计算两个列表中相同位置的数据的和
lst1 = [1, 2, 3, 4, 5]
lst2 = [2, 4, 6, 8, 10]
print(list(map(lambda x, y: x+y, lst1, lst2)))
结果:
[3, 6, 9, 12, 15]
sorted排序函数
语法:sorted(iterable,key=None,reverse=False)
iterable : 可迭代对象
key: 排序规则(排序函数),在sorted内部会将可迭代对象中的每一个元素传递给这个函数的参数.根据函数运算的结果进行排序
reverse :是否是倒序,True 倒序 False 正序
lst = [1,3,2,5,4]
lst2 = sorted(lst)
print(lst) #原列表不会改变
print(lst2) #返回的新列表是经过排序的
lst3 = sorted(lst,reverse=True)
print(lst3) #倒叙
结果:
[1, 3, 2, 5, 4]
[1, 2, 3, 4, 5]
[5, 4, 3, 2, 1]
字典使用sorted排序
dic = {1:'a',3:'c',2:'b'}
print(sorted(dic)) # 字典排序返回的就是排序后的key
结果:
[1,2,3]
和函数组合使用
# 定义一个列表,然后根据一元素的长度排序
lst = ['天龙八部','西游记','红楼梦','三国演义']
# 计算字符串的长度
def func(s):
return len(s)
print(sorted(lst,key=func))
# 结果:
# ['西游记', '红楼梦', '天龙八部', '三国演义']
和lambda组合使用
lst = ['天龙八部','西游记','红楼梦','三国演义']
print(sorted(lst,key=lambda s:len(s)))
结果:
['西游记', '红楼梦', '天龙八部', '三国演义']
lst = [{'id':1,'name':'alex','age':18},
{'id':2,'name':'wusir','age':17},
{'id':3,'name':'taibai','age':16},]
# 按照年龄对学生信息进行排序
print(sorted(lst,key=lambda e:e['age']))
结果:
[{'id': 3, 'name': 'taibai', 'age': 16}, {'id': 2, 'name': 'wusir', 'age': 17}, {'id': 1, 'name': 'alex', 'age': 18}]
max() 最大值与最小值用法相同
min() 求最小值
print(min([1,2,3])) # 返回此序列最小值
ret = min([1,2,-5,],key=abs) # 按照绝对值的大小,返回此序列最小值
print(ret)
# 加key是可以加函数名,min自动会获取传入函数中的参数的每个元素,然后通过你设定的返回值比较大小,返回最小的传入的那个参数。
print(min(1,2,-5,6,-3,key=lambda x:abs(x))) # 可以设置很多参数比较大小
dic = {'a':3,'b':2,'c':1}
print(min(dic,key=lambda x:dic[x]))
# x为dic的key,lambda的返回值(即dic的值进行比较)返回最小的值对应的键
reduce 累计算
from functools import reduce
def func(x,y):
return x + y
# reduce 的使用方式:
# reduce(函数名,可迭代对象) # 这两个参数必须都要有,缺一个不行
ret = reduce(func,[3,4,5,6,7])
print(ret) # 结果 25
reduce的作用是先把列表中的前俩个元素取出计算出一个值然后临时保存着,
接下来用这个临时保存的值和列表中第三个元素进行计算,求出一个新的值将最开始
临时保存的值覆盖掉,然后在用这个新的临时值和列表中第四个元素计算.依次类推
注意:我们放进去的可迭代对象没有更改
以上这个例子我们使用sum就可以完全的实现了.我现在有[1,2,3,4]想让列表中的数变成1234,就要用到reduce了.
普通函数版
from functools import reduce
def func(x,y):
return x * 10 + y
# 第一次的时候 x是1 y是2 x乘以10就是10,然后加上y也就是2最终结果是12然后临时存储起来了
# 第二次的时候x是临时存储的值12 x乘以10就是 120 然后加上y也就是3最终结果是123临时存储起来了
# 第三次的时候x是临时存储的值123 x乘以10就是 1230 然后加上y也就是4最终结果是1234然后返回了
l = reduce(func,[1,2,3,4])
print(l)
匿名函数版
l = reduce(lambda x,y:x*10+y,[1,2,3,4])
print(l)
在Python2.x版本中recude是直接 import就可以的, Python3.x版本中需要从functools这个包中导入
龟叔本打算将 lambda 和 reduce 都从全局名字空间都移除, 舆论说龟叔不喜欢lambda 和 reduce
最后lambda没删除是因为和一个人给龟叔写了好多封,进行交流然后把lambda保住了.
闭包
定义:在嵌套函数中,使用非本层且非全局变量的就是闭包
print(inner.closure) 判断是否是闭包 返回None就不是闭包
闭包的作用:
- 保护数据的安全性
- 装饰器
装饰器
开发封闭原则
- 代码扩展进行开放
- 修改源代码是封闭
- 在不修改源代码及调用方式,对功能额外添加的就是开放封闭原则
装饰器标准版:
def wapper(f):
def inner(*args,**kwargs):
print('被装饰函数执行前')
ret=f(*args,**kwargs)
print('被装饰函数执行后')
return ret
return inner
@wapper————语法糖 func=warpper(func)
def func(*args,**kwargs):
print(f'被装饰的{args,kwargs}')
return '是'
print(func(1,2,2,3,4))
装饰器标准版拆解:
def wapper(f):
def inner(*args,**kwargs):
print('被装饰函数执行前')
ret=f(*args,**kwargs)
print('被装饰函数执行后')
return ret
return inner
# @wapper #————语法糖 func=warpper(func)
def func(*args,**kwargs):
print(f'被装饰的{args,kwargs}')
return '是'
func=wapper(func)
print(func(1,2,2,3,4))
装饰器进阶
有参装饰器
在标准装饰器的外层,套了一层就需要额外的调用一层
语法糖:@装饰器(参数)
while True:
def count(a):
def func(f):
def fool(*args,**kwargs):
if a=="1":
print("这是第一个装饰的函数qq")
f(*args,**kwargs)
if a == "2":
print("这是第一个装饰的函数微信")
f(*args, **kwargs)
if a == "3":
print("这是第一个装饰的函数虎牙")
f(*args, **kwargs)
if a == "4":
print("这是第一个装饰的函数腾讯")
f(*args, **kwargs)
return fool
return func
msg="""
1.qq
2.微信
3.虎牙
4.腾讯
"""
s=input(msg+"请输入选择的序号")
@count(s)
def cen(*args,**kwargs):
print(f"这是被修饰函数{args}")
# func=count(s)
# cen=func(cen)
cen(1)
多个装饰器装饰一个函数
多个装饰器装饰一个函数时,先执行离被装饰函数最近的装饰器
小技巧:v
def func1(f):
def fool(*args,**kwargs):
print("这是第一个修饰器的开始")
f(*args,**kwargs)
print("这是第一个修饰器的结束")
return fool
def func2(f):
def fool(*args,**kwargs):
print("这是第二个修饰器的开始")
f(*args,**kwargs)
print("这是第二个修饰器的结束")
return fool
def func3(f):
def fool(*args,**kwargs):
print("这是第三个修饰器的开始")
f(*args,**kwargs)
print("这是第三个修饰器的结束")
return fool
@func1
@func2
@func3
def cen(*args,**kwargs):
print("请修饰这个函数")
# cen=func3(cen)
# cen=func2(cen)
# cen=func1(cen)
cen()
结果:
这是第一个修饰器的开始
这是第二个修饰器的开始
这是第三个修饰器的开始
请修饰这个函数
这是第三个修饰器的结束
这是第二个修饰器的结束
这是第一个修饰器的结束
def wrapper1(func):
def inner():
print('wrapper1 ,before func')
func()
print('wrapper1 ,after func')
return inner
def wrapper2(func):
def inner():
print('wrapper2 ,before func')
func()
print('wrapper2 ,after func')
return inner
@wrapper2
@wrapper1
def f():
print('in f')
f()
结果:
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
递归
定义:
- 不断调用自己本身
- 有明确的终止条件
递归最大深度
官方文档 1000 实测是998左右
解除递归深度限制:
import sys
sys.setrecursionlimit(1000000) #例如这里设置为一百万
def cen(i):
if i==3:
return 38
else:
return cen(i+1)-2
print(cen(1))
lis=["世界","明天","你还",["在","那"],"里",["是","吗"]]
def cen(lis):
for i in lis:
if type(i)==list:
cen(i)
else:
print(i)
cen(lis)
模块
定义:一个py文件就是一个模块
功能:文件管理,避免代码重复。可以拿来就用(避免重复造轮子,python中的类库特别多)
import
import时做的三件事
- 将.py文件中所有代码读取到当前文件夹中
- 当前文件开辟新空间
- 等待被调用
import多次导入同一个模块名时,只执行一次
import导入的是整个模块
导入模块不能加后缀名
from
from 模块名 import函数名
as
as 起别名
例 from 模块名 import 函数名 as 自己定义的名字
起别名的作用:1.避免名字过长
2.避免覆盖之前的内容
import和from的异同
相同点:都是使用的相对路径
不同点:import能够执行整个模块中的所有功能 form是只能执行导入工具
form 容易将当前文件中定义的功能模块覆盖,,form比import灵活
模块导入顺序
模块导入顺序:
sys.path.append(r"C:\Users\oldboy\Desktop")
内存 > 内置 > 第三方> 自定义
sys.path.insert(0,r"C:\Users\oldboy\Desktop")
内存 > 自定义 > 内置 > 第三方
模块的两种用法: if __name__ == "__main__"
-
当做模块被导入 import from 如果调用文件时文件中的函数需要调用的称为模块
-
当做脚本被被执行 如果调用文件时文件中的函数能执行就称为脚本
只有别的盘的.py文件当做模块被导入时,字节码才会进行保留
以后会遇到的坑
- 自定义的函数名不能和内置函数重名
- 循环导入时,导入模式需要放在需要的地方
from 函数名 import * ==import 函数名 全部导入
在模块中通过___all____[ ]控制要导入的内容
time模块
import time
print(time.time()) # 时间戳 浮点数 秒
time.sleep(3) # 秒
将时间戳转换成结构化时间
print(time.localtime(time.time()))#命名元组,可以通过索引调用
print(time.localtime(time.time())[0])
print(time.localtime(time.time()).tm_year)
time.mktime ( )将结构化时间转换成时间戳
print(time.mktime(time_g))
time_g = time.localtime()#将时间戳转化成结构化时间
print(time.strftime("%Y-%m-%d %H:%M:%S",time_g))#将结构化时间转化成字符串
print(time.strptime("2018-06-09 03:23:23","%Y-%m-%d %H:%M:%S"))#将字符串转化成结构化时间
time_g = time.localtime()#将结构化时间转化成时间戳
print(time.mktime(time_g))
str_time = "2016-10-1 10:11:12"
time_int = time.time() - time.mktime(time.strptime(str_time,"%Y-%m-%d %H:%M:%S"))
print(time.localtime(time_int))
小结
import time
print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time())))
将时间戳转化成格式化然后字符串
print(time.mktime(time.strptime("2019-1-1 11:11:11",'%Y-%m-%d %H:%M:%S')))
将字符串时间转化成时间戳
总结:
time.time() 时间戳
time.sleep() 睡眠
time.localtime() 时间戳转结构化
time.strftime() 结构化转字符串
time.strptime() 字符串转结构化
time.mktime() 结构化转时间戳
%Y 年
%m 月
%d 日
%H 时间
%M 分钟
%S 秒
datetime 模块(时间模块)
from datetime import datetime
print(datetime.now())#获取当前时间
print(datetime(2018,10,6,12,11,12)-datetime(2017,10,5,12,3,22))#这里是计算差多少天小时分钟
指定时间
将对象转换成时间戳
d = datetime.now()
print(d.timestamp())
将时间戳转换成对象
import time
f_t = time.time()
print(datetime.fromtimestamp(f_t))
将对象转换成字符串
d = datetime.now()
print(d.strftime("%Y-%m-%d %H:%M:%S"))
将字符串转换成对象
s = "2018-12-31 10:11:12"
print(datetime.strptime(s,"%Y-%m-%d %H:%M:%S"))
可以进行加减运算(不能算年)
from datetime import datetime,timedelta
print(datetime.now() - timedelta(days=1))
print(datetime.now() - timedelta(hours=1))
random随机数模块
import random
print(random.randint(1,50))
选择1-50之间随机的整数
print(random.random())
0-1 之间随机小数,不包含1
print(random.uniform(1,10))
1- 10 之间随机小数,不包含10
print(random.choice((1,2,3,4,5,7)))
从容器中随机选择一个
print(random.choices((1,2,3,4,5,7),k=3))
从容器中随机选择3个元素,以列表的形式方式,会出现重复元素[ ]
print(random.sample((1,2,3,4,5,7),k=3))
从容器中随机选择3个元素,以列表的形式方式,不会出现重复元素
print(random.randrange(1,10,2))
随机的奇数或随机的偶数(一个)
洗牌 将有序的数据打散
lst = [1,2,3,4,5,6,7]
random.shuffle(lst) (有操作,将有序的数据打散)
print(lst)
sys模块 与python解释器交互的模块
import sys
sys.argv 命令行参数List,第一个元素是程序本身路径,只能在终端执行
sys.exit(n) 退出程序,正常退出时exit(0)
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
print(sys.path) ,模块查找的顺序 ****
print(sys.modules) 查看加载到内存的模块
print(sys.version) 查看当前解释器的版本
os模块 与操作系统交互的模块
工作路径:
import os # os是和操作系统做交互,给操作发指令
print(os.getcwd()) # 获取当前文件工作的路径 ***
os.chdir("D:\Python_s25\day16") # 路径切换 **
print(os.getcwd())
print(os.curdir)
print(os.pardir)
文件夹 ***
os.mkdir("a2") # 创建文件夹
os.rmdir("a2") # 删除文件夹
os.makedirs('a1/a2/a3') # 递归创建文件夹
os.removedirs("a1/a2/a3") # 递归删除文件夹
print(os.listdir(r"D:\Python_s25\day17")) # 查看当前文件下所有的内容
文件 ***
os.remove(r"D:\Python_s25\day17\a") #删除文件,彻底删除 找不回来
os.rename() # 重命名
路径
print(os.path.abspath("test")) # 返回的是绝对路径 ***
print(os.path.split(r"D:\Python_s25\day17\test")) #将路径分割成一个路径和一个文件名 **
print(os.path.dirname(r"D:\Python_s25\day17\test")) #获取到父目录 ***
print(os.path.basename(r"D:\Python_s25\day17\test")) #获取文件名 **
print(os.path.join("D:\Python","day17","test")) # 路径拼接 ***(非常重要)
判断
print(os.path.exists(r"D:\Python_s25\day17\blog")) # 判断当前路劲是否存在 **
print(os.path.isabs(r"D:\Python_s26\day17\blog")) # 判断是不是绝对路径 **
print(os.path.isdir(r"D:\Python_s25\day17\blog")) # 判断是不是文件夹 **
print(os.path.isfile(r"D:\Python_s25\day17\blog")) # 判断是不是文件 **
print(os.path.getsize(r"D:\Python_s25\day17\01 今日内容.py")) # 获取文件大小
print(os.path.getsize(r"D:\Python_s25")) # 获取文件大小 ***
文件夹相关
os.makedirs('dirname1/dirname2') 可生成多层递归目录 ***
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 ***
os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname ***
os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname ***
os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 **
文件相关
os.remove() 删除一个文件 ***
os.rename("oldname","newname") 重命名文件/目录 ***
os.stat('path/filename') 获取文件/目录信息 **
路径相关
os.path.abspath(path) 返回path规范化的绝对路径 ***
os.path.split(path) 将path分割成目录和文件名二元组返回 ***
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 **
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值,即os.path.split(path)的第二个元素。 **
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False ***
os.path.isabs(path) 如果path是绝对路径,返回True **
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False ***
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False ***
os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 ***
os.path.getatime(path) 返回path所指向的文件或者目录的最后访问时间 **
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间 **
os.path.getsize(path) 返回path的大小 ***
re模块 正则表达式
元字符 匹配内容
\w 匹配字母(包含中文)或数字或下划线
\W 匹配非字母(包含中文)或数字或下划线
\s 匹配任意的空白符
\S 匹配任意非空白符
\d 匹配数字
\D p匹配非数字
\A 从字符串开头匹配
\Z 匹配字符串的结束,如果是换行,只匹配到换行前的结果
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配字符串的开始
$ 匹配字符串的结尾
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 匹配字符组中的字符
[^...] 匹配除了字符组中的字符的所有字符
* 匹配0个或者多个左边的字符。
+ 匹配一个或者多个左边的字符。
? 匹配0个或者1个左边的字符,非贪婪方式。
{n} 精准匹配n个前面的表达式。
{n,m} 匹配n到m次由前面的正则表达式定义的片段,贪婪方式
a|b 匹配a或者b。
() 匹配括号内的表达式,也表示一个组
import re
findall 返回的是列表
print(re.findall("\w","123asdsaafd__-dsdcc子")) #\w这是找到数字,字母,汉字,下划线
print(re.findall("\W","1232_-cdsa cc你")) #\W这是找除啦字母,数字,汉字,下划线
print(re.findall("\s","123 243 13 13"))#\s这是将字符串中的空格输出以列表的形式最后默认时空格
print(re.findall("\S","123 4 5 5 5 ")) #\S这是将字符串中的非空格输出
print(re.findall("\d","1.12,331.1,ad,③-dACADW3你好")) #\d这是将字符串中的十进制输出
print(re.findall("\D","122431_454③aaAAjainh 经")) #\D这是将字符串中的非十进制输出
print(re.findall(".","1232A 姜2@\n"))#这是将字符串中的任何数据输出除啦\n
print(re.findall("\Aa","asdsxfa")) #\A这里是将字符串中的首字母判断是否是a,多数情况下使用^
print(re.findall("^a","abdun"))#^这里是匹配字符串的首字母是否是a
print(re.findall("s\Z","sdixkakos"))#\Z这里是判断字符串中最后面是不是s是返回["s"]不是返回[]多用¥
print(re.findall("s$","dsfao;osASs"))#$这里是判断字符串中最后面是不是s是返回["s"]不是返回[]
print(re.findall("[0-9]","12343,,poaskoidwx,mami你好——_")) #这里是匹配字符串中的所有数字字符
print(re.findall("[-0-9]","213445-4"))#[...]如果想要匹配特殊字符的时候在最前面写
print(re.findall('[-0-9]',"alex-123,日魔DSB,小黄人_229"))
print(re.findall("a.x","acssasaacxxzza x"))#a.c匹配是a开头c结尾的三个字符
print(re.findall("[a-z]","zaijdI834W")) #这里是想要匹配的小写字母的范围
print(re.findall("[^0-9]","234210kfjfawo_\n_7800-="))#这里是匹配所有不是数字字符的字符
print(re.findall("\n","weuid\n"))#这里是想找到\n
print(re.findall("\t","dan;lk\t"))#这里是想找到\n
print(re.findall("a*","zoaopOPLAAKAA Aaaaa"))#找到所有的0或多个a
print(re.findall("a+","dsfkakzaaa,aa,aa,aa"))#找到所有的1或多个a
print(re.findall("a?","aal,ldmwsdjiosoappa"))#这里是找到所有的a 0 或1个
print(re.findall("as(.*)d","ascdnfducxideuhdmdisdixcicdid"))#这里是以as开头d结尾的字符串,
中间是任意的除了\n 贪婪匹配
print(re.findall("as(.*?)d","ascdnfducxideuhdmdiasssedvdgdixcicdid")) #这里限制贪婪匹配,
只要符合要求就输出可输出多个只输出括号中的
print(re.findall("as(?:.*?)d","ascdnfducxideuhdmdiasssedvdgdixcicdid")) #这里限制贪婪匹配,
只要符合要求就输出,全部输出如ascd
print(re.findall("a|b","dwaKlpaps,xb"))#这里是匹配a或b
search找3开头是任意位置开始,如果没有报错。
s=re.search("3.+","3qwwfrqa3qdfeq")
print(s.group())
match只找第一个是不是3是就输出不是就报错如果没有group函数的话不报错输出None
s=re.match("3.+","sadfds3ss")
print(s.group())
print(re.split("[:,.]","jxc:c,cds.cd")) #分隔符
sub -- 替换
s = "alex:dsb#wusir.djb"
s1=re.sub("d","e",s,count=1)
print(s1)
compile 定义匹配规则
s = re.compile("\w")
print(s.findall("alex:dsb#wusir.djb"))
迭代器
s=re.finditer("\w","apokopf")
print(s.__next__().group())
换名
ret = re.search("<(?P<tag_name>\w+)>\w+</\w+>","<h1>hello</h1>")
s=re.search("<(?P<a_asa>\w+)>\w+</\w+>","<h1>hello</h1>")
print(s.group("a_asa"))
\. 没有任意的功能了
s = "1-2*(60+(-40.35/5)-(-4*3))"
print(re.findall("\d+",s))
print(re.findall("\d+\.\d+|\d+",s))
print(re.findall("-\d+\.\d+|-[0-9]|\d+",s))
re.match与re.search的区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;
re.search匹配整个字符串,直到找到一个匹配。
PS:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
JavaScript正则表达式在线测试工具:
http://tools.jb51.net/regex/javascript
正则表达式在线生成工具:
http://tools.jb51.net/regex/create_reg
更多关于Python相关内容可查看本站专题:《Python正则表达式用法总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》
json模块 文件传输和网络传输序列化
序列化:将数据类型转化成字符串 (为了实现变量或者对象持久化的存储到磁盘或者在网络上进行传输,需要将这些变量或者对象转化为二进制流,这种转化为二进制流的过程称为序列化)
反序列化:将字符串转化成数据类型 (反序列化是程序运行的时候,不能从磁盘中读取,需要将序列化的对象或者变量从磁盘转移到内存中,同时也会将二进制流转为原来的数据个是,这个过程叫做反序列化)
能够序列的有:字典、列表、元组
序列化:
1.json
2.pickle
4个方法 2组
dumps loads -- 用于网络传输
dump load -- 用于文件存储
dic = {"key": 1}
lst = [1,2,3,4]
手写
new_dic = {}
k,v = dic.replace("{","").replace("}","").replace('"',"").split(":")
new_dic[k] = v
print(new_dic["key"])
import json # 重点
s = json.dumps(dic)
print(s,type(s))
d = json.loads(s)
print(d,type(d))
s = json.dumps(lst) #序列
l = json.loads(s) # 反序列
print(l,type(l))
import json
def func():
print(11)
print(json.dumps(func))
将数据类型转换成字符串(序列化),将字符串转成原数据类型(反序列)
dic = {"key":1}
json.dump(dic,open("a","a",encoding="utf-8")) # 将源数据类型转换成字符串,写入到文件中
print(json.load(open("a","r",encoding="utf-8"))['key']) # 将文件中字符串转成源数据类型
dic = {"key":"宝元"}
f = open("a","a",encoding="utf-8")
f.write(json.dumps(dic)+"\n")
f.write(json.dumps(dic)+"\n")
f.write(json.dumps(dic)+"\n")
f.write(json.dumps(dic)+"\n")
f1 = open("a","r",encoding="utf-8")
for i in f1:
print(json.loads(i),type(json.loads(i)))
dic = {"meet":27,"太白":30,"alex":36,"wusir":33}
print(json.dumps(dic,ensure_ascii=False,sort_keys=True))
pickle
只有python有,几乎可以序列python中所有数据类型,匿名函数不能序列
import pickle
def func():
print(1)
a = pickle.dumps(func) # 将原数据类型转换成类似字节的内容
print(pickle.loads(a)) # 将类似字节的内容转换成原数据类型
软件开发规范
软件开发规范 -- 分文件
几百行 -- 大几万行
当代码存在一个py文件中时:
- 不便于管理 修改 增加
- 可读性差
- 加载速度慢
jango -- 雏形
约定俗称
- 启动文件 启动接口 bin
- 公共文件 大家需要的功能 lib
- 配置文件 (静态文件) 变量 conf
- 主逻辑 核心 core
- 用户相关数据 账号和密码等文件 db
- 日志 记录主要信息,记录开发人员的行为 log
******注意:在拆一个大的文件时,只要文件中的内容全部能取到就行。不用考虑别的模块,还有如果文件有父级文件一定得导入用
import sys
sys.path.append(f"父文件地址")
可以通过print(sys.path)查看是否导入
包的写作要求
包: 文件夹下具有__init__.py
就是一个包
通过from和import 可以查询包中的指定方法
有两种
1.from day1.day2 import cen2
cen2.cen2()
2.import day1.day2.cen2 as f
f.cen2()
这两种都是绝对路径那都好使,就是用起来不灵活
还有一种省略的方法
from day1.day2.cen2 import *
通过from和import可以获取到整个包
两种
1.from day1.day2 import *
拿到的是day2中所有的模块
取值方式
cen3.cen3()
2.import day1
取值方式
day1.day2.cen2.cen2()
需要设置__init__如# from . import day2 每个init都要配置
这里拿到的是整个包中所有的模块,但是想要使用模块中的函数换需要使用绝对路径取获取
总结:无论是想要获得模块还是包其实都是通过绝对路径
二
相对路径不能使用到最外层,上级必须是包
注意:这里如果使用相对路径只能在启动文件中执行,如果在当前文件夹引用时会报错
问题:
如果想要调用同级模块中的函数时,使用相对路径时,在本页面能运行不一定在启动文件中能用
解决:
通过i
import os
import sys
b=os.path.dirname(__file__)
sys.path.insert(0,b)
再次调用相对路径时自动获取父级的绝对路径。将代码复制到别的文件时也可以直接使用
日志
logging -- 日志
- 记录用户的信息
- 记录个人流水
- 记录软件的运行状态
- 记录程序员发出的指令
- 用于程序员代码调试
日志中要记录的信息
默认从warning开始记录
import logging
# 初始化一个空日志
logger = logging.getLogger() # -- 创建了一个对象
# 创建一个文件,用于记录日志信息
fh = logging.FileHandler('test.log',encoding='utf-8')
# 创建一个文件,用于记录日志信息
fh1 = logging.FileHandler('test1.log',encoding='utf-8')
# 创建一个可以在屏幕输出的东西
ch = logging.StreamHandler()
# 对要记录的信息定义格式
msg = logging.Formatter('%(asctime)s - [line:%(lineno)d] %(filename)s - %(levelname)s - %(message)s')
# 对要记录的信息定义格式
msg1 = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 设置记录等级
logger.setLevel(10) or logger.setLevel(logging.DEBUG)
# 等级对应表
'''
DEBUG - 10
INFO - 20
WARNING - 30
ERROR - 40
CRITICAL - 50
'''
# 将咱们设置好的格式绑定到文件上
fh.setFormatter(msg)
fh1.setFormatter(msg)
# 将咱们设置好的格式绑定到屏幕上
ch.setFormatter(msg1)
# 将设置存储日志信息的文件绑定到logger日志上
logger.addHandler(fh) #logger对象可以添加多个fh和ch对象
logger.addHandler(fh1)
logger.addHandler(ch)
# 记录日志
logger.debug([1,2,3,4,])
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
hasglib 加密
摘要算法,加密算数 ...
1.加密
2.校验
md5,sha1,sha256,sha512
1.md5,加密速度快,安全系数低
2.sha512 加密速度慢,安全系数高
明文(123adsa) -- 字节 -- 密文(bs2501153023ras32rf150q23r13ar)
1.当要加密的内容相同时,你的密文一定是一样的
2.当你的明文不一样时,密文不一样
3.不可逆
import hashlib
md5 = hashlib.md5() # 初始化
md5.update("alex".encode("utf-8")) # 将明文转换成字节添加到新初始化的md5中
print(md5.hexdigest()) # 进行加密
dic = {"534b44a19bf18d20b71ecc4eb77c572f":"alex"}
534b44a19bf18d20b71ecc4eb77c572f
9b4c00b63b24c060abd31c6cb96b7bc8
msg = input("请输入密码")
print(msg)
加盐
加固定盐
import hashlib
md5 = hashlib.md5("rimo_dsb".encode("utf-8")) # 初始化
md5.update("alex".encode("utf-8")) # 将明文转换成字节添加到新初始化的md5中
print(md5.hexdigest()) # 进行加密
加动态盐
import hashlib
user = input("username:")
pwd = input("password:")
md5 = hashlib.md5(user.encode("utf-8")) # 初始化
md5.update(pwd.encode("utf-8")) # 将明文转换成字节添加到新初始化的md5中
print(md5.hexdigest()) # 进行加密
import hashlib
sha1 = hashlib.sha1()
sha1.update("alex".encode("utf-8"))
print(sha1.hexdigest())
import hashlib
sha1 = hashlib.sha1()
sha1.update("日魔就是一个大SB".encode("utf-8"))
print(sha1.hexdigest())
sha1 = hashlib.sha1()
sha1.update("日魔就是一个大SB".encode("gbk"))
print(sha1.hexdigest())
中文内容编码不同时密文是不一致,英文的密文都是一致的
import hashlib
md5 = hashlib.md5()
md5.update(b"afdadfadfadsfafasdfasfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd")
md5.update(b"afdadfadfadsfafasdfasfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd")
md5.update(b"afdadfadfadsfafasdfasfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfdfd")
print(md5.hexdigest())
efe9bb9e7e090768597517019e5716b6
efe9bb9e7e090768597517019e5716b6
import hashlib
md5 = hashlib.md5()
md5.update(b"afdadfadfadsfafasd")
print(md5.hexdigest())
import hashlib
def file_check(file_path):
with open(file_path,mode='rb') as f1:
md5= hashlib.md5()
while True:
content = f1.read(1024) # 2049 1025 1
if content:
md5.update(content)
else:
return md5.hexdigest()
print(file_check('python-3.6.6-amd64.exe'))
明文 ——字节——密文
1.当要加密的内容相同时,密文一定是一样的
2.当明文不一样是,密文不一定一样
import hashlib
md5=hashlib.md5() 初始化
md5.update('字符串'.encode ('utf-8')) 将明文转化为字节
print(md5.hexdigest()) 进行加密
现在可以简单破解
加盐
import hashlib
md5=hashlib.md5(‘ 字符串’.encode('utf-8') 固定盐
md5.update('加密内容'.encode ('utf-8'))
print(md5.hexdigest())
加动态盐
user=input(“username”)
pwd= input(“password”)
md5=hashlib.md5(user.encode('utf-8'))
md5.update(pwd.encode(' utf-8')
print(md5.hexdigest())
sha512加密
import hashlib
sha1=hashlib.sha512()
sha1.update('sa'.encode('utf-8'))
print(sha1.hexdigest())
中文内容编码不同时,密文时不一致的,英文密文都是一致的
文件校验
import hashlib
def file_check(file_path):
with open(file_path,mode='rb') as f1:
sha256 = hashlib.md5()
while Ture:
content = f1.read(1024)
if content:
sha256.update(content)
else:
return sha256.hexdigest()
print(file_check('python-3.6.6-amd64.exe'))
collections 基于python自带的数据类型之外额外增加的几个数据类型
1.namedtuple: 生成可以使用名字来访问元素内容的tuple
2.deque: 双端队列,可以快速的从另外一侧追加和推出对象
队列:先进先出
栈:先进后出
3.Counter: 计数器,主要用来计数
4.OrderedDict: 有序字典 python3.6以上默认显示有序
5.defaultdict: 带有默认值的字典
form collections import Counter
c = Counter('abcdeabcdabcaba')
print c
输出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
lst = [1,23,,4,5,6,77,8,4,,2,5]
d=Counter(lst)
print(dict(d))
命名元组:
from collections import namedtuple
limit = namedtuple("limit",["x","y"])
l = limit(1,2)
print(l.x)
print(l[0])
双端队列
from collections import deque
lst = [1,2,3,4]
deque = [1,2,3,4]
deque.append(1)
deque.remove(1)
print(deque)
l = deque([1,2])
l.append(3)
l.appendleft(0)
l.pop()
l.popleft()
l.remove(2)
print(l)
队列: 先进先出
栈: 先进后出
from collections import OrderedDict
有序字典(python2版本) -- python3.6 默认是显示有序
dic = OrderedDict(k=1,v=11,k1=111)
print(dic)
print(dic.get("k"))
dic.move_to_end("k")
from collections import defaultdict
默认字典
dic = defaultdict(list)
dic[1]
print(dic)
lst = [11,22,33,44,55,77,88,99]
dic = defaultdict(list)
for i in lst:
if i > 66:
dic['key1'].append(i)
else:
dic['key2'].append(i)
print(dict(dic))
from collections import Counter
计数 返回一个字典
lst = [1,2,112,312,312,31,1,1,1231,23,123,1,1,1,12,32]
d = Counter(lst)
print(list(d.elements()))
print(dict(d))
面向对象
面向对象初识
面向对象优点:
-
结构清晰,可读性高
-
上帝思维(慢慢体会)什么是对象?什么是类?
类:对一些事物的统称和概括对象:实实在在存在的东西,具有特征和功能
类 和 对象 是 面向对象编程的 两个 核心概念
-
类 一个模板, (人类,动物)---是一个抽象的, 没有实体的一个群体
是对一群具有相同 特征或者行为的事物的一个统称,是抽象的,不能直接使用
- 特征 被称为 属性
- 行为 被称为 方法
类 就相当于制造飞机时的图纸,是一个 模板,是 负责创建对象的
-
对象: (eg: 甲、乙、 丙 、 丁)
- 对象 是 由类创建出来的一个具体存在,可以直接使用
- 由 哪一个类创建出来的对象,就拥有在 哪一个类中定义的:
- 属性
- 方法
- 对象 就相当于用 图纸 制造 的飞机
-
属性: (表示这类东西的特征:都有眼睛, 嘴巴或者鼻子等等)
-
方法: (表示这类物体可以做的事情, eg: 吃饭, 睡觉,学习等 方法(在类里面定义的函数, 叫做方法))
类和对象的关系
- 类是模板,对象 是根据 类 这个模板创建出来的,应该 先有类,再有对象
- 类只有一个,而对象可以有很多个
- 不同的对象 之间 属性 可能会各不相同
- 类 中定义了什么 属性和方法,对象 中就有什么属性和方法,不可能多,也不可能少
大驼峰命名法
- 每一个单词的首字母大写
- 单词与单词之间没有下划线
类名的确定
名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类
属性和方法的确定
- 对 对象的特征描述,通常可以定义成 属性
- 对象具有的行为(动词),通常可以定义成 方法
class Human:
"""
此类主要是构建人类
"""
mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段
dic = {}
l1 = []
def work(self): # 第二部分:方法 函数 动态属性
print('人类会工作')
class 是关键字与def用法相同,定义一个类。
Human是此类的类名,类名使用驼峰(CamelCase)命名风格,首字母大写,私有类可用一个下划线开头。
类的结构从大方向来说就分为两部分:
静态变量。
动态方法。
从类名的角度研究类
类名操作静态属性
第一种,查看类中的所有内容:类名.__dict__方式。
class Human:
"""
此类主要是构建人类
"""
mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段
dic = {}
l1 = []
def work(self): # 第二部分:方法 函数 动态属性
# print(self)
print('人类会工作')
print(Human.__dict__)
以下了解:
print(Human.__dict__['mind'])
Human.__dict__['mind'] = '无脑' # 错误
#通过这种方式只能查询,不能增删改.
print(Human.__dict__)
返回的是一个字典
第二种:万能的点.
如果想查询类中的所有内容,通过 第一种__dict__方法,如果只是操作单个属性则用万能的点的方式。
对象可以通过点来调用方法
- 类就是一些相似事物的统称,范围比较广
- 对象就是具体的实物,范围比较小.能够准确定位到某些东西
- 定义类 使用 `class`关键字 类名使用大驼峰
- 类名+() 是在实例化对象
- 查看类空间和对象空间使用.__dict__
- 类和对象通过.可以进行增删改查 这里的增和删相同和字典有点像
- 除去(类方法,静态方法) 建议使用对象调用类中的方法,因为Python帮我们自动把对象当最第一个参数传递给self
- self 是可以进行更改,但是不建议更改(面试可能会问)
- 一个类可以有多个对象,但是对象的空间是独立的
class People:
mind = "有思想" # 静态属性
def eat(self): # 方法
print("在吃饭")
def work(self):
print("在工作")
查看类下所有内容
print(People.__dict__)
万能的点 查看单个属性或方法
print(People.mind)
增:
People.emotion = "有情感"
删:
del People.mind
改:
People.mind = "无脑"
print(People.__dict__)
查:
print(People.mind) # 单独查一个
People.eat(1)
一般情况下我们不使用类名去操作方法 (类方法除外)
类名操作动态方法
前提:除了两个特殊方法:属性,类方法之外,一般不会通过类名操作一个类中的方法。
class Human:
"""
此类主要是构建人类
"""
mind = '有思想' # 第一部分:静态属性 属性 静态变量 静态字段
dic = {}
l1 = []
def work(self): # 第二部分:方法 函数 动态属性
# print(self)
print('人类会工作')
def tools(self):
print('人类会使用工具')
Human.work(111)
Human.tools(111)
下面可以做,但不用。
Human.__dict__['work'](111)
从对象的角度研究类
对象是从类中出来的,只要是类名加上(),这就是一个实例化过程,这个就会实例化一个对象。
class Human:
mind = '有思想'
def work(self):
print('人类会工作')
def tools(self):
print('人类会使用工具')
obj = Human() # 实例化对象
print(obj) # <__main__.Human object at 0x00000191508AA828>
print(p.__dict__) # 对象的空间
其实实例化一个对象总共发生了三件事:
1,在内存中开辟了一个对象空间。
2,自动执行类中的__init__方法,并将这个对象空间(内存地址)传给了__init__方法的第一个位置参数self。
3,在__init__ 方法中通过self给对象空间添加属性。
示例:
class Human:
mind = '有思想'
language = '使用语言'
def __init__(self,name,sex,age,hobby):
# self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。
self.n = name
self.s = sex
self.a = age
self.h = hobby
obj = Human('meet','男',18,'运动')
对象操作对象空间属性
对象查询对象中所有属性。 对象.__dict__
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
# self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。
self.n = name
self.s = sex
self.a = age
self.h = hobby
obj = Human('meet','男',18,'运动')
print(obj.__dict__) # {'n': 'meet', 'h': '运动', 's': '男', 'a': 18}
对象操作对象中的单个属性。 万能的点 .
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
# self 和 obj 指向的是同一个内存地址同一个空间,下面就是通过self给这个对象空间封装四个属性。
self.n = name
self.s = sex
self.a = age
self.h = hobby
obj = Human('meet','男',18,'运动')
obj.job = 'IT' # 增
del obj.n # 删
obj.s = '女' # 改
print(obj.s) # 查
print(obj.__dict__)
对象查看类中的属性
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
self.n = name
self.s = sex
self.a = age
self.h = hobby
obj = Human('meet','男',18,'运动')
print(obj.mind)
print(obj.language)
obj.a = 666
print(obj.a)
对象操作类中的方法
class Human:
mind = '有思想'
language = '实用语言'
def __init__(self,name,sex,age,hobby):
self.n = name
self.s = sex
self.a = age
self.h = hobby
def work(self):
print(self)
print('人类会工作')
def tools(self):
print('人类会使用工具')
obj = Human('meet','男',18,'运动')
obj.work()
obj.tools()
类中的方法一般都是通过对象执行的(除去类方法,静态方法外),并且对象执行这些方法都会自动将对象空间传给方法中的第一个参数self.
self 是什么
self其实就是类中方法(函数)的第一个位置参数,只不过解释器会自动将调用这个函数的对象传给self。
所以咱们把类中的方法的第一个参数约定俗成设置成self,
代表这个就是对象.这个self可以进行改变但是不建议
大家进行修改传参分为隐式传参和显示传参,self这种方式就是隐式传参
类空间
1.给对象空间添加属性
class A:
def __init__(self,name):
# 类里边给对象添加属性
self.name = name
def func(self,sex):
self.sex = sex
a = A("meet")
a.func("男")
# 类外边给对象添加属性
a.age = 18
print(a.__dict__)
总结:给对象空间添加属性可以在类的内部,类的外部,类中的方法
2.给类空间添加属性
class A:
def __init__(self,name):
# 类内部给类空间添加属性
A.name = name
def func(self,age):
# 类中的方法给类空间添加属性
A.age = age
类外部给类空间添加属性
A.name = "alex"
a = A('meet')
a.func(19)
print(A.__dict__)
总结:给类空间添加属性可以在类的内部,类的外部,类中的方法
class B:
def __init__(self,name):
self.name = name
def index(self):
print(self.name,"is index")
b = B("alex")
b.index()
类与类之间的关系
1.依赖关系
主 -- 人
次 -- 冰箱
class People:
def __init__(self,name):
self.name = name
def open(self,bx):
bx.open_door(self)
def close(self,bx):
bx.close_door(self)
class Refrigerator:
def __init__(self,name):
self.name = name
def open_door(self,p):
print(f"{p.name} 打开冰箱")
def close_door(self,p):
print(f"{p.name} 关闭冰箱")
r = People("日魔")
aux = Refrigerator("奥克斯")
r.open(aux)
r.close(aux)
class People:
def __init__(self,name):
self.name = name
def eat(self,food,flag):
food.eat(self,flag)
class Food:
def __init__(self,name):
self.name = name
def eat(self,p,f):
if f:
print(f"{p.name} 吃了 {self.name}")
else:
print(f"{p.name} 消化了 {self.name}")
p = People("日魔")
f = Food("大煎饼")
p.eat(f,False)
总结:将一个类的对象当做参数传递到另一个类中使用 -- 依赖关系
2.组合关系
class Boy:
def __init__(self,name):
self.name = name
def eat(self):
print(f"{self.name}和{self.girl} 一起吃了个烛光晚餐!")
def make_keep(self):
print(f"{self.name}带着{self.girl}去做俯卧撑!")
b = Boy("日魔")
b.girl = "乔bi萝"
# b.eat()
b.make_keep()
class Boy:
def __init__(self,name,g):
self.name = name # self = b
self.g = g # g就是girl类实例化的一个对象内存地址
def eat(self):
print(f"{self.name}和{self.g.age}岁,且{self.g.weight}公斤的{self.g.name}py朋友.一起吃了个烛光晚餐!")
def make_keep(self):
self.g.live(f"{self.g.weight}公斤的{self.g.name}给{self.name}踩背")
class Girl:
def __init__(self,name,age,sex,weight,*args):
self.name = name
self.age = age
self.sex = sex
self.weight = weight
self.args = args
def live(self,argv):
print(f"直播内容:{argv}")
g = Girl("乔毕萝",54,"女",220)
b = Boy("太正博",g)
b.make_keep()
总结
将一个类的对象封装到另一个类的对象属性中
1.类空间
给类空间和对象空间添加属性
类外部,内部,方法中
2.类关系:
依赖:将一个类的对象当做参数传递给另一个类的方法中
组合:将一个类的对象封装到另一个类实例化的对象空间中
self.g.f.eat() 一定是组合 (组合是咱们最常用的关系)
依赖关系和组合关系: 会用就行
函数嵌套
类的继承
单继承
程序中A(B) B是A的父类
A--子类,派生类
B--父类,基类,超类
class Human:
def __init__(self,name,age,sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Dog:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Cat:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Pig:
def __init__(self, name, age, sex):
self.name = name
self.sex = sex
self.age = age
def eat(self):
print("吃")
class Animal: # 父类
"""
动物类
"""
live = "活的"
def __init__(self, name, age, sex):
print("is __init__")
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
class Human(Animal): # 子类
pass
class Dog(Animal): # 子类
pass
通过子类的类名使用父类的属性和方法
Human.eat(12)
Human.__init__(Human,"日魔",18,"男")
print(Human.live)
print(Human.__dict__)
通过子类的对象使用父类的属性和方法
p = Human("日魔",18,"男")
d = Dog("rimo",1,'母')
print(d.__dict__)
print(p.__dict__)
p = Human("日魔",18,"男")
print(p.live)
查找顺序:
不可逆(就近原则)
通过子类,类名使用父类的属性或方法(查找顺序):当前类, 当前类的父类,当前类的父类的父类 --->
通过子类对象使用父类的属性或方法(查找顺序):先找对象,实例化这个对象的类,当前类的父类, --->
## 重要
继承: 单继承,多继承
Python2: python2.2 之前都是经典类,python2.2之后出现了新式类,继承object就是新式类
Python3: 只有新式类,不管你继不继承object都是新式类
继承的优点:
1.减少重复代码
2.结构清晰,规范
3.增加耦合性(不在多,在精)
使用子类和父类的方法或属性
class Animal: # 父类
"""
动物类
"""
live = "活的"
def __init__(self, name, age, sex):
# self = p的内存地址
self.name = name
self.sex = sex
self.age = age
def eat(self): # self 是函数的位置参数
print("吃")
方法一: 不依赖(不需要)继承
class Human: # 子类
def __init__(self, name, age, sex, hobby):
# print(Animal.live)
# self = p的内存地址
Animal.__init__(self,name,age,sex)
self.hobby = hobby
class Dog:
def __init__(self, name, age, sex, attitude):
# self = p的内存地址
self.name = name
self.sex = sex
self.age = age
p = Human("日魔",18,"男","健身")
print(p.__dict__)
方法二: 依赖(需要)继承
class Dog(Animal):
def __init__(self, name, age, sex, attitude):
# self = p的内存地址
# super(Dog,self).__init__(name,age,sex) # 完整写法
super().__init__(name,age,sex) # 正常写法
self.attitude = attitude
d = Dog("日魔",18,"男","忠诚")
print(d.__dict__)
def func(self):
self = 3
print(self)
self = 3
func(self)
1
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
class Foo(Base):
pass
obj = Foo(123)
obj.func1()
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
class Foo(Base):
def func1(self):
print("Foo. func1", self.num)
obj = Foo(123)
obj.func1()
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
self.func2()
def func2(self):
print("Base.func2")
class Foo(Base):
def func2(self):
print("Foo.func2")
obj = Foo(123)
obj.func1()
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
self.func2()
def func2(self):
print(111, self.num)
class Foo(Base):
def func2(self):
print(222, self.num)
a = Base(1)
b = Base(2)
c = Foo(3)
lst = [a, b, c]
print(lst)
for obj in lst:
obj.func2()
class Base:
def __init__(self, num):
self.num = num
def func1(self):
print(self.num)
self.func2()
def func2(self):
print(111, self.num)
class Foo(Base):
def func2(self):
print(222, self.num)
lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
obj.func1()
1
111 1
2
111 2
3
222 3
多继承
补充
单继承,多继承
经典类,新式类
-
子类的类名使用父类的属性和方法
-
子类的对象使用父类的属性和方法
-
既要使用子类的属性又使用父类的属性
方法一:不依赖(需要)继承
在本类的__init__方法中,使用另一个类名的__init__方法进行初始化
def __init__(self,name,age,sex):
A.__init__(self,name,age)
self.sex = sex
方法二:(依赖继承) super只能继承之后使用
def __init__(self,name,age,sex): # 初始化方法
super(本类,self).__init__(name,age)
super().__init__(name,age) # 重构方法
self.sex = sex
经典类
在python2.2之前的都是经典类,在python2.2到3.0之前继承了object的就是新式类
新式类
在python3之后,不管继不继承object都是新式类
经典类的取值顺序:深度优先先执行左侧
class ShenXian: # 神仙
def fei(self):
print("神仙都会⻜")
def chitao(self):
print("猴子喜欢吃西瓜")
class Monkey: # 猴
def chitao(self):
print("猴⼦喜欢吃桃⼦")
class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是⼀只猴
pass
sxz = SunWukong() # 孙悟空
sxz.chitao() # 会吃桃⼦
sxz.fei() # 会⻜
新式类的取值顺序:c3算法
c3算法:
mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
结果是一个列表
查看方法:
print(子类.mro())
计算过程:
mro(a(b,c))=[a]+merge(mro(b),mro(c),[b,c])
mro(b)=mro(b(d,e))=[b]+merge(mro(d),mro(e),[d,e])
mro(d(o))=[d,o]
mro(e(o))=[e,o]
mro(b(d,e))=[b]+merge([d,o],[e,o],[d,e])
=[b,d]+merge([o],[e,o],[e])
=[b,d,e]+merge([o],[o])
=[b,d,e,o]
mro(c)=mro(c(e,f))=[c]+merge(mro(e),mro(f),[e,f])
mro(e(o))=[e,o]
mro(f(o))=[f,o]
mro(c)=mro(c(e,f))=[c]+merge([e,o],[f,o],[e,f])
=[c,e]+merge([o],[f,o],[f])
=[c,e,f]+merge([o],[o])
=[c,e,f,o]
mro(a(b,c))=[a]+merge([b,d,e,o],[c,e,f,o],[b,c])
=[a,b]+merge([d,e,o],[c,e,f,o],[c])
=[a,b,d]+merge([e,o],[c,e,f,o],[c])
=[a,b,d,c]+merge([e,o],[e,f,o])
=[a,b,d,c,e]+merge([o],[f,o])
=[a,b,d,c,e,f]+merge([o],[o])
=[a,b,d,c,e,f,o]
print a.mro()
新式类 服从的是c3算法 mro算法
class O(object):
# name = "宝元"
pass
class D(O):
# name = "日魔"
pass
class E(O):
# name = "太正博"
pass
# class F(O):
# name = "alex"
pass
class B(D,E):
# name = "三哥"
pass
class C(E,F):
# name = "文刚"
pass
class A(B,C):
# name = "春生"
pass
print (A.mro())
面向对象的三大关系
继承
单继承、多继承
封装
将一些代码或数据存储到某个空间中(函数、类、对象)
多态
python默认就是多态
print带颜色输出
开头格式: \033[显示方式;字体色;背景色m
结尾格式: \033[0m
print('\033[0;35m字体变色,但无背景色 \033[0m') # 有高亮 没有背景颜色,只有字体颜色
print('\033[1;35m字体变色,但无背景色 \033[0m') # 有高亮
print('\033[4;35m字体变色,但无背景色 \033[0m') # 有高亮
print('\033[5;35m字体变色,但无背景色 \033[0m') # 有高亮 # 不好使
print('\033[7;35m字体变色,但无背景色 \033[0m') # 有高亮
print('\033[8;35m字体变色,但无背景色 \033[0m') # 有高亮 # 不好使
print('\033[1;32m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;33m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;36m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;32m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;33m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;36;40m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;32;40m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;30;43m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[1;30;43m 字体变色,但无背景色 \033[0m') # 有高亮
print('\033[0;36m骑牛远远过前村,')
print('短笛横吹隔陇闻。')
print('多少长安名利客,')
print('机关用尽不如君。\033[0m')
鸭子类型
官方解释
有一只鸟,叫声像鸭子,走路像鸭子,长相像鸭子,则称这只鸟为鸭子类型。
class Str:
def index(self):
print("好好好")
def do(self):
print("走走走")
class List:
def index(self):
print("是是是")
def do(self):
print("跑跑跑")
# 统一接口
def index(object):
object().index()
index(List)
在python中str.index(),list.index(),tuple.index()...都是鸭子类型
类约束
class A:
def pay(self):
print("A支付")
class B:
def pay(self):
print("B支付")
def pay(object):
object().pay()
类约束:方式一(常用)
class A:
def pay(self):
raise Exception("每个方法中都要有一个pay方法") (主动抛出异常)
class Weixin(A):
def pay(self):
print("微信支付")
class Alipay(A):
def pay(self):
print("支付宝支付")
class Qqpay(A):
def zhifu(self):
print("QQ支付")
def pay(object):
object().pay()
pay(Weixin)
pay(Alipay)
pay(Qqpay)
类约束:方式二 不常用 抽象类,接口类:制定一些规则
from abc import ABCMeta,abstractmethod
class A(metaclass=ABCMeta):
@abstractmethod
def pay(self):
raise Exception("每个类中都要有一个pay方法")
class Weixin(A):
def pay(self):
print("微信支付")
class Alipay(A):
def pay(self):
print("支付宝支付")
class QQpay(A):
def payy(self):
print("qq支付")
def pay(object):
object().pay()
# pay(Weixin)
# pay(Alipay)
pay(QQpay)
from abc import ABCMeta,abstractmethod # 抽象类,接口类
class PayClass(metaclass=ABCMeta): # 元类
@abstractmethod
def pay(self):
raise Exception("你子类必须要写一个pay方法")
class WechatPay(PayClass):
def pay(self):
print("微信支付")
class AliPay(PayClass):
def pay(self):
print("支付宝支付")
class QQpay(PayClass):
def pay1(self):
print("QQ支付")
def pay(object):
object().pay()
pay(WechatPay)
pay(AliPay)
pay(QQpay)
super剖析
按照mro顺序查找
super里的类名指定查找mro中类名的下一个类,self是指定查找时使用mro的顺序
class D(object):
def f1(self):
print("这是D父类",self)
class B1(D):
pass
def f1(self):
print("这是B1父类",self)
class A(D):
pass
def f1(self):
print("这是A父类",self)
class B(B1,A):
def f1(self):
print ("这是子类",self)
super(B,self).f1() # super 按照mro进行查找
b = B()
b.f1()
print(B.mro())
class D(object):
def f1(self):
print("这是D父类",self)
class B1(D):
pass
# def f1(self):
# print("这是B1父类",self)
class A(D):
pass
def f1(self):
print("这是A父类",self)
class B(B1,A):
def f1(self):
print ("这是子类",self)
super(B1,self).f1() # super 按照mro进行查找
b = B()
b.f1()
print(B.mro())
类的私有成员
类的私有成员
私有 : 只能自己有
以__开头就是私有内容
私有内容的目的就是保护数据的安全性
class Human:
live = "有思想" # 类公有的属性
__desires = "有欲望" # (程序级别)类私有的属性
_desires = "有欲望" # (程序员之间约定俗成)类私有的属性
def __init__(self,name,age,sex,hobby):
self.name = name
self.age = age
self.sex = sex # 对象的公有属性
self.__hobby = hobby # 对象的私有属性
def func(self):
# 类内部可以查看对象的私有属性
print(self.__hobby)
def foo(self): # 公有方法
# 类内部可以查看类的私有属性
print(self.__desires)
def __abc(self): # 私有方法
print("is abc")
print(Human.__dict__)
print(Human._desires)
class B(Human):
pass
def run(self):
print(self.__desires)
self._Human__abc() # 强制继承(非常不推荐使用)
b = B("日魔",28,"男","女孩子")
print(b.__desires)
b.run()
b._Human__abc()
_类名私有的属性和方法
子类不能继承父类中私有的方法和属性
类的外部查看
print(obj.name)
print(Human.live)
Human.live = "无脑"
print(Human.live)
obj.__abc()
print(Human.__desires) # 保证数据的安全性
类方法
类方法可以自动变换类名 (最大的作用)当换类名的时候,只需要将对应的类名换掉就可以了,不用换类方法中的类名,是默认的。
-
使用类方法可以获取到类地址进行实例化
-
可以通过类名修改类属性
类方法偶然会使用,使用最多的还是实例方法
class Student:
__num = 0#类的私有属性
def __init__(self,name,age):
self.name = name
self.age= age
Student.addNum() # 写在__new__方法中比较合适,但是现在还没有学,暂且放到这里
def aa(self):
print("这是实例方法")
@classmethod
def addNum(cls):
print(cls)#这是类的地址
cls.__num += 1
@classmethod
def getNum(cls):
return cls.__num
a = Student('宝元', 18)
b = Student('武sir', 36)
c = Student('alex', 73)
print(Student.getNum())getNum
静态方法
class A:
def func(self): # 实例方法
print("is A func")
@classmethod
def cls_func(cls): # 类方法
print("is A cls_func")
@staticmethod #静态方法
def static_func():
print("他就是普通的函数了")
静态方法不依赖于对象和类 (静态方法就是一个普通的函数)
def static_func():
print("他就是普通的函数了")
A.static_func()
a = A()
a.static_func()
属性(组合 伪装成属性)
class People:
hight=1.73
weight=63
@property
def mig(self):
mig=self.weight/(self.hight**2)
print(mig)
@mig.setter
def mig(self,value):
print("设置时执行我")
@mig.getter
def mig(self):
print("查找时执行我")
@mig.deleter
def mig(self):
print("删除的时候调用")
p=People()
p.mig=11
p.mig
del p.mig
元类
isinstance判断参数1是不是参数2的对象
print(isinstance(a,b))
issubclass判断参数1是不是参数2的子类
type 查看数据类型 (能够查看出当前内容从属于那个类)
print(type("alex"))
print(type([1,2,3,4]))
print(type((1,2,3,40)))
python自带的类
class A:
pass
a = A()
print(type(a))
print(type(A))
print(type(str))
print(type(list))
print(type(tuple))
print(type(object))
python自带的 str,list,tuple 也是某一个类的对象 (python中一切皆对象)
isinstance 判断参数1是不是参数2的对象
print(isinstance([1,2,3],list))
print(isinstance(str,type))
print(isinstance(list,type))
print(isinstance(object,type))
issubclass 判断参数1是不是参数2的子类
class A:
pass
class B(A):
pass
print(issubclass(B,A))
元类 -- 构建类
object与type的关系 (了解)
1.object是type的对象
2.新式类都默认继承object
print(issubclass(type,object)) # type是object的子类
print(isinstance(object,type)) # object是type的对象
class A:
pass
class B(A):
pass
obj = B()
print(isinstance(obj,B))
print(issubclass(B,A))
反射
反射:python中一切皆对象
通过字符串操作对象的属性和方法
反射组合:getattr(对象,字符串,查找不到的返回值)获取 内存地址
setattr(对象,字符串,值)设置
hasattr()判断是否存在 返回True说明这个属性在对象中存在
delattr()删除
反射的应用场景:
- 通过对象的角度使用反射
- 类的角度使用反射
- 6当前模块使用反射
当前模块:
def func():
print("is func")
print(globals()["func"])
import sys
o = sys.modules[__name__] # 获取当前模块名对应的对象
f = getattr(o,"func")
f()
其他模块使用反射
import test
test.func()
o = globals()["test"]
getattr(o,"func")()
配合使用:
if hasattr():
getattr()
模块部分使用反射的方式
方法一
import sys
sys.modules[__name__]
方法二
globals()[__name__]
class A:
name="黎明"
sex="男"
age=54
def __init__(self,name,sex):
self.name=name
self.sex=sex
def fun(self):
print("调用实例方法1")
def fun1(self):
print("调用实例方法2")
a=A("黄家驹","男")
类的操作
print(getattr(A,"name"))#获取
setattr(A,"name","想名")#设置
print(getattr(A,"name"))
getattr(A,"fun")(1)
print(hasattr(A,"fun"))
delattr(A,"name")
print(A.__dict__)
对象操作
print(getattr(a,"name"))
getattr(a,"fun")()
setattr(a,"name","小明")
print(hasattr(a,"name"))
print(getattr(a,"name"))
delattr(a,"name")
print(a.__dict__)
对当前模块操作
name="黎明"
sex="男"
age=54
def fun1():
print("调用实例方法2")
# print(globals())
import sys
p=sys.modules[__name__]
print(getattr(p,"name"))
getattr(p,"fun1")()
f=getattr(p,"fun1")
f()
class A:
def c(self):
print("naho")
import sys
p=sys.modules[__name__]
getattr(p.A(),"c")()
对其他模块进行操作
import aaa
print(getattr(aaa,"a"))
getattr(aaa,"cen")()
print(getattr(aaa.C,"b"))
getattr( aaa.C(),"ce")()
双下方法
__len__ : len()
__hash__: hash()
__repr__: %r
__eq__ : ==
__del__ : del 析构方法,删对象之前会自动执行
__item__ 系列 和字典操作一样
__getitem__ : dic["键"]
__setitem__ : dic["键"] = 值
__delitem__ : del dic["键"]
class A:
def __init__(self,name,age):
self.name = name
self.age = age
def __getitem__(self, item):
print(self.__dict__)
print(self.__dict__[item]) # self.__dict__ 获取的就是字典
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
del self.__dict__[key]
a = A("meet",58)
a["sex"] = "男"
a["sex"]
del a["sex"]
print(a.__dict__)
__str__ : str() print
class A:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def __str__(self): # print 触发 str()
print(111)
return f"姓名:{self.name} 年龄:{self.age} 性别:{self.sex}"
# 必须有返回值,返回值的类型必须是字符串
__call__ : 对象()
class A:
def __init__(self):
pass
def __call__(self, *args, **kwargs): # 对象()时调用的__call__
print("走我")
print(*args, **kwargs)
a = A()
a()
__new__ : 实例化时自动触发,先执行__new__在执行__init__ (面试手写单例模式)
单例模式:
class A:
__a = None
def __init__(self,name,age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
if cls.__a is None:
obj = object.__new__(cls)
cls.__a = obj
return cls.__a
a = A("meet",123)
a1 = A("Xiao",11)
print(a.name)
# 单例模式:不管你创建多少次,使用的都是同一个内存空间
# 模块的导入,手写的单例模式
# 实例化对象时发生的事
# 1.创建对象,并开辟对象空间 __new__
# 2.自动执行__init__方法
上下文管理
# __enter__
# __exit__
with open的原理
class my_open:
# def __init__(self,file,mode="r",encoding="utf-8"):
# self.file = file
# self.mode = mode
# self.encoding = encoding
#
# def __enter__(self):
# self.f = open(self.file,self.mode,encoding=self.encoding)
# return self.f
#
# def __exit__(self, exc_type, exc_val, exc_tb):
# print(exc_type,exc_val,exc_tb) # 没有错误时就是None
# self.f.close()
#
#
# with my_open("a.txt") as ffff:
# for i in ffff:
# print(i)
# print(ffff.read(1111))
# print(ffff.read())
# with open("a") as f: 使用了上下文
异常处理
常见异常
AttributeError 试图访问一个对象没有的属性,例如f.x 但是f没有属性x
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 代码没有正确对齐
IndexError 下标索引超出序列索引边界
KeyError 试图访问字典里不存在的键
NameError 使用一个还未被赋予对象的变量
SyntaxError Python遇到非法代码,代码不能编译
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
异常处理
为什么进行异常处理
- 出现异常,异常下方的代码就不执行了(中断了)
- 用户体验不良好
方式一:
if
if
num = input(">>")
if num.isdecimal():
int(num)
if len(num) == 0:
pass
if可以进行异常处理,但是只能针对某一段代码
方式二:
try(捕捉错误),可以捕获异常,保证程序不中断运行
try: #尝试
int("alex") # 尝试运行的代码
except ValueError as e: # 捕捉异常
except Exception as e 万能捕获异常
print(e) # 显示异常
处理方式
分支 + else + finally
try:
num = int(input("请输入数字:"))
lst = [1,2,3]
# dic = {"name":"meet",1:"rimo"}
# print(dic[num])
print(lst[num])
except ValueError as e:
print(e)
except KeyError as e:
print(f"没有{e}这个键")
except IndexError as e:
print(e)
except Exception as e:
print(e)
else:
print("没有异常走我")
finally:
print("清理工作")
推荐:使用try,但是不建议在任意位置添加try
网络编程
概念
网络架构
C/S :qq 微信 浏览器 英雄联盟 穿越火线 王者荣耀 安装
C:client 客户端
S:server 服务端
B/S :百度 淘宝 码云 只要在浏览器输入网址就可以直接使用了
B:browser 浏览器
S:server 服务端
B/S 架构中的浏览器 也是客户端的一种
B/S 是C/S架构中的一种
B/S更好: 更节省资源 不用更新 不依赖环境
统一了所有web程序的入口
C/S架构: 安全性 程序比较庞大
移动端
app
微信小程序 : 统一了所有web程序的入口
支付宝 : 统一了所有和钱相关的事儿
mac地址
是一个物理地址
唯一的标识你的网络设备
ip地址
是一个逻辑地址
是可以根据你的位置变化发生改变的
能够在广域网中快速的定位你
ipv4地址
4位点分十进制
0.0.0.0-255.255.255.255
2**32
公网和内网
公网 0.0.0.0-255.255.255.255(不包含保留字段的ip)
你能够在任意一个地方去访问的ip地址
内网 所有的内网ip都要使用保留字段
只能在一个区域内使用,出了这个区域就用不了了
192.168.0.0 - 192.168.255.255
10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.32.255.255
路由器和交换机
交换机完成局域网内通信
通过ip找mac地址 : arp协议
单播 广播 组播
路由器完成局域网间通信
网关ip
子网掩码(了解)
255.0.0.0
255.255.0.0
255.255.255.0
ip 和子网掩码 按位与运算
192.168.13.26 # 255.255.0.0
11000000.10101000.00001101.00011010
11111111.11111111.00000000.00000000
11000000.10101000.00000000.00000000 = 192.168.0.0
192.168.12.134
255.255.0.0
11000000.10101000.00001100.10000110
11111111.11111111.00000000.00000000
11000000.10101000.00000000.00000000 = 192.168.0.0
ipv6
6位冒分十六进制
0:0:0:0:0:0 - FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
mac ip 定位到一台机器
port 端口
0-65535
ip + port能够唯一的确认网络上的一台机器上的一个服务
socket模块 sever端(服务端),client端(客户端)
sever端
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)
client端
import socket
sk = socket.socket() # 创建客户套接字
sk.connect(('127.0.0.1',8898)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字
小结
B/S 和 C/S
网络相关概念
硬件设备 : 交换机 网卡 路由器
网卡 : 上面有一个mac地址能唯一标识一台网络设备
交换机 : 在局域网部通信 广播 单播 组播
路由器 : 局域网之间通信
网关 : 所有与局域网外部通信的时候所过的关口
所有的请求都会在这里换上网关ip对外通信
子网掩码 : 可以判断要寻找的机器是不是在一个局域网中
协议 :
arp协议 : 地址解析协议 通过一台机器的ip地址找到mac地址
交换机 广播 单播
ip协议 : ipv4 ipv6
内网的保留字段 除了内网字段都是公网字段
192.168.0.0 - 192.168.255.255 2*16 65536
172.16.0.0 - 172.31.255.255 2**20 1048576
10.0.0.0 - 10.255.255.255 2**24 16777216
几个特殊ip
127.0.0.1 本地回环地址
0.0.0.0 表示你的所有网卡的地址
端口 0-65535 用来标识一台机器上的一个服务
8000以上
socket 套接字
程序 要分cs端
既要完成server
也要完成client
地址 (ip,port)
ios五层协议
- 物理层
- 数据链路层 MAC地址 arp协议 物理设备: 网卡 、交换机
- 网络层 ipv4和ipv6 物理设备:路由器、三层交换机
- 传输层 tcp\udp协议 物理设备:四层路由器、四层交换机
- 应用层
socket是什么
将应用层以下的部分封装起来可以直接调用的网络通信模块
tcp协议的特点
面向链接、可靠性高,长度无限制、速度慢、流式传输
应用场景
上传、下载、发邮件、实时通信(打电话、发短信)
三次握手和四次挥手
三次握手:
- 三次握手是tcp协议建立连接的过程
- 客户端 会先向 服务端 发起一个SYN请求——请求与服务端建立连接
服务端会向 客户端返回一个SYN响应,并且带一个ACK请求——请求与客户端建立连接
客户端 接收ACK请求,向服务端返回一个SYN响应,可以建立连接。此时三次握手完毕,连接通道建立 - 在原生的socket代码中三次握手是由accept connect
四次挥手:
-
四次挥手是tcp协议断开链接的过程
-
客户端 会向 服务端 发起一个FIN请求——请求断开连接 ,
服务端 接收到FIN请求,会向 客户端 返回一个ACK响应——可以断开连接(注意,此时只是客户端到服务端的连接断开,服务端到客户端的连接依旧存在),
服务端 会向 客户端 发送一个FIN请求——请求断开连接 ,
客户端 接收到请求,会向服务端 返回一个ACK响应——可以断开连接。 此时四次挥手完毕,双方连接通道断开
-
在原生的socket代码中四次挥手是由两端的close完成的
tcp下的黏包和解决方案
定义:
同时执行多条命令之后,得到的结果很可能只有一部分,
在执行其他命令的时候又接收到之前执行的另外一部分结果,
这种显现就是黏包
成因:
-
发送方的缓存机制
发送端需要等缓冲区满才发送出去,造成粘包
(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包) -
接收方的缓存机制
接收方不及时接收缓冲区的包,造成多个包接收
(客户端发送了一段数据,服务端只收了一小部分,
服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
总结:
黏包现象只发生在tcp协议中:
- 从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向链接的特点。
- 实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
解决方案(常用struct模块)
理论
发送时 先发送struct转换好的数据长度4字节
再发送数据
接收时
先接受4个字节,使用struct转换成数字来获取要接收的数据长度
再按照长度接收数据
代码部分
服务端 :
import socket #导入网络通讯模块
import struct #struct主要作用是将要发送的数据长度转化成固定长度的字节
sk = socket.socket() #创建服务端套接字
sk.bind(('127.0.0.1',9003)) #把地址绑定到套接字上
sk.listen() #监听链接
conn,addr = sk.accept() #接收客户端链接
ms1 = '你好'.encode() #将字符串转化成字节
ms2 = '还好啊'.encode()
len_ms1 = len(ms1) #计算字节长度
send_len = struct.pack("i",len_ms1) #将数字长度转化成四字节,这四字节就是客户端接收的字节数
conn.send(send_len) #先像客户端发送四字节(这四字节是要接收的字节数)
conn.send(ms1) #在发送要发送的内容(字节形式)
客户端:
import struct
import socket
sk = socket.socket() #创建客户端套接字
sk.connect(('127.0.0.1',9003)) #和服务端进行连接
ma_len = sk.recv(4) #先接收服务端发送来的四字节
ma_len = struct.unpack('i',ma_len)[0] #用struct模块将接收的四字节长度进行解包(得到一个数字元组)
m = sk.recv(ma_len).decode() #按照得到的字节长度进行接收,然后解码
print(m)
进度条功能的实现
实现进度条功能
import sys
def processBar(num, total):
rate = num / total
rate_num = int(rate * 100)
if rate_num == 100:
r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,)
else:
r = '\r%s>%d%%' % ('=' * rate_num, rate_num,)
print(r,flush=True)
processBar(2048,10240)
验证客户端的合法性
服务端
import os
import socket
import hashlib
SECRET_KEY = b'alexbigsb'
def check_client(conn):
randbytes = os.urandom(32)
conn.send(randbytes)
md5 = hashlib.md5(SECRET_KEY)
md5.update(randbytes)
code = md5.hexdigest()
code_cli = conn.recv(32).decode('utf-8')
return code == code_cli
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()
while True:
conn,addr = sk.accept()
if not check_client(conn):continue
print('进程正常的通信了')
# import os
# import hmac # hashlib
# randbytes = os.urandom(32)
# mac = hmac.new(SECRET_KEY,randbytes)
# ret = mac.digest()
# print(ret)
客户端部分
import os
import socket
import hashlib
SECRET_KEY = b'alexbigs'
def check_client():
randbytes = sk.recv(32)
md5 = hashlib.md5(SECRET_KEY)
md5.update(randbytes)
code = md5.hexdigest().encode('utf-8')
sk.send(code)
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
check_client()
print('正常的客户端通信')
tcp的并发
import time
from socketserver import BaseRequestHandler,ThreadingTCPServer
# BaseRequestHandler 基础请求操作符
# ThreadingTCPServer 线程实现的基于tcp协议的server
class Myserver(BaseRequestHandler):
def handle(self):
n = 0
while True:
self.request.send(str(n).encode('utf-8')) # self.request == conn
n += 1
time.sleep(0.5)
server = ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
基于tcp协议实现文件的上传、下载
服务端:
import os
import json
import struct
import socket
REMOTE_DIR = os.path.join(os.path.dirname(__file__), 'remote')
sk = socket.socket()
sk.bind(('127.0.0.1', 9001))
sk.listen()
conn, addr = sk.accept()
num_by = conn.recv(4)
print(num_by)
msg_len = struct.unpack('i', num_by)[0]
print(struct.unpack('i', num_by))
json_dic = conn.recv(msg_len).decode('utf-8')
opt_dic = json.loads(json_dic)
if opt_dic['operate'] == 'upload':
filename = opt_dic['filename']
filesize = opt_dic['filesize']
filepath = os.path.join(REMOTE_DIR, filename)
with open(filepath, 'wb') as f:
while filesize > 0:
content = conn.recv(1024)
f.write(content)
filesize -= len(content)
# 要接受的字节数不一定是你实际接收到的数据长度
elif opt_dic['operate'] == 'download':
file_path = os.path.join(REMOTE_DIR, opt_dic['filename'])
if os.path.isfile(file_path): # 判断是否存在这个文件
size = os.path.getsize(file_path) # 返回文件大小
dic = {'filesize': size}
dic_bytes = json.dumps(dic).encode('utf-8')
bytes_len = len(dic_bytes)
send_len = struct.pack('i', bytes_len)
conn.send(send_len)
conn.send(dic_bytes)
with open(file_path, 'rb') as f:
while size > 0:
content = f.read(4096)
conn.send(content)
size -= len(content)
客户端:
import os
import json
import struct
import socket
LOCAL_DIR = os.path.join(os.path.dirname(__file__), 'local')
sk = socket.socket()
sk.connect(('127.0.0.1', 9001))
opt_lst = ['上传', '下载'] # 选择 要做的操作 是上传 还是下载
for index, opt in enumerate(opt_lst, 1):
print(index, opt)
inp = int(input('>>>'))
if inp == 1: # 选上传
path = input('请输入要上传的文件路径和文件名 : ') # 用户输入要上传的文件
if os.path.isfile(path): # 检测这个文件是否存在
filename = os.path.basename(path) # 获取文件名
filesize = os.path.getsize(path) # 获取文件大小
opt_dic = {'filename': filename, 'filesize': filesize,
'operate': 'upload'} # 把请求发过去
opt_bytes = json.dumps(opt_dic).encode('utf-8')
num_bytes = struct.pack('i', len(opt_bytes))
sk.send(num_bytes)
sk.send(opt_bytes)
with open(path, 'rb') as f:
while filesize > 0:
content = f.read(4096)
sk.send(content)
filesize -= 4096
elif inp == 2: # 选下载
# remote文件夹中的所有文件你都可以下载
filename = input('请输入要下载的文件名 : ') # 用户输入一个文件名
opt_dic = {'filename': filename, 'operate': 'download'}
opt_bytes = json.dumps(opt_dic).encode('utf-8')
num_bytes = struct.pack('i', len(opt_bytes))
sk.send(num_bytes)
sk.send(opt_bytes) # 把文件名发送到server端
bytes_len = sk.recv(4) # 要接受的数据的长度
msg_len = struct.unpack('i', bytes_len)[0]
str_dic = sk.recv(msg_len).decode('utf-8') # 接收文件的大小
size_dic = json.loads(str_dic)
filepath = os.path.join(LOCAL_DIR, filename)
with open(filepath, 'wb') as f:
while size_dic['filesize'] > 0:
content = sk.recv(1024)
f.write(content)
size_dic['filesize'] -= len(content)
udp协议
特点
面向数据报、可靠性低、有长度限制、速度快
应用场景
即时通讯、观看在线视频
udp协议下的sever、client
sever端(服务端)
from socket import socket,SOCK_DGRAM
sk=socket(type=SOCK_DGRAM)
sk.bind(('127.0.0.1',9001))
while True:
msg,addr=sk.recvfrom(1024)
print(msg.decode("utf-8"))
mm=input(">>>").encode("utf-8")
sk.sendto(mm,addr)
client(客户端)
from socket import socket,SOCK_DGRAM
sk=socket(type=SOCK_DGRAM)
while True:
server_addr=(('192.168.12.59',8520))
nn=input("》》》")
if nn.lower()=="q":
break
sk.sendto(nn.encode("utf-8"),server_addr)
msg=sk.recv(1024)
print(msg.decode("utf-8"))
小结
# B/S 和 C/S
# 网络相关概念
# 硬件设备 : 交换机 网卡 路由器
# 网卡 : 上面有一个mac地址能唯一标识一台网络设备
# 交换机 : 在局域网部通信 广播 单播 组播
# 路由器 : 局域网之间通信
# 网关 : 所有与局域网外部通信的时候所过的关口
# 所有的请求都会在这里换上网关ip对外通信
# 子网掩码 : 可以判断要寻找的机器是不是在一个局域网中
# 协议 :
# arp协议 : 地址解析协议 通过一台机器的ip地址找到mac地址
# 交换机 广播 单播
# ip协议 : ipv4 ipv6
# 内网的保留字段 除了内网字段都是公网字段
# 192.168.0.0 - 192.168.255.255 2*16 65536
# 172.16.0.0 - 172.31.255.255 2**20 1048576
# 10.0.0.0 - 10.255.255.255 2**24 16777216
# 几个特殊ip
# 127.0.0.1 本地回环地址
# 0.0.0.0 表示你的所有网卡的地址
# 端口 0-65535 用来标识一台机器上的一个服务
# 8000以上
# socket 套接字
# 程序 要分cs端
# 既要完成server
# 也要完成client
# 地址 (ip,port)
# 网络
# osi五层协议
# 应用层 要发送的数据 http/https协议
# 传输层 端口 tcp/udp协议 四层路由器/四层交换机
# tcp 面向连接 可靠 速度慢 长度不受限 全双工 流式传输
# 文件传输\邮件\实时通信
# 三次握手 :记那张图(SYN ACK) accept connect
# 1.三次握手是tcp协议建立连接的过程
# 2.由客户端发起一个syn请求,服务端接收并回复(syn\ack)
# 客户端收到ack和syn之后再回复一个ack
# 3.在原生的socket代码中三次握手是由accept connect
# 数据的交互 : 粘包现象 粘包成因
# 四次挥手 :记那张图(FIN ACK) close close
# udp 无连接 面向数据报 不可靠 速度快 长度受限制 一对一 一对多 多对多
# 短消息类\在线观看视频
# 网络层 ip协议 路由器/三层交换机
# 数据链路层 mac地址 arp协议 网卡/交换机
# 物理层
# 局域网和广域网的区别 : 相对论
# 内网和公网的区别 : ip地址的区别
# 特殊的ip : 0.0.0.0 127.0.0.1
# 什么是交换机\路由器
# 代码
# socket模块
# tcp服务
# udp服务 参数
# tcp的粘包
# 如何解决
# socketserver模块
# 固定的格式(背)
# 粘包现象
# 当多条消息发送时接受变成了一条或者出现接收不准确的情况
# 粘包现象会发生在发送端
# 两条消息间隔时间短,长度短 就会把两条消息在发送之前就拼在一起
# 节省每一次发送消息回复的网络资源
# 粘包现象会发生在接收端
# 多条消息发送到缓存端,但没有被及时接收,或者接收的长度不足一次发送的长度
# 数据与数据之间没有边界
# 本质 : 发送的每一条数据之间没有边界
# osi七层协议
# 应用层
# 表示层
# 会话层
# 传输层
# 网络层
# 数据链路层
# 物理层
# osi五层协议
# 应用层(五层)
# 传输层(四层) 端口 UDP TCP 四层交换机 四层路由器
# 网络层(三层) ipv4 ipv6协议 路由器 三层交换机
# 数据链路层(二层) mac arp协议 网卡 (二层)交换机
# 物理层(一层)
# TCP/IP协议
# 网络层 arp协议在网络层
# TCP协议 上传下载\发邮件 可靠 面向连接 速度慢 能传递的数据长度不限
# 建立连接 三次握手
# 消息传递 可靠传输
# 断开连接 四次挥手
# UDP协议 即时通讯工具 不可靠 面向数据报 速度快 能传递的数据长度有限
# 不管对方在不在 直接发送
# 不占连接
# 随时可以收发消息
操作系统基础
穿孔卡片
高速磁带
-- 操作系统
-
多道操作系统
-
第一次提出了多个程序可以同时在计算机中被计算
1.遇到IO就让出CPU
2.把CPU让给其他程序,让其他程序能够使用CPU
3.CPU的让出这件事 占用时间
4.两个程序来回在CPU上切换,不会
每个程序有独立的内存空间
每个程序在切换的前后会把当前程序的状态记录下来
CPU计算和不计算(IO)操作IO操作(网络操作\文件操作) : 输入输出:相对内存
阻塞: sleep\input\recv\accept\recvfrom是不需要cpu参与的
对文件的读取 : 对硬盘的操作一次读取相当于90w条代码
Input : 向内存输入数据 读\load\input\recv\recvfrom\accept\connect\close O
utput : 从内存输出数据 写\dump\print\send\sendto\accept\connect\close
所有的IO操作本质都是文件操作
input\print input是写入文件,然后通过读取文件把输入的内容加载到内存
print是直接写入文件,然后通过文件展示给用户看
socket中的交互方法 : 都是文件操作 send 是向缓存文件中写 recv 是从缓存文件中读
也就是说只要涉及到IO操作 至少就是一个0.009s=就是CPU执行90w条python代码的时间
0.009s 500000000条指令/s /5 = 100000000条python代码/s
0.009s * 100000000 = 900000条python代码
import dis
a = 1
def func():
global a
a+=1
dis.dis(func)
1.老教授和研究生
研究生 5min 没有IO操作 先来先服务(FIFS)
老教授 24h 没有IO操作 研究生 3min 没有IO操作 短作业优先算法
2.时间片轮转算法 -- 分时操作系统
1w = 0.00005s
1.时间片到了才让出CPU
2.CPU的让出这件事 占用时间
3.减低工作效率,提高了用户体验
-
进程
# 程序与进程(计算机中最小的资源分配单位)
# 运行中的程序 就是 进程
# 进程与进程之间的数据是隔离的
# 线程(计算机中能被操作系统调度的最小单位)
# 每个程序执行到哪个位置是被记录下来的
# 在进程中 有一条线程是负责具体的执行程序的
# 进程的调度(由操作系统完成的) :
# 被操作系统调度的,每个进程中至少有一个线程
# 短作业优先算法
# 先来先服务算法
# 时间片轮转算法
# 多级反馈算法
# 进程的启动 销毁
# 进程的启动 : 交互(双击) 在一个进程中启动另一个 开机自启动
# 负责启动一个进程的程序 被称为一个父进程
# 被启动的进程 被成为一个子进程
# 销毁 : 交互 被其他进程杀死(在父进程结束子进程) 出错进程结束
# 父子进程
# 父进程开启子进程
# 父进程还要负责对结束的子进程进行资源的回收
# 进程id --> processid --> pid
# 在同一台机器上 同一个时刻 不可能有两个重复的进程id
# 进程id不能设置 是操作系统随机分配的
# 进程id随着多次运行一个程序可能会被多次分配 每一次都不一样
# 进程的三状态图
# 就绪ready 运行run 阻塞block
# import os
# import time
#
# print(os.getpid())
# print(os.getppid()) # parent process id
# time.sleep(100)
# 2.模块multiprocessing模块 :内置模块
# multiple 多元化的
# processing 进程
# 把所有和进程相关的机制都封装在multiprocessing模块中了
3.学习这个模块 multiprocessing
import os
import time
from multiprocessing import Process
def func():
'''
在子进程中执行的func
:return:
'''
print('子进程 :',os.getpid(),os.getppid())
time.sleep(3)
if __name__ == '__main__':
p = Process(target=func)
p.start()
print('主进程 :',os.getpid())
# 并行 : 多个程序同时被CPU执行
# 并发 : 多个程序看起来在同时运行
# 同步 : 一个程序执行完了再调用另一个 并且在调用的过程中还要等待这个程序执行完毕
# 异步 : 一个程序在执行中调用了另一个 但是不等待这个任务完毕 就继续执行 start
# 阻塞 : CPU不工作
# 非阻塞 : CPU工作
# 多道操作系统 : 遇到IO就切换,支持多个进程同时被计算机执行
# 分时操作系统 : 时间片的概念 时间片到了就切换,更好的支持多个进程同时被计算机执行
# IO操作的概念 :
# time.sleep
# input和print
# 网络操作 : connect accept recv recvfrom send sendto close
# 文件的操作 : read write dump load
# 会发生人类能感知到的阻塞:
# sleep
# input
# accept
# connect
# recv/recvfrom
# 计算 : 除了文件操作和sleep之外,所有的python操作内存中数据的代码都是计算.都需要CPU参与
# + - * / ** %
# 各种书据类型的方法 :strip len split
# 进程的三状态图
# 就绪 运行 阻塞
# 阻塞,需要重新排队
import sys
# username = input() 阻塞
# username,password = sys.argv[1],sys.argv[2]
# ftp 命令行方式
# python ftp.py alex alex3714 upload d://xxx.mp4
# python ftp.py alex alex3714 download xxxx.mov
# python ftp.py alex alex3714 ls xxxx.mov
# 阻塞 : 你写的一段代码 在这段代码的执行期间CPU不工作
# 反过来就是非阻塞
# 同步 : 之前写的左右程序都是同步
# 异步 : 进程start()
# 同步阻塞 : input() sleep()
# 同步非阻塞 : len()
# from multiprocessing import Process
#
# def func(a):
# print('我是一个子进程',a)
#
# if __name__ == '__main__':
# # 为了避免在win操作系统中递归开启子进程而必须要写的
# # 主要是因为不同的操作系统开启进程的方式不同
# p = Process(target=func,args=(1,))
# p.start()
process模块
import os
import time
from multiprocessing import Process
def son_process():
print('strat son',os.getpid())
time.sleep(50)
print('end son')
if __name__ == '__main__':
print(os.getpid())
Process(target=son_process).start()
主进程是在子进程执行完毕之后才结束
主进程回收子进程的资源
多个子进程
def son_process():
print('strat son',os.getpid())
time.sleep(1)
print('end son')
if __name__ == '__main__':
print(os.getpid())
Process(target=son_process).start()
Process(target=son_process).start()
Process(target=son_process).start()
def son_process():
print('strat son',os.getpid())
time.sleep(1)
print('end son')
if __name__ == '__main__':
for i in range(5):
Process(target=son_process).start()
join
import time
def son_process():
time.sleep(4)
print('通知xxx用户,机器挂掉了')
if __name__ == '__main__':
p = Process(target=son_process)
p.start()
p.join() # 阻塞,直到p对应的子进程对象执行结束
print('所有任务结束')
import time
def son_process(n):
print('start', n)
time.sleep(2)
print('end',n)
if __name__ == '__main__':
p_l = []
for i in range(10):
p = Process(target=son_process,args=(i,))
p.start() # start相当于告诉操作系统要开启一个子进程,而子进程的调度是由操作系统控制的
p_l.append(p)
for p in p_l:p.join() # join 如果执行这句话的时候子进程已经结束了,那么join就不阻塞了
print('所有任务结束')
守护进程
守护进程会随着父进程的代码结束而结束
import time
def son():
while True:
time.sleep(1)
print('in son')
if __name__ == '__main__':
p = Process(target=son)
p.daemon = True # 将当前的子进程设置为守护进程
p.start()
time.sleep(5)
正常情况下 父进程永远会等着子进程结束
如果设置了守护进程 父进程的代码结束之后 守护进程也跟着结束
子进程结束之后,父进程才会结束
代码结束和进程结束是两回事儿
没设置守护进程
1.子进程的代码和主进程的代码自己执行自己的,互相之间没关系
2.如果主进程的代码先结束,主进程不结束,等子进程代码结束,回收子进程的资源,主进程才结束
3.如果子进程的代码先结束,主进程边回收子进程的资源边执行自己的代码,当代码和资源都回收结束,主进程才结束
设置了守护进程
1.子进程的代码和主进程的代码自己执行自己的,互相之间没关系
2.一旦主进程的代码先结束,主进程会先结束掉子进程,然后回收资源,然后主进程才结束
import time
def son():
while True:
time.sleep(1)
print('in son')
def son2():
print('start son2')
time.sleep(10)
print('end son2')
if __name__ == '__main__':
p = Process(target=son)
p.daemon = True
p.start()
Process(target=son2).start()
time.sleep(5)
守护进程不会守护除了主进程代码之外的其他子进程
偏要求守护进程在子进程结束之后才结束
import time
def son():
while True:
time.sleep(1)
print('in son')
def son2():
print('start son2')
time.sleep(10)
print('end son2')
if __name__ == '__main__':
p = Process(target=son)
p.daemon = True
p.start()
p = Process(target=son2)
p.start()
time.sleep(5)
p.join()
Process对象的方法 ****
start()启动子进程
join()阻塞主进程
terminate()杀死子进程
isalive()子进程是否运行
对象.daemon==True守护进程,主进程代码执行完(并不一定结束)守护进程停止运行
import time
def son():
while True:
time.sleep(1)
print('in son')
if __name__ == '__main__':
p = Process(target=son)
p.start()
time.sleep(5)
print(p.is_alive())
p.terminate() # 异步非阻塞操作
time.sleep(0.1)
print(p.is_alive())
print('我还可以继续做其他的事情,主进程的代码并不结束')
面向对象的方式开启子进程
class MyProcess(Process):
def run(self):
print(os.getpid())
if __name__ == '__main__':
print('主 :',os.getpid())
MyProcess().start()
传参数
class MyProcess(Process):
def __init__(self,arg1,arg2):
super().__init__() # 执行父类的init
self.a1 = arg1
self.a2 = arg2
def run(self):
print(os.getpid(),self.a1,self.a2)
if __name__ == '__main__':
print('主 :',os.getpid())
MyProcess(1,2).start()
进程之间的数据是否隔离
n = 0
def son():
global n
n += 1
for i in range(20):
son()
print(n)
n = 0
def son():
global n
n += 1
if __name__ == '__main__':
p_l = []
for i in range(20):
p = Process(target=son)
p.start()
p_l.append(p)
for p in p_l:p.join()
print(n)
进程锁
# 数据安全(锁) :用来保证数据安全
# 如果多个进程同时对一个文件进行操作会出现什么问题
# 1.读数据 : 可以同时读
# 2.写数据 : 但不能同时写
from multiprocessing import Process,Lock
def change(lock):
print('一部分并发的代码,多个进程之间互相不干扰的执行着')
lock.acquire() # 给这段代码上锁
with open('file','r') as f:
content = f.read()
num = int(content)
num += 1
for i in range(1000000):i+=1
with open('file','w') as f:
f.write(str(num))
lock.release() # 给这段代码解锁
print('另一部分并发的代码,多个进程之间互相不干扰的执行着')
if __name__ == '__main__':
lock = Lock()
for i in range(10):
Process(target=change,args=(lock,)).start()
# 当多个进程同时操作文件/共享的一些数据的时候就会出现数据不安全
# 开启多进程 同时执行100000行代码
# 其中20行涉及到了操作同一个文件
# 只给这20行代码枷锁,来保证数据的安全
IPC通信(进程间通信)
进程之间通信 IPC
Inter Process Communication
from multiprocessing import Queue,Process
def son(q):
print(q.get())
if __name__ == '__main__':
q = Queue()
p = Process(target=son,args=(q,))
p.start()
q.put(123)
在进程之间维护数据的安全 -- 进程安全
队列是进程安全的(进程队列保证了进程的数据安全)
队列都是先进先出的
队列是基于文件 + 锁实现的
队列一共提供两个方法:get put
q = Queue()
q.put({1,2,3})
num = q.get() # get是一个同步阻塞方法,会阻塞直到数据来
print(num)
q = Queue(2)
q.put({1,2,3})
q.put({1,2,3})
q.put({1,2,3}) # put是一个同步阻塞方法,会阻塞直到队列不满
import queue
q = Queue(2)
try:
for i in range(4):
q.put_nowait(i) # put_nowait 同步非阻塞方法
except queue.Full:
print(i)
q2 = Queue(2)
try:
print(q2.get_nowait())
except queue.Empty:pass
q = Queue(5)
ret = q.qsize() # 查看当前队列有多少值
print(q.empty())
print(q.full())
生产者消费者模型
队列Queue = 管道Pipe + 锁
Pipe 基于文件实现的(socket+pickle) = 数据不安全
Queue 基于文件(socket+pickle)+锁(lock)实现的 = 数据安全
基于pipe+锁(lock)实现的
IPC:
内置的模块实现的机制 :队列\管道
第三方工具 : redis rabbitMQ memcache
小结
概念
是操作系统进行资源分配 的最小单位,是操作系统结构的基础
进程是线程的容器,
进程的调度
先来先服务
短作业优先算法
时间片轮转法
多级反馈队列
进程的并行和并发
并行:两个或多个(进程)同时执行
并发:在资源有限的情况下,两者交替轮流使用资源,目的提高效率
进程的三状态
就行 运行 阻塞
利用multiprocess.Process模块
例:
import time
from multiprocessing import Process
def f(name):
print('hello', name)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=f, args=('bob',))
p.start()
time.sleep(1)
print('执行主进程的内容了')
Process模块介绍
Process( ),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
target表示调用对象,即子进程要执行的任务 target=子进程的名字
args表示调用对象的位置参数元组,args=(1,2,'egon',)
方法介绍
p.start():启动进程,并调用该子进程中的p.run()
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,
该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止
(强调:是主线程处于等的状态,而p是处于运行的状态)。
timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,
而不能join住run开启的进程
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,
当p的父进程终止时,p也随之终止,并且设定为True后,
p不能创建自己的新进程,必须在p.start()之前设置
p.pid:进程的pid
p.name:进程的名称
注意:
在win系统中,要创建进程时(其他系统就不用了)
必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来
import 的时候 ,就不会递归运行了。
多进程同时运行
例:
import time
from multiprocessing import Process
def f(name):
print('hello', name)
time.sleep(1)
if __name__ == '__main__':
p_lst = []
for i in range(5):
p = Process(target=f, args=('bob',))
p.start()
p_lst.append(p)
# [p.join() for p in p_lst]
print('父进程在执行')
另一种开启进程的方式,继承Process类的形式开启进程
例:
import os
from multiprocessing import Process
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(os.getpid())
print('%s 正在和女主播聊天' %self.name)
if __name__ == '__main__':
p1=MyProcess('wupeiqi')
p2=MyProcess('yuanhao')
p3=MyProcess('nezha')
p1.start() #start会自动调用run
p2.start()
# p2.run()
p3.start()
p1.join()
p2.join()
p3.join()
print('主线程')
守护进程 会随着主进程的结束而结束
进程之间是相互独立的,主进程代码运行结束,守护进程随即终止
from multiprocessing import Process
import time
def foo():
print(123)
time.sleep(1)
print("end123")
def bar():
print(456)
time.sleep(3)
print("end456")
p1=Process(target=foo)
p2=Process(target=bar)
if __name__ == '__main__':
p1.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
p1.start()
p2.start()
time.sleep(0.1)
print("main-------")#打印该行则主进程代码结束,则守护进程p1应该被终止.#可能会有p1任务执行的打印信息123
# ,因为主进程打印main----时,p1也执行了,但是随即被终止.
socket 聊天并发
sever端
from socket import *
from multiprocessing import Process
server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)
def talk(conn,client_addr):
while True:
try:
msg=conn.recv(1024)
if not msg:break
conn.send(msg.upper())
except Exception:
break
if __name__ == '__main__': #windows下start进程一定要写到这下面
while True:
conn,client_addr=server.accept()
p=Process(target=talk,args=(conn,client_addr))
p.start()
client端
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8'))
multiprocess.lock 锁
作用:保护数据安全
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,
即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1.效率低(共享数据基于文件,而文件是硬盘上的数据)
2.需要自己加锁处理
例:
import os
import time
import random
from multiprocessing import Process,Lock
def work(lock,n):
lock.acquire()
print('%s: %s is running' % (n, os.getpid()))
time.sleep(random.random())
print('%s: %s is done' % (n, os.getpid()))
lock.release()
if __name__ == '__main__':
lock=Lock()
for i in range(3):
p=Process(target=work,args=(lock,i))
p.start()
IPC 通信:
进程间的通信 (作用:提高效率)
队列 multiprocess.Queue
Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递
队列特点:
1在进程之间维护数据安全
2.队列都是先进先出
3.队列都是基于文件+锁实现的
4..队列提供了get和put两种方法
get()取 put()放
Queue 基于文件(socket+pickle)+锁实现
Queue = 管道(Pipe)+锁
IPC:
内置模块实现(队列和管道)
第三方模块(Redis rabbitMQ )
线程
概念
线程是CPU调度的最小单位,每个进程中至少有一个线程
特点:
线程间资源共享
threading模块中的Thread类
方式一:
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' %name)
if __name__ == '__main__':
t=Thread(target=sayhi,args=('egon',))
t.start()
print('主线程')
方式二
from threading import Thread
import time
class Sayhi(Thread):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
time.sleep(2)
print('%s say hello' % self.name)
if __name__ == '__main__':
t = Sayhi('egon')
t.start()
print('主线程')
thread类的其他方法:
Thread实例对象的方法
isAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
start()将线程提交给CPU,由CPU进行调度
join()等待 同进程中的join
setDaemon 设置成为守护线程
示例:
import threading
loop = 10000000
number = 0
def _add(count):
global number
for i in range(count):
number += 1
def _sub(count):
global number
for i in range(count):
number -= 1
t1 = threading.Thread(target=_add,args=(loop,))
t2 = threading.Thread(target=_sub,args=(loop,))
t1.start()
t1.join() # t1线程执行完毕,才继续往后走
t2.start()
t2.join() # t2线程执行完毕,才继续往后走
print(number)
setDaemon 设置成为守护线程
import threading
import time
def task(arg):
time.sleep(5)
print('任务')
t = threading.Thread(target=task,args=(11,))
t.setDaemon(True)
t.start()
print('END')
threading模块提供的一些方法:
threading.currentThread(): 返回当前的线程变量。
threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
基于socket和多线程实现类似于socketserver模块的功能
import socket
import threading
def task(connect,address):
pass
server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5)
while True:
conn,addr = server.accept()
# 处理用户请求
t = threading.Thread(target=task,args=(conn,addr,))
t.start()
线程安全
线程安全的例子:
logging模块 列表 字典
使用锁来保证数据安全,来了多个线程,使用锁让他们排队,逐一执行:
使用锁的时候注意不要多个锁交叉使用,避免形成死锁
例一:互斥锁
import threading
import time
timenum = 0
# 线程锁
lock = threading.Lock()
def task():
global num
# # 申请锁
lock.acquire()
num += 1
time.sleep(0.2)
print(num)
# 释放锁
lock.release()
with lock:
num += 1
time.sleep(0.2)
print(num)
for i in range(10):
t = threading.Thread(target=task)
t.start()
例二:递归锁
import threading
import time
num = 0
# 线程锁
lock = threading.RLock()
def task():
global num
# 申请锁
lock.acquire()
num += 1
lock.acquire()
time.sleep(0.2)
print(num)
# 释放锁
lock.release()
lock.release()
for i in range(10):
t = threading.Thread(target=task)
t.start()
线程中的单例模式
import threading
import time
class Singleton:
instance = None
lock = threading.RLock()
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
with cls.lock:
if cls.instance:
return cls.instance
time.sleep(0.1)
cls.instance = object.__new__(cls)
return cls.instance
def task():
obj = Singleton('x')
print(obj)
for i in range(10):
t = threading.Thread(target=task)
t.start()
# 执行1000行代码
data = Singleton('a4sdfasdf')
print(data)
GIL:全局解释器锁
同一时刻保证一个进程中只有一个线程可以被cpu调度,所以在使用Python开发时要注意:
计算密集型,用多进程.
IO密集型,用多线程.
进程池&线程池
作用:
保证程序中最多可以创建的线程(进程)个数,防止无节制的创建线程(进程),导致性能降低
线程池
例一:
import time
from concurrent.futures import ThreadPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
pool = ThreadPoolExecutor(10)# 创建线程池
for i in range(100):
pool.submit(task, i, 1)
print('END')
pool.shutdown(True)# 等线程池中的任务执行完毕之后,再继续往下走
print('其他操作,依赖线程池执行的结果')
例二:
import time
from concurrent.futures import ThreadPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
return n1+n2
pool = ThreadPoolExecutor(10)# 创建线程池
future_list = []
for i in range(20):
fu = pool.submit(task, i, 1)
future_list.append(fu)
pool.shutdown(True)
for fu in future_list:
print(fu.result())
进程池
例:
import time
from concurrent.futures import ProcessPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
if __name__ == '__main__':
pool = ProcessPoolExecutor(10) # 创建进程池
for i in range(20):
pool.submit(task, i, 1)
print('END')
协程
定义:
协程又称微线程,是程序员自己定义的
优点:
1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点:
1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
特点:
必须在只有一个单线程里实现并发
修改共享数据不需加锁
用户程序里自己保存多个控制流的上下文栈
附加:一个协程遇到IO操作自动切换到其它协程
(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
协程的实现需要安装第三方模块 gevent
from gevent import monkey
monkey.patch_all()一定要放到导入socket模块之前,否则gevent无法识别socket的阻塞
通过gevent实现单线程下的socket并发
server端:
from gevent import monkey;monkey.patch_all()
from socket import *
import gevent
#如果不想用monkey.patch_all()打补丁,可以用gevent自带的socket
# from gevent import socket
# s=socket.socket()
def server(server_ip,port):
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((server_ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr)
def talk(conn,addr):
try:
while True:
res=conn.recv(1024)
print('client %s:%s msg: %s' %(addr[0],addr[1],res))
conn.send(res.upper())
except Exception as e:
print(e)
finally:
conn.close()
if __name__ == '__main__':
server('127.0.0.1',8080)
多线程并发多个客户端:
from threading import Thread
from socket import *
import threading
def client(server_ip,port):
c=socket(AF_INET,SOCK_STREAM) #套接字对象一定要加到函数内,即局部名称空间内,放在函数外则被所有线程共享,则大家公用一个套接字对象,那么客户端端口永远一样了
c.connect((server_ip,port))
count=0
while True:
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))
msg=c.recv(1024)
print(msg.decode('utf-8'))
count+=1
if __name__ == '__main__':
for i in range(500):
t=Thread(target=client,args=('127.0.0.1',8080))
t.start()
协程要和IO操作配合
协程+IO切换+爬虫实例:
from gevent import monkey
monkey.patch_all() #一定要加猴子补丁
import gevent
import requests
def f1(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
def f2(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
def f3(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
gevent.joinall([ gevent.spawn(f1, 'https://www.python.org/'),
gevent.spawn(f2, 'https://www.yahoo.com/'),
gevent.spawn(f3, 'https://github.com/'), ])
进程、线程和协程的异同
共同点:
都可以提高并发
区别:
进程是计算机分配资源的最小单位,线程是被CPU调用的最小单位,协程又称为微线程,是基于代码人为创造的
,而进程和线程是计算机中真实存在的,一个进程中可以有多个线程,一个线程可以创建多个协程;
在python中由于有GIL锁,所以在同一时刻的同一进程中只能有一个线程被CPU调用,在python开发中
计算密集型应用用多进程
IO密集型用多线程
单纯的协程是没有办法并发,只是代码之间的来回切换,加上io自动切换才有意义,有 io操作时才用协程
进程之间不能资源共享,线程间的资源是 共享的,协程需要导入第三方库,需要加补丁
发邮件
先设置POP3/SMTP/IMAP
# 示例:
# 1. 申请126或163邮箱
# 2. 开启服务+授权码
# 3. 通过代码发送
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
# 写邮件的内容
msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["炮手", 'zh309603@163.com'])
msg['To'] = formataddr(["老板", '424662508@qq.com'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP_SSL("smtp.163.com", 465)
server.login("zh309603", "zhzzhz123") # 授权码
server.sendmail('zh309603@163.com', ['424662508@qq.com', ],
msg.as_string())
server.quit()
生产者消费者模型(以发邮件为例)
import threading
from queue import Queue
import time
q = Queue()
def send(to,subject,text):
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
# 写邮件的内容
msg = MIMEText(text, 'plain', 'utf-8')
msg['From'] = formataddr(["炮手", 'zh309603@163.com'])
msg['To'] = formataddr(["老板", to])
msg['Subject'] = subject
server = smtplib.SMTP_SSL("smtp.163.com", 465)
server.login("zh309603", "zhzzhz123") # 授权码
server.sendmail('zh309603@163.com', [to, ], msg.as_string())
server.quit()
def producer(i):
"""
生产者
:return:
"""
print('生产者往队列中放了10个任务',i)
info = {'to':'424662508@qq.com', 'text':'你好','subject':'好友请求'}
q.put(info)
def consumer():
"""
消费者
:return:
"""
while True:
print('消费者去队列中取了任务')
info = q.get()
print(info)
send(info['to'],info['subject'],info['text'])
for i in range(10):
t = threading.Thread(target=producer,args=(i,))
t.start()
for j in range(5):
t = threading.Thread(target=consumer)
t.start()
Python内存管理机制
Python 是动态类型语言,对象的类型和内存占用都是运行时确定的,一边运行一边解释。
python解释器通过以下三种方式来管理内存
1.对象的引用次数
引用计数特点:如果引用计数如果是0,就把这个值从内存中释放掉
缺点:在维护引用计数时,有可能数据产生循环引用,造成数据不能删除,造成内存泄漏
2.垃圾回收机制
引用计数为主,标记清除和分带回收为辅
标记清除:检测标记该对象,避免出现循环引用不能删除的现象
标记清除就是一样的变量名引用多少次,只需要一次删除就都删掉了
分带回收:把内存中的数据分成三个区域:新生代0,老年代1,永久带2
3.内存池机制
python垃圾回收机制
Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题。通过“分代回收”(generation collection)以空间换取时间来进一步提高垃圾回收的效率。没有变量引用及回收