跨平台性

平台 = 操作系统 + 硬件,跨平台就是多平台使用

操作系统:
    应用程序(系统软件【控制底层硬件】和应用软件【相关逻辑】)
    操作系统,比如调用win的api
    硬件
CPU指令集:(应用程序通过CPU来控制硬件)
    精简指令集
    复杂指令集
CPU具备运算器和控制器功能

操作系统能控制其他硬件的指令集和运算相关的指令集
应用程序只能 运算相关的指令集

内核态开放所有指令集,运行的是操作系统
用户态只开放运算指令集,运行的是应用程序
计算机工作时频繁的发生内核态和用户态的状态切换

2核4线程-》4核8线程

存储器访问速度:
寄存器 > 高速缓存 > 内存 > 磁盘 > 磁带

存储器:
    RAM:随机存储器
    ROM:只读存储器BIOS
    CMOS:存取速度慢,断点数据丢失,耗电量极低
    机械硬盘:
    固态硬盘:
exe文件运行流程:
双击exe快捷方式-》找到exe文件的绝对路径
操作系统找到exe文件在硬盘中的位置,控制其代码从硬盘加载到内容
然后控制cpu从内存中读取刚刚输入内存的应用程序的代码执行,应用程序完成启动

编译型和解释型
编译型:开发效率低,运行效率高,编译器,不好逆,反编译,跨平台性差
解释型:开发效率高,运行效率低,解释器(解释一行执行一行),跨平台性强
运行python的两种方式
  1. 交互式 2.脚本

python解释器运行步骤:启动python3.8解释器,把1.py文件当成文本文件从硬盘载入到内存,解释器开始执行刚刚读入的python代码开始识别python语法

变量

变量不用了记得释放,否则会导致内存溢出;转义\,使之仅成为字符

垃圾回收机制

这里的栈区有两部分:比如x是他的名字,0xfffff0000是它所指向的堆区的地址

HRICi6.png

引用计数,分为直接引用和间接引用(被引用的次数,一旦变成0就会被回收),del x,解除x和10的绑定关系

标记清除(凡是从根出发的,无根清除),为了防止循环引用导致内存泄露;扫描栈区,如果没有直接引用,那么直接清除

分代回收(分区设置扫描时间)

del x  # 解除变量名x与值10的绑定关系,10的引用计数变为2
三大组成部分

变量名,变量值,赋值符号(内存地址)

print(id(name))
print(name)
print(type(name))
is与==

is是比较id是否都相同

==是比较值是否相同

这里要注意一点,如果你在python3在线运行时和在pycharm里是不一样的

bxJpKs.png

如图,在cmd中的python里id是不同的,但是在pycharm里id是相同的

bxJVGF.png

这是因为pycharm独断专行,

小整数池[-5,256]

在这个范围内的数命名时相等的话id也会相等,目的是为了节约空间

bxJLLR.png

命名规则-驼峰命名法

类/对象:xx__yy_zz

变量:xxYyZz

常量:XxYyZz

基本数据类型

HyHFrn.png

数字类型

整型int,浮点型float

字符串类型

str

布尔bool

true,false

列表
字典

栈存放变量以及在堆中的值的内存地址,堆存放数据和new出来的对象

与用户交互

输入
输入单值
username = input("请输入您的账号:")     #  接收到的为字符串
输入多值
username,password = input("请输入您的账号:").split(' ')
输入多值并给一个数组
username = input("请输入您的账号:").split(' ')

字符串的格式化输出
%
res="my name is %s my age is %s" %("18",'egon')
以字典的形式传值,打破位置的限制
res="我的名字是 %(name)s 我的年龄是 %(age)s" %{"age":"18","name":'egon'}
%s可以接收任意类型                    %d只能接收int
str.format:兼容性好
res='我的名字是 {} 我的年龄是 {}'.format('egon',18)
print(res)

res='我的名字是 {0}{0}{0} 我的年龄是 {1}{1}'.format('egon',18)
print(res)

res="我的名字是 {name} 我的年龄是 {age}".format(age=18,name='egon')
print(res)

f:python3.5以后才推出
x = input('your name: ')
y = input('your age: ')
res = f'我的名字是{x} 我的年龄是{y}'
print(res)
取前三个值
x,y,z,*_=salaries=[111,222,333,444,555] # *会将没有对应关系的值存成列表然后赋值给紧跟其后的那个变量名,此处为_

取后三个值
*_,x,y,z=salaries=[111,222,333,444,555]

前1个后2个
x,*_,y,z=salaries=[111,222,333,444,555]
格式化字符串

%s res = "my name is %s my age is %s" %('egon','18')

res = "my name is %(name)s,my age is %(age)s" %('name':'batmanfuture','age':'18')

str.format res = "my name is {name}, and my age is {age}".format(age=18,name='batmanfuture')

f "" res = f"my name is {x},my pwd is {y}"

可变不可变类型

可变类型:值改变,id不变,证明改的是原值,证明原值是可以被改变的
int、float、str,bool都被设计成了不可分割的整体,不能够被改变

不可变类型:值改变,id也变了,证明是产生新的值,压根没有改变原值,证明原值是不可以被修改的
list,dict是可变类型

布尔值

显式布尔值和隐式布尔值

比如print(age > 16)是显式,而'aaa'是隐式

逻辑运算符

not > and > or

成员运算符

print("egon" in "hello egon") # 判断一个字符串是否存在于一个大字符串中
print("e" in "hello egon") # 判断一个字符串是否存在于一个大字符串中
判断key是否存在于字典
print(111 in {"k1":111,'k2':222})
print("k1" in {"k1":111,'k2':222})

not in
print("egon" not in "hello egon") # 推荐使用
print(not "egon" in "hello egon") # 逻辑同上,但语义不明确,不推荐

2、身份运算符
is # 判断的是id是否相等
流程控制之if

if,elif,else

深浅copy

直接赋值相等:
比如list1 = list2, 这是完全把list2的地址给了list1,两个指向到的同一个地址
浅copy:
如果是两层列表,那么第一层的列表id地址变化,第二层不变,这里就是指可变变量
深copy:
如果是两层列表,那么第一层的列表id地址变化,第二层也变化,这里就是指可变变量
直接赋值

bxwpfP.png

浅copy

这里list1[2]和list3[2]就是第二层列表id地址,发现他们相同

bxdzFI.png

深copy

这里list1[2]和list3[2]就是第二层列表id地址,发现他们不相同

bxw5ng.png

HW6SzR.jpg

while循环

if循环

列表循环取值
l = ['alex_dsb', 'lxx_dsb', 'egon_nb']
for x in l:  # x='lxx_dsb'
    print(x)

字典循环取值
dic={'k1':111,'k2':2222,'k3':333}
for k in dic:
    print(k,dic[k])

字符串循环取值
msg="you can you up,no can no bb"
for x in msg:
    print(x)

for循环控制循环次数:range()
for i in range(30):
    print('===>')

for搭配range,可以按照索引取值,但是麻烦,所以不推荐
l=['aaa','bbb','ccc'] # len(l)
for i in range(len(l)):
    print(i,l[i])

for+continue

数字类型

int类型
类型转换
int(10)

进制转换
10进制 -> 二进制
print(bin(11)) # 0b1011

10进制 -> 八进制
print(oct(11)) # 0o13

10进制 -> 十六进制
print(hex(11)) # 0xb
print(hex(123)) # 0xb

二进制->10进制
print(int('0b1011',2)) # 11

二进制->8进制
print(int('0o13',8)) # 11

二进制->16进制
print(int('0xb',16)) # 11

abs        绝对值
round    四舍五入
min        最大
max        最小
len        计算个数
float类型
类型转换
res=float("3.1")
int与float没有需要掌握的    内置方法
他们的使用就是        数学运算+比较运算

字符串类型

//去除左右两边空格字符,包括\n
strip()

//去掉左右两边的*
strip('*')

//去除多个
strip('*-+=')

split(':')
//只切以此
split(':',1)
//从右向左切
rsplit(':')

//判断是不是以xxx开头的
print("my name is batmna,and l diss".startswith("my"))
//判断是不是以xxx结尾的
print("my name is batmna,and l diss".endswith("diss"))

//判断是不是以xxx开头的
print("my name is batmna,and l diss".startswith("my"))
//判断是不是以xxx结尾的
print("my name is batmna,and l diss".endswith("diss"))

w = "123"
res=":".join(w)
print(res)
--------------------
res=":".join(l)

//replace替换,并且替换几次
print(msg.replace("you","YOU",1))

//判断字符串是否由纯数字组成
 print('12.3'.isdigit())

//返回要查找的字符串在大字符串中的起始索引
print(msg.find('egon'))
print(msg.index('egon'))

//统计个数
print(msg.count('egon'))

//中间egon,两侧用*填充直到50个,同理还有ljust,rjust
print('egon'.center(50,'*'))
print('egon'.ljust(50,'*'))
print('egon'.rjust(50,'*'))
//规定用0填充
print('egon'.zfill(10))

//设置制表符代表的空格数为2
print(msg.expandtabs(2))

//首字母大写
print("hello world egon".capitalize())

//大小写反转
print("Hello WorLd EGon".swapcase())

//每个单词的首字母大写
print("hello world egon".title())

//判断是否小写
print('abc'.islower())

//判断是否大写
print('ABC'.isupper())

//判断单词首字母是否都大写
print('Hello World'.istitle())

//字母或者数字组成
print('123123aadsf'.isalnum())

//字母组成
print('ad'.isalpha())


//是不是空格
print('     '.isspace())

//判断名字是否合法
print('print'.isidentifier())
print('age_of_egon'.isidentifier())
print('1age_of_egon'.isidentifier())

num1=b'4' #bytes
num2=u'4' #unicode,python3中无需加u就是unicode
num3='四' #中文数字
num4='Ⅳ' #罗马数字

isdigit只能识别:num1、num2
isnumberic可以识别:num2、num3、num4
isdecimal只能识别:num2

列表类型

类型转换: 但凡能够被for循环遍历的类型都可以当做参数传给list()转成列表

res=list('hello')
print(res)

res=list({'k1':111,'k2':222,'k3':3333})
print(res)

内置方法

正向取
print(l[0])
反向取
print(l[-1])
可以取也可以改:索引存在则修改对应的值
l[0]=222
print(l)

切片(顾头不顾尾,步长)                        切片等同于拷贝行为,而且相当于浅copy
l = [111, 'egon', 'hello', 'a', 'b', 'c', 'd', [1, 2, 3]]
print(l[0:3])
print(l[0:5:2]) # 0 2 4

成员运算innot in
print('aaa' in ['aaa', 1, 2])
print(1 in ['aaa', 1, 2])

追加
l=[111,'egon','hello']
l.append(3333)
l.append(4444)
print(l)

插入值
l=[111,'egon','hello']
l.insert(0,'alex')
print(l)

extend该方法没有返回值,但会在已存在的列表中添加新的列表内容
aList = [123, 'xyz', 'zara', 'abc', 123];
bList = [2009, 'manni'];
aList.extend(bList)
print "Extended List : ", aList ;

>>>Extended List :  [123, 'xyz', 'zara', 'abc', 123, 2009, 'manni']

删除
方式一:通用的删除方法,只是单纯的删除、没有返回值
del l[1]

方式二:l.pop()根据索引删除,会返回删除的值
l.pop(1) # 不指定索引默认删除最后一个

方式三:l.remove()根据元素删除,返回None
l.remove([1,2,3])

循环
l.count()#返回数量

l.index()#返回索引

l.clear()# 清空列表,类似于 del a[:]

l.reverse()#不是排序,就是将列表倒过来

l.sort():#列表内元素必须是同种类型才可以排序
l=[11,-3,9,2,3.1]
l.sort() # 默认从小到大排,称之为升序
l.sort(reverse=True) # 从大到小排,设置为降序
print(l)

队列:FIFO,先进先出
# 入队操作
l.append('first')

# 出队操作
print(l.pop(0))

# 入栈操作
l.append('first')

# 出队操作
print(l.pop())

元组类型

元组就是"一个不可变的列表"

类型转换
print(tuple('hello'))

按索引取值(正向取+反向取):只能取
print(t[0])
print(t[-1])

切片(顾头不顾尾,步长)
print(t[0:3])
print(t[::-1])

长度
t=('aa','bbb','cc','dd','eee')
print(len(t))

成员运算innot in
print('aa' in t)

字典类型

造字典的方式一:
d={'k1':111,(1,2,3):222} # d=dict(...)

造字典的方式二:
d=dict(x=1,y=2,z=3)

数据类型转换
info=[
    ['name','egon'],
    ('age',18),
    ['gender','male']
]
d={}
for k,v in info: # k,v=['name','egon'],
    d[k]=v
print(d)

造字典的方式三:
res=dict(info) # 一行代码搞定上述for循环的工作

造字典的方式四:快速初始化一个字典
keys=['name','age','gender']
d={}
for k in keys:
    d[k]=None
print(d)
d={}.fromkeys(keys,None) # 一行代码搞定上述for循环的工作
print(d)

内置方法
pop删除:根据key删除元素,返回删除key对应的那个value值
res=d.pop('k2')

通用删除
del d['k1']

popitem删除:随机删除,返回元组(删除的key,删除的value)
res=d.popitem() #随机删,返回刚刚随机删除的key和value

d.update()
d.update({'k2':222,'k3':333,'k1':111111111111111})

d.get() #根据key取值,容错性好
print(d['k2'])  # key不存在则报错
tang.get('fengzi','meiyou')    #判断key是否有对应,有则输出,没有则添加

d.setdefault()                #如果key没有则添加,返回字典中key对应的值
res=info.setdefault('name','egon')

集合类型

去重        在{}内用逗号分隔开多个元素,多个元素满足以下三个条件
1.集合内元素必须为不可变类型
2.集合内元素无序
3.集合内元素没有重复

s={1,2} # s=set({1,2})

s={} # 默认是空字典

类型转换
set({1,2,3})
res=set('hellolllll')
print(res)

内置方法
取交集:两者共同的好友
res=friends1 & friends2
print(res)
print(friends1.intersection(friends2))

取并集/合集:两者所有的好友
print(friends1 | friends2)
print(friends1.union(friends2))

取差集:取friends1独有的好友
print(friends1 - friends2)
print(friends1.difference(friends2))

对称差集: 求两个用户独有的好友们(即去掉共有的好友)
print(friends1 ^ friends2)
print(friends1.symmetric_difference(friends2))

内置方法1:discard
s.discard(4) # 删除元素不存在do nothing
print(s)
s.remove(4) # 删除元素不存在则报错

内置方法2:update
s.update({1,3,5})
print(s)

内置方法3:pop
res=s.pop()
print(res)

内置方法4:add
s.add(4)
print(s)

res=s.isdisjoint({3,4,5,6}) # 两个集合完全独立、没有共同部分,返回True
print(res)

编码/解码

解决python2编码
# coding: 与文件存的编码一致
x = u"上"

解决python2编码
# coding: 与文件存的编码一致
x = "上"

x.encode('gbk')

内存固定使用unicode,和utf-8转换

当在内存时,字符都转换为unicode编码,在进入硬盘时,再再转换为utf-8编码

#coding:gbk

文件操作

文件是操作系统提供给用户/应用程序操作硬盘的一种虚拟的概念/接口,用户/应用程序可以通过文件将数据永久

保存的硬盘中,即操作文件就是操作硬盘,用户/应用程序直接操作的是文件,对文件进行的所有的操作,都是

在向操作系统发送系统调用,然后再由操作将其转换成具体的硬盘操作

open()
控制文件读写内容的模式:t和b

强调:t和b不能单独使用,必须跟r/w/a连用

t文本(默认的模式)        
           1、读写都以str(unicode)为单位的
            2、文本文件
            3、必须指定encoding='utf-8'

b二进制/bytes
控制文件读写操作的模式
        r只读模式
        w只写模式
        a只追加写模式
        +:r+、w+、a+
步骤
1、打开文件
windows路径分隔符问题
open('C:\a.txt\nb\c\d.txt')
解决方案一:推荐
open(r'C:\a.txt\nb\c\d.txt')                  rawstring
解决方案二:
open('C:/a.txt/nb/c/d.txt')
f=open(r'aaa/a.txt',mode='rt') # f的值是一种变量,占用的是应用程序的内存空间
print(f)
# 2、操作文件:读/写文件,应用程序对文件的读写请求都是在向操作系统发送
# 系统调用,然后由操作系统控制硬盘把输入读入内存、或者写入硬盘
res=f.read()
print(type(res))
# print(res)
# 3、关闭文件
f.close() # 回收操作系统资源

# print(f)
# f.read() # 变量f存在,但是不能再读了

# del f     # 回收应用程序资源

# with是为了防止有人忘记close而来的                    当一行太长时,可以用\换行
with open('a.txt',mode='rt') as f1: # f1=open('a.txt',mode='rt')
    res=f1.read()
    print(res)
# 没有指定encoding参数操作系统会使用自己默认的编码
# linux系统默认utf-8
# windows系统默认gbk

当在windows上打开一个a.txt文件时,a.txt是以utf-8存进去的,在f.read()打开时,会把那一串二进制数进行gbk(操作系统默认)解码,当然会乱码,所以要用encoding="utf-8",告诉操作系统要用utf-8解码,解码成功后再按照Unicode编码
with open('c.txt',mode='rt',encoding='utf-8') as f:
    res=f.read() # t模式会将f.read()读出的结果解码成unicode
    print(res,type(res))

r(默认的操作模式):只读模式,当文件不存在时报错,当文件存在时文件指针跳到开始位置


w:只写模式,当文件不存在时会创建空文件,当文件存在会清空文件,指针位于开始位置
# 强调1
# 在以w模式打开文件没有关闭的情况下,连续写入,新的内容总是跟在旧的之后
with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.write('擦勒1\n')
    f.write('擦勒2\n')
    f.write('擦勒3\n')

# 强调2
# 如果重新以w模式打开文件,则会清空文件内容
with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.write('擦勒1\n')
with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.write('擦勒2\n')
with open('d.txt',mode='wt',encoding='utf-8') as f:
    f.write('擦勒3\n')

a:只追加写,在文件不存在时会创建空文档,在文件存在时文件指针会直接调到末尾
x, 只写模式【不可读;不存在则创建,存在则报错】
with open('d.txt', mode='x', encoding='utf-8') as f:
    f.write('哈哈哈\n')

b:binary模式
    1、读写都是以bytes为单位
    2、可以针对所有文件
    3、一定不能指定字符编码,即一定不能指定encoding参数

总结:
1、在操作纯文本文件方面t模式帮我们省去了编码与解码的环节,b模式则需要手动编码与解码,所以此时t模式更为方便
2、针对非文本文件(如图片、视频、音频等)只能使用b模式
# 循环读取文件
forwhile
更推荐while模式循环,因为自己控制每次读取的数据的数据量
with open(r'test.jpg',mode='rb') as f:
    while True:
        res=f.read(1024) # 1024
        if len(res) == 0:
            break
        print(len(res))

with open(r'g.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        print(len(line),line,end="")
文件高级操作
一:读相关操作
# 1、readline:一次读一行
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
    res1=f.readline()
    res2=f.readline()
    print(res2)

    while True:
        line=f.readline()
        if len(line) == 0:
            break
        print(line)

# 2、readlines:
with open(r'g.txt',mode='rt',encoding='utf-8') as f:
    res=f.readlines()
    print(res)

# 强调:
# f.read()与f.readlines()都是将内容一次性读入内存,如果内容过大会导致内存溢出,若还想将内容全读入内存
一:写相关操作
# f.writelines():                        一次性把列表中的全写入
with open('h.txt',mode='wt',encoding='utf-8') as f:
    # f.write('1111\n222\n3333\n')

    # l=['11111\n','2222','3333',4444]
   l=['11111\n','2222','3333']
    # for line in l:
    #     f.write(line)
    f.writelines(l)

# 补充1:如果是纯英文字符,可以直接加前缀b得到bytes类型
    l = [
        b'1111aaa1\n',
        b'222bb2',
        b'33eee33'
    ]
# 补充2:'上'.encode('utf-8') 等同于bytes('上',encoding='utf-8')
    l = [
        bytes('上啊',encoding='utf-8'),
        bytes('冲呀',encoding='utf-8'),
        bytes('小垃圾们',encoding='utf-8'),
    ]
    f.writelines(l)
# 3、flush:              立即把当前内存中的内容强制写入硬盘!!!
with open('h.txt', mode='wt',encoding='utf-8') as f:
    f.write('哈')
    # f.flush()
# 4、了解              
with open('h.txt', mode='wt', encoding='utf-8') as f:
    print(f.readable())               #是否可读
    print(f.writable())               #是否可写
    print(f.encoding)                 #编码
    print(f.name)                     #名字
文件指针移动
# 指针移动的单位都是以bytes/字节为单位
# 只有一种情况特殊:
#       t模式下的read(n),n代表的是字符个数
#f.seek(n,模式):n指的是移动的字节个数
模式:
#模式0:参照物是文件开头位置
f.seek(9,0)
f.seek(3,0) # 3

#模式1:参照物是当前指针所在位置
f.seek(9,1)
f.seek(3,1) # 12

#模式2:参照物是文件末尾位置,应该倒着移动
f.seek(-9,2) # 3
f.seek(-3,2) # 9

# 强调:只有0模式可以在t下使用,1、2必须在b模式下用
# f.tell() # 获取文件指针当前位置
文件修改两种方式
# 方式一:文本编辑采用的就是这种方式
# 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改过程中同一份数据只有一份
# 缺点: 会过多地占用内存
with open('c.txt',mode='rt',encoding='utf-8') as f:
    res=f.read()
    data=res.replace('alex','dsb')
    print(data)

with open('c.txt',mode='wt',encoding='utf-8') as f1:
    f1.write(data)
# 方式二:
import os
# 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
with open('c.txt', mode='rt', encoding='utf-8') as f, \
        open('.c.txt.swap', mode='wt', encoding='utf-8') as f1:
    for line in f:
        f1.write(line.replace('alex', 'dsb'))

os.remove('c.txt')
os.rename('.c.txt.swap', 'c.txt')

函数

先定义
            三种定义方式
后调用
            三种调用方式
返回值
            三种返回值的形式
#    先定义
形式一:无参函数
形式二:有参函数
形式三:空函数,函数体代码为pass            常用在构思项目时暂时替代
def func(x, y):
    pass
#    后调用
1、语句的形式:只加括号调用函数 
2、表达式形式
3、函数调用可以当做参数  
# 三、函数返回值
1、返回None:函数体内没有return
return
return None
2、返回一个值:return3、返回多个值:用逗号分隔开多个值,会被return返回成元组  
def func():
    return 10, 'aa', [1, 2]
res = func()
print(res, type(res))
形参和实参
# 形参与实参的关系:
# 1、在调用阶段,实参(变量值)会绑定给形参(变量名)
# 2、这种绑定关系只能在函数体内使用
# 3、实参与形参的绑定关系在函数调用时生效,函数调用结束后解除绑定关系
# 形式一:
func(1,2)
# 形式二:
a=1
b=2
func(a,b)
# 形式三:
func(int('1'),2)
func(func1(1,2,),func2(2,3),333)
位置参数(形参与实参的具体使用)
位置形参
位置实参
关键字参数
在函数调用阶段,按照key=value的形式传入的值,特点:指名道姓给某个形参传值,可以完全不参照顺序
特点:指名道姓给某个形参传值,可以完全不参照顺序
默认参数
在定义函数阶段,就已经被赋值的形参,称之为默认参数
def func(x,y=3):
    print(x,y)
可变长度
# 可变长度的参数(*与**的用法)

I:*形参名:用来接收溢出的位置实参,溢出的位置实参会被*保存成元组的格式然后赋值紧跟其后的形参名
*后跟的可以是任意名字,但是约定俗成应该是args

II: **可以用在实参中(**后跟的只能是字典),实参中带**,先**后的值打散成关键字实参
def func(x,y,z):
    print(x,y,z)

混用*与**:*args必须在**kwargs之前
# 混用*与**:*args必须在**kwargs之前
def func(x,*args,**kwargs):
    print(args)
    print(kwargs)

func(1,2,3,4,5,6,7,8,x=1,y=2,z=3)
命名关键字实参
# 命名关键字实参必须按照关键字传参
def func(x,y,*,a,b):        #  *后定义的参数是命名关键字参数
  print(x,y)
  print(z,b)
func(1,2,b=222,a=111)
# 当不给a,b以关键字时,是无法传参的
组合使用
形参混用的顺序:位置新参,默认形参,*args,命名关键字形参,**kwargs

名称空间和作用域

内置名称空间
# 存放的名字:存放的python解释器内置的名字
# 存活周期:python解释器启动则产生,python解释器关闭则销毁

全局名称空间
# 存放的名字:只要不是函数内定义、也不是内置的,剩下的都是全局名称空间的名字
# 存活周期:python文件执行则产生,python文件运行完毕后销毁

局部名称空间
# 存放的名字:在调用函数时,运行函数体代码过程中产生的函数内的名字
# 存活周期:在调用函数时存活,函数调用完毕后则销毁

加载顺序:内置名称空间>全局名称空间>局部名称空间
名称空间的"嵌套"关系是以函数定义阶段为准,与调用位置无关

global    声明这个是全局名字,不需要再造新的名字
x = 10
def f1():
  global x
  x = 20

nonlocal    修改函数外层函数包含的名字对应的值(不可变类型)
x = 10 
def f1():
  x = 11
  def f2():
    global x
    x = 22
   f2()
  print(x)
f1()
函数对象and函数嵌套
精髓:可以把函数当成变量去用
func=内存地址

函数的嵌套调用:在调用一个函数的过程中又调用其他函数
闭包函数
闭包函数=名称空间与作用域+函数嵌套+函数对象
核心点:名字的查找关系是以函数定义阶段为准

# "闭"函数指的该函数是内嵌函数
# "包"函数指的该函数包含对外层函数作用域名字的引用(不是对全局作用域)

# 闭包函数:函数对象
def f1():
    x = 33333333333333333333
    def f2():
        print('函数f2:',x)
    return f2

f=f1()
print(f)
两种为函数体传参的方式
方式一:直接把函数体需要的参数定义成形参
def f2(x):
    print(x)

f2(1)
f2(2)
f2(3)

方式二:
def f1(x): # x=3
    x=3
    def f2():
        print(x)
    return f2

x=f1(3)
print(x)

b9D98U.png

装饰器(无参)

什么是装饰器:装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
为何要用装饰器:开放封闭原则
        开放:指的是对拓展功能是开放的
        封闭:指的是对修改源代码是封闭的
装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能
import time

def index(name,age,power):
    time.sleep(2)
    print("my name is %s,and my age is %s,my superpower is %s" %(name,age,power))
    return 123456

def outter(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        msg = func(*args,**kwargs)
        stop = time.time()
        print(stop - start)
        return msg
    return wrapper

index = outter(index)    #  这里outter(index)中的index是指调用的函数名字,返回值是wrapper闭包函数

res = index('batmanfuture','11','123456')    #  这里引用闭包函数传入的值就是我们要调的函数的值

print("返回值res = %s" %res)

# 这里需要满足开放和封闭的原则
语法糖
# 在被装饰对象正上方的单独一行写@装饰器名字
def timmer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        msg = func(*args,**kwargs)
        stop = time.time()
        print(stop - start)
        return msg
    return wrapper

@timmer # index=timmer(index)
def index(x,y,z):
    time.sleep(3)
    print('index %s %s %s' %(x,y,z))

@timmer # home=timmer(ome)
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)


index(x=1,y=2,z=3)
home('egon')

装饰器(有参)

    # 1、函数wrapper.__name__ = 原函数.__name__
    # 2、函数wrapper.__doc__ = 原函数.__doc__

from functools import wraps        #    即将原函数名指向的内存地址偷梁换柱成wrapper函数,所以应该将wrapper做的跟原函数一样才行

def outter(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """这个是主页功能"""
        res = func(*args, **kwargs) # res=index(1,2)
        return res

    # 手动将原函数的属性赋值给wrapper函数
    # 1、函数wrapper.__name__ = 原函数.__name__
    # 2、函数wrapper.__doc__ = 原函数.__doc__
    # wrapper.__name__ = func.__name__
    # wrapper.__doc__ = func.__doc__

    return wrapper
由于语法糖@的限制,outter函数只能有一个参数,并且该才是只用来接收被装饰对象的内存地址
def auth(db_type):
    def deco(func):
        def wrapper(*args, **kwargs):
            name = input('your name>>>: ').strip()
            pwd = input('your password>>>: ').strip()

            if db_type == 'file':
                print('基于文件的验证')
                if name == 'egon' and pwd == '123':
                    res = func(*args, **kwargs)  # index(1,2)
                    return res
                else:
                    print('user or password error')
            elif db_type == 'mysql':
                print('基于mysql的验证')
            elif db_type == 'ldap':
                print('基于ldap的验证')
            else:
                print('不支持该db_type')
        return wrapper
    return deco


@auth(db_type='file')  # @deco # index=deco(index) # index=wrapper
def index(x, y):
    print('index->>%s:%s' % (x, y))

@auth(db_type='mysql')  # @deco # home=deco(home) # home=wrapper
def home(name):
    print('home->>%s' % name)


@auth(db_type='ldap')  # 账号密码的来源是ldap
def transfer():
    print('transfer')

有参装饰器,在本来的两层之外又多一层用来传递变量,利用语法糖

迭代器

迭代器指的是迭代取值的工具,迭代是一个重复的过程,每次重复
    都是基于上一次的结果而继续的,单纯的重复并不是迭代

迭代器是用来迭代取值的工具,而涉及到把多个值循环取出来的类型
    有:列表、字符串、元组、字典、集合、打开文件
迭代器对象有:文件类型

可迭代的对象:但凡内置有__iter__方法的都称之为可迭代的对象
d={'a':1,'b':2,'c':3}
d_iterator=d.__iter__()
print(d_iterator)

print(d_iterator.__next__())        # 内置有__next__方法并且内置有__iter__方法的对象是迭代器对象
print(d_iterator.__next__())
print(d_iterator.__next__())
print(d_iterator.__next__()) # 抛出异常StopIteration

while True:
    try:
        print(d_iterator.__next__())
    except StopIteration:    # 抛出异常StopIteration
        break
# 在一个迭代器取值取干净的情况下,再对其取值取不到

3.2 迭代器对象:内置有__next__方法并且内置有__iter__方法的对象
       迭代器对象.__next__():得到迭代器的下一个值
       迭代器对象.__iter__():得到迭代器的本身,说白了调了跟没调一个样子
for循环本质
d={'a':1,'b':2,'c':3}

#    1、d.__iter__()得到一个迭代器对象
#    2、迭代器对象.__next__()拿到一个返回值,然后将该返回值赋值给k
#    3、循环往复步骤2,直到抛出StopIteration异常for循环会捕捉异常然后结束循环
for k in d:
    print(k)

生成器-自定义的迭代器

如何得到自定义的迭代器:
# 在函数内一旦存在yield关键字,调用函数并不会执行函数体代码
# 会返回一个生成器对象,生成器即自定义的迭代器

# 会触发函数体代码的运行,然后遇到yield停下来,将yield后的值
# 当做本次调用的结果返回
叠加多个装饰器分析
# 加载顺序自下而上(了解)
@deco1      # index=deco1(wrapper2的内存地址)        ===> index=wrapper1的内存地址
@deco2      # index=deco2(wrapper3的内存地址)        ===> index=wrapper2的内存地址
@deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
def index(x,y):
    print('from index %s:%s' %(x,y))

# 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
index(1,2) # wrapper1(1,2)
yield
def dog(name):
    print('道哥%s准备吃东西啦...' %name)
    while True:
        # x拿到的是yield接收到的值
        x = yield # x = '肉包子'
        print('道哥%s吃了 %s' %(name,x))


g=dog('alex')
g.send(None) # 等同于next(g)
def dog(name):
    print('道哥%s准备吃东西啦...' %name)
    while True:
        # x拿到的是yield接收到的值
        x = yield # x = '肉包子'
        print('道哥%s吃了 %s' %(name,x))


g=dog('alex')
g.send(None) # 等同于next(g)

g.send(['一根骨头','aaa'])
# g.send('肉包子')
# g.send('一同泔水')
# g.close()
# g.send('1111') # 关闭之后无法传值
三元表达式
def func(x,y):
    if x > y:
        return x
    else:
        return y

res=func(1,2)
print(res)
----------------------
res=x if x > y else y
print(res)
列表生成式
l = ['alex_dsb', 'lxx_dsb', 'wxx_dsb', "xxq_dsb", 'egon']
new_l=[]
for name in l:
    if name.endswith('dsb'):
        new_l.append(name)
-------------------------------------
l = ['alex_dsb', 'lxx_dsb', 'wxx_dsb', "xxq_dsb", 'egon']
new_l=[name for name in l if name.endswith('dsb')]
函数的递归调用

函数的递归调用:是函数嵌套调用的一种特殊形式,在调用一个函数的过程中又直接或者间接地调用到本身

死循环不会无限申请内存

import sys        # 查看最大可递归调用的次数
sys.getrecursionlimit()

import sys        # 设置最多可递归的次数为2000
sys.getrecursionlimit(2000)    

递归调用不应该无限地调用下去,必须在满足某种条件下结束递归调用

# 回溯:一层一层调用下去
# 递推:满足某种结束条件,结束递归调用,然后一层一层返回
二分法

becYIP.png

匿名函数
#    调用匿名函数
res=(lambda x,y:x+y)(1,2)
print(res)
匿名用于临时调用一次的场景:更多的是将匿名与其他函数配合使用

res=max(salaries,key=lambda k:salaries[k])
print(res)

res=min(salaries,key=lambda k:salaries[k])
print(res)
sorted
salaries={
    'siry':3000,
    'tom':7000,
    'lili':10000,
    'jack':2000
}
res=sorted(salaries,key=lambda k:salaries[k],reverse=True)
print(res)
map
l=['alex','lxx','wxx','薛贤妻']
new_l=(name+'_dsb' for name in l)
print(new_l)

res=map(lambda name:name+'_dsb',l)
print(res) # 生成器
filter
#    filter会留下结果为True的
l=['alex_sb','lxx_sb','wxx','薛贤妻']
res=(name for name in l if name.endswith('sb'))
print(res)

res=filter(lambda name:name.endswith('sb'),l)
print(res)
reduce
from functools import reduce
res=reduce(lambda x,y:x+y,[1,2,3],10) # 16
print(res)

res=reduce(lambda x,y:x+y,['a','b','c']) # 'a','b'
print(res)
模块和包
一个python文件本身就一个模块,文件名m.py,模块名叫m,模块有四种形式:
1 使用python编写的.py文件
2 已被编译为共享库或DLL的C或C++扩展
3 把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
4 使用C编写并链接到python解释器的内置模块

I:内置与第三的模块拿来就用,无需定义,这种拿来主义,可以极大地提升自己的开发效率
II:自定义的模块
    可以将程序的各部分功能提取出来放到一模块中为大家共享使用
    好处是减少了代码冗余,程序组织结构更加清晰

# 一个python文件有两种用途
# 1、被当成程序运行
# 2、被当做模块导入

#1、产生一个名称空间
#2、运行包下的__init__.py文件,将运行过程中产生的名字都丢到1的名称空间中
#3、在当前执行文件的名称空间中拿到一个名字mmm,mmm指向1的名称空间

在导入时带.,左边必须是包
from foo.m1 import f1

绝对导入和相对导入
绝对导入前,可以把模块的路径添加到内存环境变量中
相对导入:
from .m1 import f1    #    这里.m1表示在当前路径下找m1
from ..m2 import f1    #    这里..m2表示在上一级目录下找m2

包内之间互相导入推荐相对导入
name
当文件运行时__name__ == '__main__'
当文件被当成模块时,__name__ == 模块名

if __name__ === '__main__':
      print('文件被执行')
    get()
    change()
else:
  print("文件被导入")
  pass
from ... import ...
# import导入模块在使用时必须加前缀"模块."
# 优点:肯定不会与当前名称空间中的名字冲突
# 缺点:加前缀显得麻烦

# from ... import ...导入也发生了三件事
# 1、产一个模块的名称空间
# 2、运行foo.py将运行过程中产生的名字都丢到模块的名称空间去
# 3、在当前名称空间拿到一个名字,该名字与模块名称空间中的某一个内存地址

多次导入,全部导入,起别名
from socket import foo,pass,test
from socket import *
from socket import xxx as f

被导入模块内部存在一个__all__的列表,列表内部的值是模块内部的名字
__all__ = ['x','get','change']
优先级
# 无论是import还是from...import在导入模块时都涉及到查找问题
# 优先级:
# 1、内存(内置模块)
# 2、硬盘:按照sys.path中存放的文件的顺序依次查找要导入的模块

import sys
#值为一个列表,存放了一系列的对文件夹
#其中第一个文件夹是当前执行文件所在的文件夹
print(sys.path)

#    sys.modules查看已经加载到内存中的模块
import sys
import foo # foo=模块的内存地址
print('foo' in sys.modules)
print(sys.modules)

# sys.path应用
#    找foo.py就把foo.py的文件夹添加到环境变量中
import sys
sys.path.append(r"/users/linhaifeng/PycharmProjects/aa")
编程语言的三种划分方式:
编译型和解释型

主要区别在于,前者源程序编译后即可在该平台运行,后者是在运行期间才编译。所以前者运行速度快,后者跨平台性好

--------编译型总结

1.一次性的编译成平台相关的机器语言文件,运行时脱离开发环境,运行效率高;

2.与特定平台相关,一般无法移植到其他平台;

3.现有的C、C++、Objective等都属于编译型语言。

--------解释型总结

1.解释型语言每次运行都需要将源代码解释称机器码并执行,效率较低;

2.只要平台提供相应的解释器,就可以运行源代码,所以可以方便源程序移植;

3.Python等属于解释型语言。

强类型和弱类型

强类型的数据类型不可以被忽视,一旦被定义不会改变除非强行转换。弱类型的数据类型可以被忽视,比如php的弱类型比较

动态型和静态型

动态型在运行时才进行数据类型检查;静态型事先给变量进行数据类型的定义


python是解释型强类型动态型

函数的类型提示
#    :后门跟的是提示信息

def register(name:str,age:int,hobbbies:tuple)->int:    #    返回的是整型
   print(name)
   print(age)
   print(hobbbies)
   return 111

软件开发目录规范

conf    配置文件
bin        可执行文件
db        数据库相关操作
core    核心逻辑
api        api接口
lib        存放自定义模块
README.md    解释说明
time模块
time.sleep(3)
# 时间分为三种格式:
# 1、时间戳:从1970年到现在经过的秒数
#    作用:用于时间间隔的计算
print(time.time())

# 2、按照某种格式显示的时间:2020-03-30 11:11:11
#    作用:用于展示时间

print(time.strftime('%Y-%m-%d %H:%M:%S %p'))
print(time.strftime('%Y-%m-%d %X'))

# 3、结构化的时间
#    作用:用于单独获取时间的某一部分

res=time.localtime()
print(res)
print(res.tm_year)
print(res.tm_yday)
datatime模块
#    一步到位得到显示出的格式化时间
import datetime

print(datetime.datetime.now())
print(datetime.datetime.now() + datetime.timedelta(days=3))
print(datetime.datetime.now() + datetime.timedelta(weeks=1))

import time
s_time=time.localtime()
print(time.mktime(s_time))

tp_time=time.time()
print(time.localtime(tp_time))

print(time.localtime())
print(time.gmtime()) # 世界标准时间,了解
print(time.localtime(333333333))
print(time.gmtime(333333333))

s_time=time.localtime()
print(time.strftime('%Y-%m-%d %H:%M:%S',s_time))
random模块
import random
print(random.random())#(0,1)----float    大于0且小于1之间的小数
print(random.randint(1,3))  #[1,3]    大于等于1且小于等于3之间的整数
print(random.randrange(1,3)) #[1,3)    大于等于1且小于3之间的整数
print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716 

item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
print(item)

------------------
随机验证码
import random
def make_code(n):
    res=''
    for i in range(n):
        s1=chr(random.randint(65,90))
        s2=str(random.randint(0,9))
        res+=random.choice([s1,s2])
    return res

print(make_code(9))
glob模块
os模块
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: ('.')
os.pardir  获取当前目录的父目录字符串名:('..')
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.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command")  运行shell命令,直接显示
os.environ  获取系统环境变量
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的大小
sys模块

针对python解释器相关变量和方法

# 版本号
print(sys.version)
# 最大表示int数值
print(sys.maxsize)
# 查看python代码模块
print(sys.path)
# 查看平台
print(sys.platform)
# 查看版权
print(sys.copyright)
# 查看参数
print(sys.argv)
# 退出码
print(sys.exit(1))
# 查看默认编码
print(sys.getdefaultencoding())
# 查看文件编码
print(sys.getfilesystemencoding())
# 最大递归次数
print(sys.getrecursionlimit())
sys.argv           #命令行参数List,第一个元素是程序本身路径
sys.exit(n)        #退出程序,正常退出时exit(0)
sys.version        #获取Python解释程序的版本信息
sys.maxint       #  最大的Int值
sys.path           #返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       #返回操作系统平台名称
sys.stdin          #输入相关
sys.stdout         #输出相关
sys.stderror       #错误相关
base64模块

常见于ctf题目或者爬虫中url中base64的转换

# 想将字符串转编码成base64,要先将字符串转换成二进制数据
import base64
url = "https://www.cnblogs.com/songzhixue/"
bytes_url = url.encode("utf-8")
str_url = base64.b64encode(bytes_url)  # 被编码的参数必须是二进制数据
print(bytes_url)

# b'aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vc29uZ3poaXh1ZS8='
-------------------------------------------------------------------------------------------------
# 将base64解码成字符串
import base64
url = "aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vc29uZ3poaXh1ZS8="
str_url = base64.b64decode(url).decode("utf-8")
print(str_url)

# 'https://www.cnblogs.com/songzhixue/'
打印进度条
'[%-50s]'    # 这里%s是替代的字符,比如■或者#,- 是左对齐

import time

for i in range(1,51):
    time.sleep(0.2)
    msg = '\r[%-50s]' % ('■' * i)
    print(msg,end='')
#    这里\r是每次打印都跳到行首
------------------------------------------------------------------------
import time


def progress(percent):
    if percent > 1:
        percent = 1
    res = int(50 * percent) * '■'
    print('\r[%-50s] %d%%' % (res, int(100 * percent)), end='')

recv_size=0
total_size=1025011

while recv_size < total_size:
    time.sleep(0.001) # 下载了1024个字节的数据

    recv_size+=1024 # recv_size=2048

    # 打印进度条
    # print(recv_size)
    percent = recv_size / total_size  # 1024 / 333333
    progress(percent)
shutil模块
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中

1 import shutil
2  
3 shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))


shutil.copyfile(src, dst)
拷贝文件

1 shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在
shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变

1 shutil.copymode('f1.log', 'f2.log') #目标文件必须存在


shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags

1 shutil.copystat('f1.log', 'f2.log') #目标文件必须存在


shutil.copy(src, dst)
拷贝文件和权限

1 import shutil
2  
3 shutil.copy('f1.log', 'f2.log')


shutil.copy2(src, dst)
拷贝文件和状态信息

1 import shutil
2  
3 shutil.copy2('f1.log', 'f2.log')


shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹

1 import shutil
2  
3 shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除 
 拷贝软连接


shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件

1 import shutil
2  
3 shutil.rmtree('folder1')


shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。

1 import shutil
2  
3 shutil.move('folder1', 'folder3')


shutil.make_archive(base_name, format,...)

创建压缩包并返回文件路径,例如:zip、tar

创建压缩包并返回文件路径,例如:zip、tar

base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak                       =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象
1 #将 /data 下的文件打包放置当前程序目录
2 import shutil
3 ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')
4   
5   
6 #将 /data下的文件打包放置 /tmp/目录
7 import shutil
8 ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
pickle,json,shelve

见python的序列化和反序列化文章

configparser

配置文件如下

# 注释1
; 注释2

[section1]
k1 = v1
k2:v2
user=egon
age=18
is_admin=true
salary=31

[section2]
k1 = v1
读取
import configparser

config=configparser.ConfigParser()
config.read('a.cfg')

#查看所有的标题
res=config.sections() #['section1', 'section2']
print(res)

#查看标题section1下所有key=value的key
options=config.options('section1')
print(options) #['k1', 'k2', 'user', 'age', 'is_admin', 'salary']

#查看标题section1下所有key=value的(key,value)格式
item_list=config.items('section1')
print(item_list) #[('k1', 'v1'), ('k2', 'v2'), ('user', 'egon'), ('age', '18'), ('is_admin', 'true'), ('salary', '31')]

#查看标题section1下user的值=>字符串格式
val=config.get('section1','user')
print(val) #egon

#查看标题section1下age的值=>整数格式
val1=config.getint('section1','age')
print(val1) #18

#查看标题section1下is_admin的值=>布尔值格式
val2=config.getboolean('section1','is_admin')
print(val2) #True

#查看标题section1下salary的值=>浮点型格式
val3=config.getfloat('section1','salary')
print(val3) #31.0
改写
import configparser

config=configparser.ConfigParser()
config.read('a.cfg',encoding='utf-8')


#删除整个标题section2
config.remove_section('section2')

#删除标题section1下的某个k1和k2
config.remove_option('section1','k1')
config.remove_option('section1','k2')

#判断是否存在某个标题
print(config.has_section('section1'))

#判断标题section1下是否有user
print(config.has_option('section1',''))


#添加一个标题
config.add_section('egon')

#在标题egon下添加name=egon,age=18的配置
config.set('egon','name','egon')
config.set('egon','age',18) #报错,必须是字符串


#最后将修改的内容写入文件,完成最终的修改
config.write(open('a.cfg','w'))

hash

hash算法就像一座工厂,工厂接收你送来的原材料(可以用m.update()为工厂运送原材料),经过加工返回的产品就是hash值

bdWGb8.png

import hashlib
m=hashlib.md5()# m=hashlib.sha256()
m.update('hello'.encode('utf8'))
print(m.hexdigest())  #5d41402abc4b2a76b9719d911017c592
m.update('alvin'.encode('utf8'))
print(m.hexdigest())  #92a7e713c30abbb0319fa07da2a5c4af
m2=hashlib.md5()
m2.update('helloalvin'.encode('utf8'))
print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af
'''
注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样
但是update多次为校验大文件提供了可能。
'''
尝试撞库攻击
import hashlib

crypto_content = '25f9e794323b453885f5181f1b624d0b'

password = [
    '123456',
    '123',
    'admin',
    'admin123456',
    'admin123',
    '888888',
    '88888888',
    '123456789',
    'admin88888'
]

for i in password:
    a = hashlib.md5()
    a.update(i.encode('utf-8'))
    res = a.hexdigest()
    if res == crypto_content:
        print(i + "是原来的值")
---------------------------------------------------
123456789是原来的值
密码加盐salt

可以通过拼接的方式,把salt放在前,中,后都可以

import hashlib

m=hashlib.md5()

m.update('天王'.encode('utf-8'))
m.update('alex3714'.encode('utf-8'))
m.update('盖地虎'.encode('utf-8'))
print(m.hexdigest())
subprocess
import subprocess

res = subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
#    这里dir是你执行的shell命令,stdout=subprocess.PIPE是当执行成功后把他放在管道里,PIPE,stderr=subprocess.PIPE是执行失败时放在管道里
das = res.stdout.read()    #    让das读出执行成功管道里的值
das_1 = res.stderr.read()  #    让das_1读出执行失败管道里的值
print(das.decode('gbk'))    #    这里gbk代表是windows下的默认编码格式,如果是linux下那么是utf-8
base64
# 想将字符串转编码成base64,要先将字符串转换成二进制数据
import base64
url = "https://www.cnblogs.com/songzhixue/"
bytes_url = url.encode("utf-8")
str_url = base64.b64encode(bytes_url)  # 被编码的参数必须是二进制数据
print(bytes_url)

# b'aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vc29uZ3poaXh1ZS8='
-------------------------------------------------------------------------------------------------
# 将base64解码成字符串
import base64
url = "aHR0cHM6Ly93d3cuY25ibG9ncy5jb20vc29uZ3poaXh1ZS8="
str_url = base64.b64decode(url).decode("utf-8")
print(str_url)

# 'https://www.cnblogs.com/songzhixue/'

对admin888先进行md5加密再进行base64编码得到N2ZlZjYxNzE0NjllODBkMzJjMDU1OWY4OGIzNzcyNDU=

然后再对N2ZlZjYxNzE0NjllODBkMzJjMDU1OWY4OGIzNzcyNDU=进行base64解码,再撞库攻击得到admin888

import hashlib
import base64

source = "admin888"
a = hashlib.md5()
a.update(source.encode('utf-8'))
source_1 = a.hexdigest()
source_2 = source_1.encode('utf-8')
source_3 = base64.b64encode(source_2)
print(source_3)

#   N2ZlZjYxNzE0NjllODBkMzJjMDU1OWY4OGIzNzcyNDU=
import hashlib
import base64

source = "N2ZlZjYxNzE0NjllODBkMzJjMDU1OWY4OGIzNzcyNDU="

source_1 = source.encode('utf-8')

source_2 = base64.b64decode(source_1)
source_3 = source_2.decode('utf-8')

# print(source_3,type(source_3))
dict_1 = [
'admin',
'123456',
'8888888',
'a123456',
'888888',
'batmanfuture',
'12345678',
'654321',
'system',
'admin888',         #   对admin888先进行md5-->7fef6171469e80d32c0559f88b377245
'superman'                                  #7fef6171469e80d32c0559f88b377245
]
for i in dict_1:
    a = hashlib.md5()
    a.update(i.encode('utf-8'))
    b = a.hexdigest()

    b = str(b)
    if b in source_3:
        print("找到明文密码了,密码是" + i)
requests
几种爬虫引擎的主机头
#    百度
Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
#    谷歌
Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
#    搜狗
Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)
#    360
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36; 360Spider
模块介绍

bwsTGq.png

url="你想获取的网址"
r=requests.get(url)
#也可以使用r=requests.get("你想要获取的网址")
#现在我们有一个名叫r的Response对象。我们可以从这个对象中来获取我们想要的信息
#(可选)是可要可不要的意思
#下面的url params **kwards data json的意思
#url:网址
#param:参数(可选)字典,要在请求的查询字符串中发送的元组或字节的列表。
#**kwards:采用可选参数
#data:(可选) 字典、要在请求的正文中发送的元组、字节或文件类对象的列表。
#json:(可选) json 数据发送到请求的正文。
#requests.get(url, params=None, **kwargs)GET请求
#requests.post(url, data=None, json=None, **kwargs) POST请求
#requests.put(url, data=None, **kwargs) PUT请求
#requests.delete(url, **kwargs) DELETE请求
#requests.head(url, **kwargs)HEAD请求
#requests.options()OPTIONS请求
#requests.patch(url, data=None, **kwargs)PATCH请求
#都是获取网址然后使我们从中获取我们想要的信息
session()维持会话
import requests

s = requests.Session()
# 第一步:发送一个请求,用于设置请求中的cookies
# tips: http://httpbin.org能够用于测试http请求和响应
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
# 第二步:再发送一个请求,用于查看当前请求中的cookies
r = s.get("http://httpbin.org/cookies")
print(r.text)
#requests.session():维持会话,可以让我们在跨请求时保存某些参数
import requests
#实例化session
session = requests.session()
#目标url
url = 'https://www.douban.com/accounts/login'
form_data = {
    'source': 'index_nav',
    'form_email': 'xxx',
    'form_password': 'xxx',
    'captcha-solution': 'stamp',
    'captcha-id': 'b3dssX515MsmNaklBX8uh5Ab:en'
}
#设置请求头
req_header = {
    'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}
#使用session发起请求
response = session.post(url,headers=req_header,data=form_data)
if response.status_code == 200:
    #访问个人主页:
    url = 'https://www.douban.com/people/175417123/'
    response = session.get(url,headers = req_header)
    if response.status_code == 200:
        with open('douban3.html','w') as file:
            file.write(response.text)
re

bwjnts.png

import re

# findall:  匹配字符串中所有的符合正则的内容
lst = re.findall(r"\d+", "我的电话号是:10086, 我女朋友的电话是:10010")
print(lst)

# finditer: 匹配字符串中所有的内容[返回的是迭代器], 从迭代器中拿到内容需要.group()
it = re.finditer(r"\d+", "我的电话号是:10086, 我女朋友的电话是:10010")
for i in it:
    print(i.group())

# search, 找到一个结果就返回, 返回的结果是match对象. 拿数据需要.group()
s = re.search(r"\d+", "我的电话号是:10086, 我女朋友的电话是:10010")
print(s.group())


# match是从头开始匹配
s = re.match(r"\d+", "10086, 我女朋友的电话是:10010")
print(s.group())

# 预加载正则表达式
obj = re.compile(r"\d+")

ret = obj.finditer("我的电话号是:10086, 我女朋友的电话是:10010")
for it in ret:
    print(it.group())

ret = obj.findall("呵呵哒, 我就不信你不换我1000000000")
print(ret)

s = """
<div class='jay'><span id='1'>郭麒麟</span></div>
<div class='jj'><span id='2'>宋铁</span></div>
<div class='jolin'><span id='3'>大聪明</span></div>
<div class='sylar'><span id='4'>范思哲</span></div>
<div class='tory'><span id='5'>胡说八道</span></div>
"""

# (?P<分组名字>正则) 可以单独从正则匹配的内容中进一步提取内容
obj = re.compile(r"<div class='.*?'><span id='(?P<id>\d+)'>(?P<wahaha>.*?)</span></div>", re.S)  # re.S: 让.能匹配换行符

result = obj.finditer(s)
for it in result:
    print(it.group("wahaha"))
    print(it.group("id"))

类和对象

bBt4c4.png

bBtTBR.png

# 类是对象相似数据与功能的集合体
# 所以类体中最常见的是变量与函数的定义,但是类体其实是可以包含任意其他代码的
# 注意:类体代码是在类定义阶段就会立即执行,会产生类的名称空间

bBNF4f.png

class Student:
    # 1、变量的定义
    stu_school='oldboy'

    # 2、功能的定义
    def tell_stu_info(stu_obj):
        print('学生信息:名字:%s 年龄:%s 性别:%s' %(
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj,x,y,z):
        stu_obj['stu_name']=x
        stu_obj['stu_age']=y
        stu_obj['stu_gender']=z

# 属性访问的语法
# 1、访问数据属性
# print(Student.stu_school) # Student.__dict__['stu_school']
# 2、访问函数属性
# print(Student.set_info) # Student.__dict__['set_info']

stu1_obj = Student()

stu1_obj.__dict__['stu_name'] = 'batmanfuture'
stu1_obj.__dict__['stu_age'] = '20'
stu1_obj.__dict__['stu_gender'] = 'male'

print(stu1_obj.__dict__)
#   ----------------------------
stu1_obj.stu_name = 'batmanfuture'
stu1_obj.stu_age = '20'
stu1_obj.stu_gender = 'male'
print(stu1_obj.__dict__)
def init(obj,x,y,z):
    obj.stu_name = x
    obj.stu_age = y
    obj.stu_gender = z

init(stu1_obj,'batmanfuture',20,'male')
print(stu1_obj.__dict__)
#    当我们调用过程时,会产生实例化,类里面 
def __init__(obj,x,y,z):
    obj.stu_name = x
    obj.stu_age = y
    obj.stu_gender = z

stu1_obj = Student("batmanfuture",20,'male')

#    调用类的过程又称之为实例化,发生了三件事
# 1、先产生一个空对象
# 2、python会自动调用类中的__init__方法然将空对象已经调用类时括号内传入的参数一同传给__init__方法
# 3、返回初始完的对象

stu1_obj = Student("batmanfuture",20,'male') #    也就是说我们不必传入obj,调用时对方会自动把obj = stu1_obj,同理我们通常把这里的obj改成self
def __init__(self,x,y,z):
    self.stu_name = x
    self.stu_age = y
    self.stu_gender = z

# 总结__init__方法
# 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据
# 2、__init__内应该存放是为对象初始化属性的功能,但是是可以存放任意其他代码,想要在
#    类调用时就立刻执行的代码都可以放到该方法内
# 3、__init__方法必须返回None
类的属性以及绑定方法
类的数据属性是共享给所有对象用的,大家访问的地址都一样
类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
"""
@作者: egon老湿
@微信:18611453110
@专栏: https://zhuanlan.zhihu.com/c_1189883314197168128
"""


# 整合->解耦合->扩展性增强

class School:
    school_name = 'OLDBOY'

    def __init__(self, nickname, addr):
        self.nickname = nickname
        self.addr = addr
        self.classes = []

    def related_class(self, class_obj):
        # self.classes.append(班级名字)
        # self.classes.append(class_name)
        self.classes.append(class_obj)

    def tell_class(self): # 改
        # 打印的班级的名字
        print(self.nickname.center(60,'='))
        # 打印班级开设的课程信息
        for class_obj in self.classes:
            class_obj.tell_course()


# # 一:学校
# #1、创建校区
school_obj1=School('老男孩魔都校区','上海')
school_obj2=School('老男孩帝都校区','北京')
#
# #2、为学校开设班级
# # 上海校区开了:脱产14期,上海校区开了脱产15期
# school_obj1.related_class("脱产14期")
# school_obj1.related_class("脱产15期")
#
# # 北京校区开了:脱产29期
# school_obj2.related_class("脱产29期")
#
# #3、查看每个校区开设的班级
# school_obj1.tell_class()
# school_obj2.tell_class()


class Class:
    def __init__(self, name):
        self.name = name
        self.course = None

    def related_course(self, course_obj):
        # self.course = course_name
        self.course = course_obj

    def tell_course(self):
        print('%s' % self.name,end=" ")
        self.course.tell_info() # 打印课程的详细信息

# 二:班级
# 1、创建班级
class_obj1 = Class('脱产14期')
class_obj2 = Class('脱产15期')
class_obj3 = Class('脱产29期')

# 2、为班级关联一个课程
# class_obj1.related_course('python全栈开发')
# class_obj2.related_course('linux运维')
# class_obj3.related_course('python全栈开发')

# 3、查看班级开设的课程信息
# class_obj1.tell_course()
# class_obj2.tell_course()
# class_obj3.tell_course()

# 4、为学校开设班级
# 上海校区开了:脱产14期,上海校区开了脱产15期
school_obj1.related_class(class_obj1)
school_obj1.related_class(class_obj2)

# 北京校区开了:脱产29期
school_obj2.related_class(class_obj3)


# school_obj1.tell_class()
# school_obj2.tell_class()



class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price

    def tell_info(self):
        print('<课程名:%s 周期:%s 价钱:%s>' %(self.name,self.period,self.price))
# 三:课程
# 1、创建课程
course_obj1=Course('python全栈开发','6mons',20000)
course_obj2=Course('linux运维','5mons',18000)

# 2、查看课程的详细信息
# course_obj1.tell_info()
# course_obj2.tell_info()

# 3、为班级关联课程对象
class_obj1.related_course(course_obj1)
class_obj2.related_course(course_obj2)
class_obj3.related_course(course_obj1)

# class_obj1.tell_course()
# class_obj2.tell_course()
# class_obj3.tell_course()

school_obj1.tell_class()
school_obj2.tell_class()


class Student:
    pass

封装

# 封装是面向对象三大特性最核心的一个特性
# 封装<->整合

# 1、如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果
隐藏属性
class Foo: 
      __x = 1  # _Foo__x
    def __f1(self):  # _Foo__f1
        print('from test')
# I:在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如Foo._A__N,
# 所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形
II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形

class Foo:
    __x = 1  # _Foo__x = 1

    def __f1(self):  # _Foo__f1
        print('from test')

    def f2(self):
        print(self.__x) # print(self._Foo__x)
        print(self.__f1) # print(self._Foo__f1)

print(Foo.__x)
print(Foo.__f1)
obj=Foo()
obj.f2()
# III: 这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形
class Foo:
    __x = 1  # _Foo__x = 1

    def __f1(self):  # _Foo__f1
        print('from test')

    def f2(self):
        print(self.__x) # print(self._Foo__x)
        print(self.__f1) # print(self._Foo__f1)

Foo.__y=3
print(Foo.__dict__)
print(Foo.__y)
# 2、为何要隐藏?
# I、隐藏数据属性"将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,接口之上可以附加额外的逻辑来对数据的操作进行严格地控制:
property
# 装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加
# 新功能的可调用对象
# print(property)

# property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性

class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    # 定义函数的原因1:
    # 1、从bmi的公式上看,bmi应该是触发功能计算得到的
    # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值
    #    说白了,每次都是需要临时计算得到的

    # 但是bmi听起来更像是一个数据属性,而非功能
    @property
    def bmi(self):
        return self.weight / (self.height ** 2)

继承

# 1、什么是继承
# I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
# II:需要注意的是:python支持多继承
#          在Python中,新建的类可以继承一个或多个父类

class Parent1(object):
    x=1111

class Parent2(object):
    pass

class Sub1(Parent1): # 单继承
    pass

class Sub2(Parent1,Parent2): # 多继承
    pass

print(Sub1.__bases__)
print(Sub2.__bases__)

print(Sub1.x)
新式类和经典类
# ps1: 在python2中有经典类与新式类之分
# 新式类:继承了object类的子类,以及该子类的子类子子类。。。
# 经典类:没有继承object类的子类,以及该子类的子类子子类。。。

# ps2:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类
# print(Parent1.__bases__)
# print(Parent2.__bases__)

# III:python的多继承
#     优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
#     缺点:
#         1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
#         2、代码可读性会变差
#         3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
#         如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins

# 2、为何要用继承:用来解决类与类之间代码冗余问题

# 3、如何实现继承
# # 示范1:类与类之间存在冗余问题

class OldboyPeople:
    school = 'OLDBOY'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class Student(OldboyPeople):
    def choose_course(self):
        print('学生%s 正在选课' % self.name)
# stu_obj = Student('lili', 18, 'female')
# print(stu_obj.__dict__)
# print(stu_obj.school)
# stu_obj.choose_course()


class Teacher(OldboyPeople):
    #           老师的空对象,'egon',18,'male',3000,10
    def __init__(self, name, age, sex, salary, level):
        # 指名道姓地跟父类OldboyPeople去要__init__
        OldboyPeople.__init__(self,name,age, sex)
        self.salary = salary
        self.level = level

    def score(self):
        print('老师 %s 正在给学生打分' % self.name)

tea_obj=Teacher('egon',18,'male',3000,10)
# print(tea_obj.__dict__)
# print(tea_obj.school)

tea_obj.score()

bR8qIJ.png

单继承背景下的属性查找
class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1() # obj.f1()

class Bar(Foo):
    def f1(self):
        print('Bar.f1')

obj=Bar()
obj.f2()
---------------------------------------
class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        Foo.f1(self) # 调用当前类中的f1

class Bar(Foo):
    def f1(self):
        print('Bar.f1')

obj=Bar()
obj.f2()
-----------------------------------------
class Foo:
    def __f1(self): # _Foo__f1
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1() # self._Foo__f1,# 调用当前类中的f1

class Bar(Foo):
    def __f1(self): # _Bar__f1
        print('Bar.f1')

obj=Bar()
obj.f2()

菱形问题-MRO

bWgqUJ.png

对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下

>>> D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则

1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
class A(object):
    # def test(self):
    #     print('from A')
    pass

class B(A):
    def test(self):
        print('from B')
    pass

class C(A):
    # def test(self):
    #     print('from C')
    pass

class D(C,B):
    # def test(self):
    #     print('from D')
    pass

print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表

# print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
# c=C()
# c.test()

# 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro

# 二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
#    都是一个分支一个分支地找下去,然后最后找object

class E:
    # def test(self):
    #     print('from E')
    pass

class F:
    def test(self):
        print('from F')


class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D:
    def test(self):
        print('from D')


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass

# 新式类
# print(A.mro()) # A->B->E->C->F->D->object

obj = A()
obj.test() # 结果为:from F

如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

bfrz90.png

# 三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
#    经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
#    新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

class G: # 在python2中,未继承object的类及其子类,都是经典类
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class F(G):
    def test(self):
        print('from F')

class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

# 新式类
# print(A.mro()) # A->B->E->C->F->D->G->object

# 经典类:A->B->E->G->C->F->D
obj = A()
obj.test() #

经典类没有object这一个东西
Mixins机制
# mixins机制核心:就是在多继承背景下尽可能地提升多继承的可读性
Mixins用来为当前类混入一些功能

回到主题,Python语言可没有接口功能,但Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系

class Vehicle:  # 交通工具
    pass


class FlyableMixin:
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(FlyableMixin, Vehicle):  # 民航飞机
    pass


class Helicopter(FlyableMixin, Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车
    pass

# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
在子类派生的新方法中重用父类功能
# 在子类派生的新方法中如何重用父类的功能
# 方式一:指名道姓调用某一个类下的函数=》不依赖于继承关系
class OldboyPeople:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

    def f1(self):
        print('%s say hello' %self.name)


class Teacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        OldboyPeople.__init__(self,name,age,sex)

        self.level = level
        self.salary=salary

tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)

# 方式二:super()调用父类提供给自己的方法=》严格依赖继承关系
# 调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性
class OldboyPeople:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

    def f1(self):
        print('%s say hello' %self.name)


class Teacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        # super(Teacher,self).__init__(name,age,sex)
        super().__init__(name,age,sex) # 调用的是方法,自动传入对象

        self.level = level
        self.salary=salary

# print(Teacher.mro())
tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)
class A:
    def test(self):
        print('from A')
        super().test1()

class B:
    def test(self):
        print('from B')

class C(A,B):
    def test1(self):
        print('from C')

obj=C()
obj.test()

print(C.mro())
多态
# 1、什么多态:同一事物有多种形态
class Animal:
    pass

class People(Animal):
    pass

class Dog(Animal):
    pass

class Pig(Animal):
    pass

# 2、为何要有多态=》多态会带来什么样的特性,多态性
#    多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象

print('hello'.__len__())
print([1,2,3].__len__())
print({'a':1,'b':2}.__len__())

def my_len(val):
    return val.__len__()

print(my_len('hello'))
print(my_len([1,2,3]))
print(my_len({'a':1,'b':2}))
鸭子类型
class Cpu:
    def read(self):
        print('cpu read')

    def write(self):
        print('cpu write')

class Mem:
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')


class Txt:
    def read(self):
        print('txt read')

    def write(self):
        print('txt write')


obj1=Cpu()
obj2=Mem()
obj3=Txt()

obj1.read()
obj1.write()

obj2.read()
obj2.write()

obj3.read()
obj3.write()
绑定方法与非绑定方法

@classmethod和@staticmethod

# 一:绑定方法:特殊之处在于将调用者本身当做第一个参数自动传入
#    1、绑定给对象的方法:调用者是对象,自动传入的是对象
#    2、绑定给类的方法:调用者类,自动传入的是类

import settings

class Mysql:
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port

    def func(self):
        print('%s:%s' %(self.ip,self.port))

    @classmethod # 将下面的函数装饰成绑定给类的方法
    def from_conf(cls):
        print(cls)
        return cls(settings.IP, settings.PORT)

# obj1=Mysql('1.1.1.1',3306)

obj2=Mysql.from_conf()
print(obj2.__dict__)

# 二:非绑定方法-》静态方法:
#    没有绑定给任何人:调用者可以是类、对象,没有自动传参的效果

class Mysql:
    def __init__(self,ip,port):
        self.nid=self.create_id()
        self.ip=ip
        self.port=port

    @staticmethod # 将下述函数装饰成一个静态方法
    def create_id():
        import uuid
        return uuid.uuid4()

    @classmethod
    def f1(cls):
        pass

    def f2(self):
        pass
obj1=Mysql('1.1.1.1',3306)

print(Mysql.create_id)
print(Mysql.f1)
print(obj1.f2)
内置函数
#    取绝对值
print(abs(-1))
#    所有的值取出来做布尔运算,所有的值都为true结果才为true        如果可迭代对象为空返回true
print(all([1,'aaa','1']))                                        print(all([]))
#    所有的值取出来做布尔运算,所有的值都为false结果才为false    如果可迭代对象为空返回false
print(any([0,None,1]))                                            print(any([]))
#    进制转换:bin是二进制,oct是八进制,hex是十六进制
print(bin(11))
print(oct(11))
print(hex(11))
#    用来判断某一个对象,或者类,或者函数是否可以被调用
print(callable(Foo)) 
#    ord把字符转ascii数字,chr是ascii码转字符
print(chr(65))
print(ord('A'))
#    dir()    查看对应的下面有哪些属性
class Foo:
  pass
obj = Foo()
print(dir(obj))
#    divmod可以算出商和余数
print(divmod(10,3))
#    enumerate既拿到索引,也拿到值
for i,v in enumerate(['a','b','c']):
    print(i,v)
#    执行字符串中的表达式
res=eval('{"a":1}') 
print(res,type(res))
# 不可变集合
s=frozenset({1,2,3})
# 类型判断推荐使用isinstance
print(isinstance([],list)) 
#    10的平方再对3取余数
print(pow(10,2,3))
#    slice切片
s=slice(1,4,2)
l1=['a','b','c','d','e']
l2=['aaa','bbb','ccc','ddd',444]

print(l1[1:4:2]) # l1[s]
print(l2[1:4:2]) # l2[s]

#    zip
v1='hello'
v2=[111,222,333,444,5555,6666]
res=zip(v1,v2)
print(list(res))

#    __import__('time')字符串模块导入
# import 'time' # 错误
time=__import__('time')
time.sleep(3)
反射机制
# 什么是反射?
# 指的是在程序运行过程中可以"动态(不见棺材不掉泪)"获取对象的信息

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('<%s:%s>' %(self.name,self.age))

obj=People('辣白菜同学',18)

# 实现反射机制的步骤
# 1、先通过多dir:查看出某一个对象下可以.出哪些属性来
# print(dir(obj))

# 2、可以通过字符串反射到真正的属性上,得到属性值
# print(obj.__dict__[dir(obj)[-2]])

# 1、hasattr()
print(hasattr(obj,'name'))
print(hasattr(obj,'x'))

# 2、getattr()
print(getattr(obj,'name'))

# 3、setattr()
setattr(obj,'name','EGON') # obj.name='EGON'
print(obj.name)

# 4、delattr()
delattr(obj,'name') # del obj.name
print(obj.__dict__)
内置方法
# 1、什么是内置方法?
# 定义在类内部,以__开头并以__结果的方法
# 特点:会在某种情况下自动触发执行

# 2、为何要用内置方法?
# 为了定制化我们的类or对象

# 3、如何使用内置方法
# __str__:在打印对象时会自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # print('运行了...')
        return "<%s:%s>" %(self.name,self.age)


obj = People('辣白菜同学', 18)

# print(obj.__str__())
print(obj)  # <'辣白菜同学':18>

obj1=int(10)
print(obj1)

# __del__:在清理对象时触发,会先执行该方法
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.x = open('a.txt',mode='w')
        # self.x = 占据的是操作系统资源

    def __del__(self):
        # print('run...')
        # 发起系统调用,告诉操作系统回收相关的系统资源
        self.x.close()

obj = People('辣白菜同学', 18)
# del obj # obj.__del__()
print('============>')
元类

一切皆为对象

# 元类就是用来实例化产生类的类
# 关系:元类---实例化---->类(People)---实例化---->对象(obj)

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say(self):
        print('%s:%s' %(self.name,self.name))

obj = People('batmanfuture','123456')
print(type(obj))
print(type(People))
class机制分析
类有三大特征:
# 1、类名
class_name="People"
# 2、类的基类
class_bases=(object,)
# 3、执行类体代码拿到类的名称空间
异常处理
# 1、什么是异常
# 异常是程序发生错误的信号,程序一旦出错就会抛出异常,程序的运行随即终止

# 1.1 异常处理的三个特征
# 异常的追踪信息
# 异常的类型
# 异常的内容

# 2、为何处理异常
# 为了增强程序的健壮性,即便是程序运行过程中出错了,也不要终止程序
# 而是捕捉异常并处理:将出错信息记录到日志内

# 3、如何处理异常?
# 3.1 语法上的错误SyntaxError,
# 处理方式一:必须在程序运行前就改正
if 1>3
    print("run...")

# 3.2 逻辑上的错误
# 3.2.1 错误发生的条件是可以预知的,使用if判断来解决


# 3.2.2 错误发生的条件是无法预知的

print('start...')
try:
    # 有可能会抛出异常的代码
    子代码1
    子代码2
    子代码3
except 异常类型1 as e:
    pass
except 异常类型2 as e:
    pass
...
else:
    如果被检测的子代码块没有异常发生,则会执行else的子代码
finally:
    无论被检测的子代码块有无异常发生,都会执行finally的子代码

print('end...')

# 用法一:
print('start...')

try:
    print('1111111111')
    l=['aaa','bbbb']
    l[3] # 抛出异常IndexError,该行代码同级别的后续代码不会运行
    print('2222222222')
    xxx
    print('33333333')
    dic={'a':1}
    dic['a']
except IndexError as e:
    print('异常的信息: ',e)

print('end....')

# 用法二:
print('start...')

try:
    print('1111111111')
    l=['aaa','bbbb']
    # l[3] # 抛出异常IndexError,该行代码同级别的后续代码不会运行
    print('2222222222')
    xxx
    print('33333333')
    dic={'a':1}
    dic['a']
except IndexError as e:
    print('异常的信息: ',e)
except NameError as e:
    print('异常的信息: ',e)

print('end....')

# 用法三:
print('start...')

try:
    print('1111111111')
    l = ['aaa', 'bbbb']
    l[3] # 抛出异常IndexError,该行代码同级别的后续代码不会运行
    print('2222222222')
    xxx
    print('33333333')
    dic = {'a': 1}
    dic['aaa']
# except (IndexError, NameError) as e:
#     print('异常的信息: ', e)
# except KeyError as e:
#     print('字典的key不存在: ', e)
except Exception as e:  # 万能异常
    print('所有异常都可以匹配的到')
print('end....')

# 用法四:else不能单独与try配合使用,必须要搭配except
print('start...')

try:
    print('1111111111')
    print('2222222222')
    print('33333333')
except Exception as e:  # 万能异常
    print('所有异常都可以匹配的到')
else:
    print('====>')

print('end....')

# 用法五:finally可以单独与try配合使用
print('start...')

try:
    print('1111111111')
    l = ['aaa', 'bbbb']
    l[3] # 抛出异常IndexError,该行代码同级别的后续代码不会运行
    print('2222222222')
    xxx
    print('33333333')
    dic = {'a': 1}
    dic['aaa']
finally: # 不处理异常,无论是否发生异常都会执行finally的子代码
    print('====》》》》》应该把被检测代码中回收系统资源的代码放到这里')

print('end....')

多道技术

并发:看起来像同时执行
并行:就是在同时执行

~并行一定是并发,但并发不一定是并行
~单核的计算机肯定不可能有并行,但是可以实现并发

把文件从硬盘加载到内存,然后CPU执行内存中代码

时间复用 + 空间复用
"""
切换(CPU)分为两种情况
    1.当一个程序遇到IO操作的时候,操作系统会剥夺该程序的CPU执行权限
        作用:提高了CPU的利用率 并且也不影响程序的执行效率

    2.当一个程序长时间占用CPU的时候,操作吸引也会剥夺该程序的CPU执行权限
        弊端:降低了程序的执行效率(原本时间+切换时间)
"""
  • 先来先服务调度算法

    """对长作业有利,对短作业无益"""
    
  • 短作业优先调度算法

    """对短作业有利,多长作业无益"""
    
  • 时间片轮转法+多级反馈队列

    ~~~ 时间片轮转(Round Robin,RR)法的基本思路是让每个进程在就绪队列中的等待时间与享受服务的时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固定大小的时间片,例如,几十毫秒至几百毫秒。如果一个进程在被调度选中之后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,等待下一次调度。同时,进程调度程序又去调度当前就绪队列中的第一个进程

两对重要概念

  • 同步和异步

    """描述的是任务的提交方式"""
    同步:任务提交之后,原地等待任务的返回结果,等待的过程中不做任何事(干等)
          程序层面上表现出来的感觉就是卡住了
    
    异步:任务提交之后,不原地等待任务的返回结果,直接去做其他事情
          我提交的任务结果如何获取?
        任务的返回结果会有一个异步回调机制自动处理
    

同步

  • 阻塞非阻塞

    """描述的程序的运行状态"""
    阻塞:阻塞态
    非阻塞:就绪态、运行态
    
    理想状态:我们应该让我们的写的代码永远处于就绪态和运行态之间切换
    

上述概念的组合:最高效的一种组合就是异步非阻塞

开启进程的两种方式(异步)

# 第一种
from multiprocessing import Process
import time


def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)


if __name__ == '__main__':
    # 1 创建一个对象
    p = Process(target=task, args=('jason',))
    # 容器类型哪怕里面只有1个元素 建议要用逗号隔开
    # 2 开启进程
    p.start()  # 告诉操作系统帮你创建一个进程  异步
    print('主')
"""
windows操作系统下 创建进程一定要在main内创建
因为windows下创建进程类似于模块导入的方式
会从上往下依次执行代码
linux中则是直接将代码完整的拷贝一份
"""

# 第二种方式 类的继承
# from multiprocessing import Process
# import time
#
#
# class MyProcess(Process):
#     def run(self):
#         print('hello bf girl')
#         time.sleep(1)
#         print('get out!')
#
#
# if __name__ == '__main__':
#     p = MyProcess()
#     p.start()
#     print('主')
"""
创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
一个进程对应在内存中就是一块独立的内存空间
多个进程对应在内存中就是多块独立的内存空间
进程与进程之间数据默认情况下是无法直接交互,如果想交互可以借助于第三方工具、模块
"""

join方法

join是让主进程等待子进程代码运行结束之后,再继续运行。不影响其他子进程的执行

from multiprocessing import Process
import time


def task(name, n):
    print('%s is running'%name)
    time.sleep(n)
    print('%s is over'%name)


if __name__ == '__main__':
    # p1 = Process(target=task, args=('jason', 1))
    # p2 = Process(target=task, args=('egon', 2))
    # p3 = Process(target=task, args=('tank', 3))
    # start_time = time.time()
    # p1.start()
    # p2.start()
    # p3.start()  # 仅仅是告诉操作系统要创建进程
    # # time.sleep(50000000000000000000)
    # # p.join()  # 主进程等待子进程p运行结束之后再继续往后执行
    # p1.join()
    # p2.join()
    # p3.join()
    start_time = time.time()
    p_list = []
    for i in range(1, 4):
        p = Process(target=task, args=('子进程%s'%i, i))
        p.start()
        p_list.append(p)
    for p in p_list:
        p.join()
    print('主', time.time() - start_time)

进程之间数据相互隔离

from multiprocessing import Process


money = 100


def task():
    global money  # 局部修改全局
    money = 666
    print('子',money)


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print(money)

进程对象及其他方法

"""
一台计算机上面运行着很多进程,那么计算机是如何区分并管理这些进程服务端的呢?
计算机会给每一个运行的进程分配一个PID号 
如何查看
    windows电脑 
        进入cmd输入tasklist即可查看
        tasklist |findstr PID查看具体的进程
    mac电脑 
        进入终端之后输入ps aux
        ps aux|grep PID查看具体的进程 
"""
from multiprocessing import Process, current_process
current_process().pid  # 查看当前进程的进程号

import os
os.getpid()  # 查看当前进程进程号
os.getppid()  # 查看当前进程的父进程进程号


p.terminate()  # 杀死当前进程
# 是告诉操作系统帮你去杀死当前进程 但是需要一定的时间 而代码的运行速度极快
time.sleep(0.1)
print(p.is_alive())  # 判断当前进程是否存活

僵尸进程与孤儿进程

# 僵尸进程
"""
死了但是没有死透
当你开设了子进程之后 该进程死后不会立刻释放占用的进程号
因为我要让父进程能够查看到它开设的子进程的一些基本信息 占用的pid号 运行时间。。。
所有的进程都会步入僵尸进程
    父进程不死并且在无限制的创建子进程并且子进程也不结束
    回收子进程占用的pid号
        父进程等待子进程运行结束
        父进程调用join方法
"""

# 孤儿进程
"""
子进程存活,父进程意外死亡
操作系统会开设一个“儿童福利院”专门管理孤儿进程回收相关资源
"""

守护进程

from multiprocessing import Process
import time


def task(name):
    print('%s总管正在活着'% name)
    time.sleep(3)
    print('%s总管正在死亡' % name)


if __name__ == '__main__':
    p = Process(target=task,args=('egon',))
    # p = Process(target=task,kwargs={'name':'egon'})
    p.daemon = True  # 将进程p设置成守护进程  这一句一定要放在start方法上面才有效否则会直接报错
    p.start()
    print('皇帝jason寿终正寝')

互斥锁

多个进程操作同一份数据的时候,会出现数据错乱的问题

针对上述问题,解决方式就是加锁处理:将并发变成串行,牺牲效率但是保证了数据的安全

from multiprocessing import Process, Lock
import json
import time
import random


# 查票
def search(i):
    # 文件操作读取票数
    with open('data','r',encoding='utf8') as f:
        dic = json.load(f)
    print('用户%s查询余票:%s'%(i, dic.get('ticket_num')))
    # 字典取值不要用[]的形式 推荐使用get  你写的代码打死都不能报错!!!


# 买票  1.先查 2.再买
def buy(i):
    # 先查票
    with open('data','r',encoding='utf8') as f:
        dic = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1,3))
    # 判断当前是否有票
    if dic.get('ticket_num') > 0:
        # 修改数据库 买票
        dic['ticket_num'] -= 1
        # 写入数据库
        with open('data','w',encoding='utf8') as f:
            json.dump(dic,f)
        print('用户%s买票成功'%i)
    else:
        print('用户%s买票失败'%i)


# 整合上面两个函数
def run(i, mutex):
    search(i)
    # 给买票环节加锁处理
    # 抢锁
    mutex.acquire()

    buy(i)
    # 释放锁
    mutex.release()


if __name__ == '__main__':
    # 在主进程中生成一把锁 让所有的子进程抢 谁先抢到谁先买票
    mutex = Lock()
    for i in range(1,11):
        p = Process(target=run, args=(i, mutex))
        p.start()
"""
扩展 行锁 表锁

注意:
    1.锁不要轻易的使用,容易造成死锁现象(我们写代码一般不会用到,都是内部封装好的)
    2.锁只在处理数据的部分加来保证数据安全(只在争抢数据的环节加锁处理即可) 
"""

进程间通信

队列Queue模块

"""
管道:subprocess 
    stdin stdout stderr
队列:管道+锁

队列:先进先出
堆栈:先进后出
"""
from multiprocessing import Queue

# 创建一个队列
q = Queue(5)  # 括号内可以传数字 标示生成的队列最大可以同时存放的数据量

# 往队列中存数据
q.put(111)
q.put(222)
q.put(333)
# print(q.full())  # 判断当前队列是否满了
# print(q.empty())  # 判断当前队列是否空了
q.put(444)
q.put(555)
# print(q.full())  # 判断当前队列是否满了

# q.put(666)  # 当队列数据放满了之后 如果还有数据要放程序会阻塞 直到有位置让出来 不会报错

"""
存取数据 存是为了更好的取
千方百计的存、简单快捷的取

同在一个屋檐下
差距为何那么大
"""

# 去队列中取数据
v1 = q.get()
v2 = q.get()
v3 = q.get()
v4 = q.get()
v5 = q.get()
# print(q.empty())
# V6 = q.get_nowait()  # 没有数据直接报错queue.Empty
# v6 = q.get(timeout=3)  # 没有数据之后原地等待三秒之后再报错  queue.Empty
try:
    v6 = q.get(timeout=3)
    print(v6)
except Exception as e:
    print('一滴都没有了!')

# # v6 = q.get()  # 队列中如果已经没有数据的话 get方法会原地阻塞
# print(v1, v2, v3, v4, v5, v6)

"""
q.full()
q.empty()
q.get_nowait()
在多进程的情况下是不精确
"""

image-20220913200327605

IPC机制-消息队列

from multiprocessing import Queue, Process

"""
研究思路
    1.主进程跟子进程借助于队列通信
    2.子进程跟子进程借助于队列通信
"""
def producer(q):
    q.put('我是23号技师 很高兴为您服务')


def consumer(q):
    print(q.get())


if __name__ == '__main__':
    q = Queue()
    p = Process(target=producer,args=(q,))
    p1 = Process(target=consumer,args=(q,))
    p.start()
    p1.start()

生产者消费者模型

from multiprocessing import Process, Queue, JoinableQueue
import time
import random


def producer(name,food,q):
    for i in range(5):
        data = '%s生产了%s%s'%(name,food,i)
        # 模拟延迟
        time.sleep(random.randint(1,3))
        print(data)
        # 将数据放入 队列中
        q.put(data)


def consumer(name,q):
    # 消费者胃口很大 光盘行动
    while True:
        food = q.get()  # 没有数据就会卡住
        # 判断当前是否有结束的标识
        # if food is None:break
        time.sleep(random.randint(1,3))
        print('%s吃了%s'%(name,food))
        q.task_done()  # 告诉队列你已经从里面取出了一个数据并且处理完毕了


if __name__ == '__main__':
    # q = Queue()
    q = JoinableQueue()
    p1 = Process(target=producer,args=('大厨egon','包子',q))
    p2 = Process(target=producer,args=('马叉虫tank','泔水',q))
    c1 = Process(target=consumer,args=('春哥',q))
    c2 = Process(target=consumer,args=('新哥',q))
    p1.start()
    p2.start()
    # 将消费者设置成守护进程
    c1.daemon = True
    c2.daemon = True
    c1.start()
    c2.start()
    p1.join()
    p2.join()
    # 等待生产者生产完毕之后 往队列中添加特定的结束符号
    # q.put(None)  # 肯定在所有生产者生产的数据的末尾
    # q.put(None)  # 肯定在所有生产者生产的数据的末尾
    q.join()  # 等待队列中所有的数据被取完再执行往下执行代码
    """
    JoinableQueue 每当你往该队列中存入数据的时候 内部会有一个计数器+1
    没当你调用task_done的时候 计数器-1
    q.join() 当计数器为0的时候 才往后运行
    """
    # 只要q.join执行完毕 说明消费者已经处理完数据了  消费者就没有存在的必要了

线程

    进程:资源单位(起一个进程仅仅只是在内存空间中开辟一块独立的空间)
    线程:执行单位(真正被cpu执行的其实是进程里面的线程,线程指的就是代码的执行过程,执行代码中所需要使用到的资源都找所在的进程索要)

一个进程内可以开设多个线程,在用一个进程内开设多个线程无需再次申请内存空间操作
每一个进程肯定自带一个线程

image-20220916112858678

开启线程的两种方式

# from multiprocessing import Process
# from threading import Thread
# import time
#
#
# def task(name):
#     print('%s is running'%name)
#     time.sleep(1)
#     print('%s is over'%name)
#
#
# # 开启线程不需要在main下面执行代码 直接书写就可以
# # 但是我们还是习惯性的将启动命令写在main下面
# t = Thread(target=task,args=('egon',))
# # p = Process(target=task,args=('jason',))
# # p.start()
# t.start()  # 创建线程的开销非常小 几乎是代码一执行线程就已经创建了
# print('主')



from threading import Thread
import time


class MyThead(Thread):
    def __init__(self, name):
        """针对刷个下划线开头双下滑线结尾(__init__)的方法 统一读成 双下init"""
        # 重写了别人的方法 又不知道别人的方法里有啥 你就调用父类的方法
        super().__init__()
        self.name = name

    def run(self):
        print('%s is running'%self.name)
        time.sleep(1)
        print('egon DSB')


if __name__ == '__main__':
    t = MyThead('egon')
    t.start()
    print('主')

TCP服务端实现并发的效果

import socket
from threading import Thread
from multiprocessing import Process
"""
服务端
    1.要有固定的IP和PORT
    2.24小时不间断提供服务
    3.能够支持并发

从现在开始要养成一个看源码的习惯
我们前期要立志称为拷贝忍者 卡卡西 不需要有任何的创新
等你拷贝到一定程度了 就可以开发自己的思想了
"""
server =socket.socket()  # 括号内不加参数默认就是TCP协议
server.bind(('127.0.0.1',8080))
server.listen(5)


# 将服务的代码单独封装成一个函数
def talk(conn):
    # 通信循环
    while True:
        try:
            data = conn.recv(1024)
            # 针对mac linux 客户端断开链接后
            if len(data) == 0: break
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

# 链接循环
while True:
    conn, addr = server.accept()  # 接客
    # 叫其他人来服务客户
    # t = Thread(target=talk,args=(conn,))
    t = Process(target=talk,args=(conn,))
    t.start()


"""客户端"""
import socket


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

while True:
    client.send(b'hello world')
    data = client.recv(1024)
    print(data.decode('utf-8'))

线程对象的join方法

from threading import Thread
import time


def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)


if __name__ == '__main__':
    t = Thread(target=task,args=('egon',))
    t.start()
    t.join()  # 主线程等待子线程运行结束再执行
    print('主')

同一个进程下的多个线程数据是共享的

from threading import Thread
import time


money = 100


def task():
    global money
    money = 666
    print(money)


if __name__ == '__main__':
    t = Thread(target=task)
    t.start()
    t.join()
    print(money)

线程对象属性及其他方法

from threading import Thread, active_count, current_thread
import os,time


def task(n):
    # print('hello world',os.getpid())
    print('hello world',current_thread().name)
    time.sleep(n)


if __name__ == '__main__':
    t = Thread(target=task,args=(1,))
    t1 = Thread(target=task,args=(2,))
    t.start()
    t1.start()
    t.join()
    print('主',active_count())  # 统计当前正在活跃的线程数
    # print('主',os.getpid())
    # print('主',current_thread().name)  # 获取线程名字

守护线程

# from threading import Thread
# import time
#
#
# def task(name):
#     print('%s is running'%name)
#     time.sleep(1)
#     print('%s is over'%name)
#
#
# if __name__ == '__main__':
#     t = Thread(target=task,args=('egon',))
#     t.daemon = True
#     t.start()
#     print('主')

"""
主线程运行结束之后不会立刻结束 会等待所有其他非守护线程结束才会结束
    因为主线程的结束意味着所在的进程的结束
"""


# 稍微有一点迷惑性的例子
from threading import Thread
import time


def foo():
    print(123)
    time.sleep(1)
    print('end123')


def func():
    print(456)
    time.sleep(3)
    print('end456')


if __name__ == '__main__':
    t1 = Thread(target=foo)
    t2 = Thread(target=func)
    t1.daemon = True
    t1.start()
    t2.start()
    print('主.......')

线程互斥锁

from threading import Thread,Lock
import time


money = 100
mutex = Lock()


def task():
    global money
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()


if __name__ == '__main__':

    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(money)

GIL全局解释器锁

python解释器其实有多个版本
    Cpython
    Jpython
    Pypypython
但是普遍使用的都是CPython解释器

在CPython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时执行
    同一个进程下的多个线程无法利用多核优势!!!
    疑问:python的多线程是不是一点用都没有???无法利用多核优势

因为cpython中的内存管理不是线程安全的
内存管理(垃圾回收机制)
    1.应用计数
    2.标记清楚
    3.分代回收

重点:
    1.GIL不是python的特点而是CPython解释器的特点
    2.GIL是保证解释器级别的数据的安全
    3.GIL会导致同一个进程下的多个线程的无法同时执行即无法利用多核优势(******)
    4.针对不同的数据还是需要加不同的锁处理 
    5.解释型语言的通病:同一个进程下多个线程无法利用多核优势

"""
多线程是否有用要看具体情况
单核:四个任务(IO密集型\计算密集型)
多核:四个任务(IO密集型\计算密集型)
"""
# 计算密集型   每个任务都需要10s
单核(不用考虑了)
    多进程:额外的消耗资源
  多线程:介绍开销
多核
    多进程:总耗时 10+
  多线程:总耗时 40+
# IO密集型  
多核
    多进程:相对浪费资源
  多线程:更加节省资源
# 计算密集型
# from multiprocessing import Process
# from threading import Thread
# import os,time
#
#
# def work():
#     res = 0
#     for i in range(10000000):
#         res *= i
#
# if __name__ == '__main__':
#     l = []
#     print(os.cpu_count())  # 获取当前计算机CPU个数
#     start_time = time.time()
#     for i in range(12):
#         p = Process(target=work)  # 1.4679949283599854
#         t = Thread(target=work)  # 5.698534250259399
#         t.start()
#         # p.start()
#         # l.append(p)
#         l.append(t)
#     for p in l:
#         p.join()
#     print(time.time()-start_time)



# IO密集型
from multiprocessing import Process
from threading import Thread
import os,time


def work():
    time.sleep(2)

if __name__ == '__main__':
    l = []
    print(os.cpu_count())  # 获取当前计算机CPU个数
    start_time = time.time()
    for i in range(4000):
        # p = Process(target=work)  # 21.149890184402466
        t = Thread(target=work)  # 3.007986068725586
        t.start()
        # p.start()
        # l.append(p)
        l.append(t)
    for p in l:
        p.join()
    print(time.time()-start_time)

总结

多进程和多线程都有各自的优势
并且我们后面在写项目的时候通常可以
    多进程下面再开设多线程
这样的话既可以利用多核也可以介绍资源消耗

results matching ""

    No results matching ""