1、内存管理(了解)
内存:所有的程序都在内存中运行 内存分栈、堆
函数的运行都是在栈里面,(栈:先进后出),函数的执行过程就是后进先出
函数调用函数第二个函数后开辟内存空间先释放
(后进先释放)
第一个函数先开辟内存空间却要等第二个函数释放完再释放
(先进后释放)
堆:所有的对象存放的地方就都在堆里面存放
#100是对象,放在堆里 在内存中有相应的内存地址
#a是指针,放在栈里 栈存放100对应的哪个内存地址
内存:6g 8g 3g 2g
地址:为了方便管理内存,给内存的每一个字节起了一个编号,这个编号就是地址
指针:是一个变量,这个变量是用来存放地址的,(引用)
在python里面所有的变量都是指针,包括列表、元组、字典中存放的都是指针
查看内存地址,通过id查看
print(hex(id(a)))
#以16进制查看a的内存地址
print(hex(id(b)))
栈空间不用管理,自动释放,堆空间需要管理
Python属于上层语言,底层已经做好了内存管理,所以不用程序猿自己管理,我们只需要了解其中的原理即可 malloc
空间需要申请和释放的
内存如何释放?当前没有程序在用时才能释放
内存释放原理:
聊天室原理:多人一起聊天 0人关闭
引用计数的概念
对象空间里面有一个引用计数,在记录当前有多少个指针指向这里,当引用计数变为0的时候,这段空间就要被销毁
程序运行结束的时候 空间也会被销毁
判断两个指针是否指向同一个地方
is和==是不一样的,is判断的是两个指针是否指向同一个地方,==判断是内容是否相同
列表:可变类型都是两个空间
字符串:不可变类型都是一个空间
深浅拷贝
一层意义:
拷贝指针叫做浅拷贝
拷贝内存叫做深拷贝
多层意义:
列表只拷贝一层,叫做浅拷贝
列表拷贝多层,里面的列表也拷贝,叫做深拷贝
import copy
lt2 = copy.deepcopy(lt1) 深拷贝
lt2 = copy.copy(lt1) 浅拷贝
lt2 = lt1.copy() 浅拷贝
#内存案例演示
#内存
#栈和堆 对象和指针
'''
a = 100 #a是指针 放在内存中的栈里面 存放的是100的内存地址
#100是对象 放在内存的堆里面 会有一个对应的内存地址
b = 200
print(hex(id(a))) #查看a,b的内存地址以16进制查看
print(hex(id(b)))
#列表的存放原理 见图
lt = [100, 200, 300, 400] #lt放在栈里存放的是lt0 lt1 lt2 lt3的内存地址
#lt0存放的是100的内存地址
#100是放在堆里面的 有内存地址
lt = 'hello'
#列表是可变的 本来lt是指向l列表的,现在指向字符串,存放'hello'的内存地址
print(lt)
'''
#内存是如何释放的?
'''
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
wangcai = Dog('旺财') #wangcai是指针,存放的是Dog函数的内存地址,放在栈空间里面的,指向堆空间
#Dog函数是对象 放在堆空间里面 有一个内存地址
wangcai = 100 #wangcai 此时指向100的内存地址,100是常量地址是固定的
#那么此时,Dog函数的内存空间就会释放
'''
'''
#证明Dog函数空间已经被释放了
#__del__()函数当内存释放的时候系统会自动调用
#当内存被销毁的时候函数调用会打印'这条狗被杀了'看打印的顺序
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
def __del__(self):
print('这条狗被杀了')
wangcai = Dog('旺财') #wangcai是指针,存放的是Dog函数的内存地址,放在栈空间里面的,指向堆空间
print(100) #Dog函数是对象 放在堆空间里面 有一个内存地址
wangcai = 100
print(200)
#输出结果
#100
#这条狗被杀了 此时Dog函数的内存已经被释放了
#200
'''
'''
#两个指针指向同一个对象 内存如何释放? ---->聊天室原理
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
def __del__(self):
print('这条狗被杀了')
wangcai = Dog('旺财')
lala = wangcai #wangcai里面存放的是Dog函数的内存地址 会给lala
#此时有两个指针lala wangcai指向Dog函数
print(100)
wangcai =100
print(200)
'''
'''
#聊天室原理:只要还有人再用就不会销毁这段空间
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
def __del__(self):
print('这条狗被杀了')
def wangwang(self):
print('汪汪')
wangcai = Dog('旺财')
lala = wangcai
dudu =lala #此时三个指针wangcai lala dudu指向Dog函数
lala.wangwang()
print(100)
wangcai = 100 #wangcai指针指向100 还有两个指针指向Dog函数 不释放
print(200)
print('lala')
lala = 200 #lala指针指向200 还有一个指针指向Dog函数 不释放
print('lala')
print('dudu')
dudu = 100 #dudu指针指向100 还有0个指针指向Dog函数 释放空间
print('dudu')
'''
'''
#判断两个指针是否指向同一个地方
#两个指针指向同一个空间
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
def __del__(self):
print('这条狗被杀了')
def wangwang(self):
print('汪汪')
wangcai = Dog('旺财')
lala = wangcai #没开辟新空间
print (lala.name)
wangcai.name = 'hehe' #只是修改了wangcai.name 并未修改lala.name为什么也会变?
print(lala.name)
#由于wangcai 和lala 指向同一个对象 操作同一个目标 name在空间里面放着
#wangcai.name修改了 lala.name也会被修改
#两个指针指向不同的目标
class Dog(object):
"""docstring for Dog"""
def __init__(self, name):
super(Dog, self).__init__()
self.name = name
def __del__(self):
print('这条狗被杀了')
def wangwang(self):
print('汪汪')
wangcai = Dog('旺财')
lala = Dog('旺财') #此时只有wangcai.name修改了 lala.name并没有修改
#此时开辟了新的空间
print (lala.name)
wangcai.name = 'hehe'
print(lala.name)
'''
'''
#列表如何判断是否指向同一空间?
lt1 = [100, 200, 300]
lt2 = [100, 200, 300]
# print(id(lt1))
# print(id(lt2))
if lt1 == lt2: #只是判断内容是否相等?
print('相等')
else:
print('不相等')
if lt1 == lt2: #判断是否同一个空间?
print('相等')
else:
print('不相等')
#字符串如何判断是否指向同一段空间?
str1 = 'hello'
str2 = 'hello'
if str1 == str2:
print('相等')
else:
print('不等')
if str1 is str2:
print('相等')
else:
print('不等')
#输出都是相等 原因字符串是不可变类型 内存地址是固定的 不会再开辟空间
#整形如何判断是否指向同一段空间?
a = -5
b = -5
if a is b:
print('相等')
else:
print('不相等')
#-5及以上一个内存相等,-5以下开辟两个内存不相等
'''
#拷贝的两种方式
#第一多一个指针指向
#第二拷贝
#深浅拷贝
'''
#一层意义上的深浅拷贝
#浅拷贝
lt1 = [100, 200, 300]
lt2 = lt1 #多一个指针指向同一个空间称浅拷贝
lt1[0] = 'hello' #一个改了另一个也会改
print(lt1)
print(lt2)
#深拷贝
lt1 = [100, 200, 300]
lt2 = lt1.copy() #重新开辟一个空间,拷贝
lt1[0] = 'hello' #一个改了另外那个不会改
print(lt1)
print(lt2)
'''
#两层意义上的深浅拷贝 多层意义上的深浅拷贝
#浅拷贝
lt1 = [['kobe', 'lebran', 'wade'], 200, 300]
lt2 = lt1.copy() #栈空间lt[0][0]没有拷贝即只拷贝外面没有全部拷贝
#lt2 = copy.copy(lt1)
lt1[0][0] = 'hello' #一个变了,另外的也会变
print(lt1)
print(lt2)
#原理见图形
#深拷贝
import copy
lt1 = [['kobe', 'lebran', 'wade'], 200, 300]
lt2 = lt1.deepcopy(lt1) #栈空间lt[0][0]也会拷贝即全部拷贝
lt1[0][0] = 'hello' #一个变了,另外的不会变
print(lt1)
print(lt2)
2、进程
多任务
生活中,好多例子,唱歌跳舞,开车,抽烟聊天
代码中,唱歌跳舞例子
#进程 多任务同时进行
#唱歌跳舞的例子
#边唱边跳
import time
def sing():
for x in range(10):
print('我在唱青藏高原')
time.sleep(1)
def dance():
for x in range(10):
print('我在跳钢管舞')
time.sleep(1)
if __name__ == '__main__':
sing()
dance()
#代码运行会先执行sing函数,执行完毕之后再执行dance函数
#也就是说先唱后跳
#无法完成边唱边跳的要求那么,怎样实现边唱边跳呢?
操作系统
可以实现多任务,可以听音乐、可以聊天、写代码
计算机在执行的时候只能执行一种运算
但是通过
cpu快速的切换任务,达到多任务同时进行的目的
打开一个应用 一个任务,就是一个进程
代码和进程
编写的代码,在没有运行之前,我们称之为程序
运行的时候,就是一个进程
进程的创建(process)
mac:unix
乌班图 Ubuntu:linux(类unix)
类unix系统的创建方式
pid = os.fork()
进程号:pid,系统都是根据进程号来管理进程的
fork函数调用一次,返回两次,如果主进程执行,返回的pid是子进程id,如果子进程执行,返回的pid是0
获取当前进程id号:os.getpid()
获取当前进程的父进程的id号:os.getppid()
windows的创建方式
from multiprocessing import Process
#通过进程实现边唱歌边跳舞-----unix系统下
#主进程是当前代码运行,子进程就是通过主进程创建的
import os
pid = os.fork() #pid== process id 进程号
#普通的函数调用之后会返回一次,fork函数调用之后会返回两次
#主进程在跑的时候会返回子进程的pid
#子进程在跑的时候会返回0,不是子进程的ID号
if pid == 0:
print('主进程在跑')
else:
print('子进程在跑')
#Unix系统下运行
#会发现if和else都在跑,会有两个进程一个跑if,一个跑else
import os
pid = os.fork()
if pid == 0:
print('子进程在跑')
print('子进程的pid为%s'%os.getpid()) #获取当前进程的ID号os.getpid()
print('子进程的父进程pid为%s'%os.getppid())#子进程的父进程就是主进程
else: #获取当前进程的父进程的ID号os.getppid( ) parrent pid
print('父进程在跑')
print('子进程的pid为%s'%pid)
print('父进程的pid为%s'%os.getpid())
#通过进程实现边唱歌边跳舞----windows系统下面向过程和面向对象两种方式
#面向过程的写法
#第一步 导入库 设置入口
#格式:(面向对象必须要按照这个格式进行创建进程)
'''
#导入库 设置入口
from multiprocessing import Process
if __name__ == '__main__':
main()
'''
#第二步创建一个进程
'''
from multiprocessing import Process
def lala():
#pass
print('lala进程在执行')
if __name__ == '__main__':
#创建一个进程
#创建进程和创建对象是一样的通过变量进行创建,里面有参数
#target 创建进程要执行的函数 target=lala 创建一个进程执行lala函数
#name是给这个进程起个名字
p = Process(target=lala,name='啦啦') #此时创建了两个进程一个主进程一个lala进程
print('主进程在执行') #但是只执行了主进程lala进程并没有执行
''' #原因是lala进程必须要启动才行
#第三步 启动一个进程
'''
#如何启动一个进程
from multiprocessing import Process
def lala():
print('lala进程在执行')
if __name__ == '__main__':
p = Process(target=lala,name='啦啦')
#启动一个进程
p.start()
print('主进程在执行')
'''
#第四步 主进程等待子进程结束再执行
'''
from multiprocessing import Process
import time
def lala():
for x in range(10):
print('lala进程在跑第%d次' % x) #让子进程走一会儿
time.sleep(1)
if __name__ == '__main__':
p = Process(target=lala,name='啦啦')
p.start() #运行的时候子进程是通过主进程创建出来的,主进程会先执行,子进程再执行,这样就会主进程先结束
#若主进程先结束那么子进程可能就找不到了变成僵尸进程
#让主进程等待子进程运行完成完毕之后再执行
p.join()
print('主进程和子进程全部结束')
'''
#最终代码
'''
from multiprocessing import Process
import time
def lala():
for x in range(10):
print('lala进程在跑第%d次' % x) #让子进程走一会儿
time.sleep(1)
if __name__ == '__main__':
p = Process(target=lala,name='啦啦')
p.start()
for x in range(10):
print('主进程在跑第%d次' % x)#让主进程走一会儿
time.sleep(1)
p.join()
print('主进程和子进程全部结束')
'''
#总结
'''
from multiprocessing import Process
def lala():
#pass
print('lala进程在执行')
if __name__ == '__main__':
#创建进程和创建对象是一样的通过变量进行创建,里面有参数
#target 创建进程要执行的函数 target=lala 创建一个进程执行lala函数
#name是给这个进程起个名字
p = Process(target=lala,name='啦啦') #此时创建了两个进程一个主进程一个lala进程
print('主进程在执行')
'''
#进程之间传递参数
'''
from multiprocessing import Process
import time
def lala(a): #主进程给子进程传递参数a就是100
# print('lala进程在执行')
print(a)
for x in range(3):
print('lala进程在跑第%d次' % x)
time.sleep(1)
if __name__ == '__main__':
# target: 创建进程要执行的函数
# name:给进程起一个名字
# args: 给子进程传递参数 是一个元组 #主进程给子进程传递参数100
p = Process(target=lala, name='啦啦', args=(100,)) #args=(100,)元组中一个参数的格式要有逗号
# 启动一个进程
p.start()
for x in range(3):
print('主进程在跑第%d次' % x)
time.sleep(1)
# print(p.name)
# 让主进程等待子进程结束之后在执行
p.join()
print('主进程和子进程全部都结束')
'''
#总结
#第一步导入库 创建入口
#第二步创建进程 启动一个进程 让主进程等待子进程结束之后再结束
#第三步 写进程里面的目标函数
面向过程
见代码
p.start() 启动进程
p.join() 让主进程等待子进程结束之后再执行
p.terminate() 让子进程终止
p = Process(target=xx, name=xx, args=(xx, xx))
target: 子进程要执行的函数
name:进程的名字
args:给子进程传递的参数
面向对象
见代码
获取进程id号:os.getpid()
获取进程父id号:os.getppid()
写一个类继承自Process,重写run方法
#windows下多进程-----面向对象的写法
#通过类来创建一个进程
#面向对象的类是自己写的,面向过程是自带的
'''
from multiprocessing import Process
import time, os
class MyProcess(Process):
"""docstring for MyProcess"""
def __init__(self, name):
super(MyProcess, self).__init__()
self.name = name
def run(self):
for x in range(5):
print('子进程在跑')
time.sleep(1)
if __name__ == '__main__':
p = MyProcess('小进程')
p.start()
for x in range(3):
print('主进程在跑')
time.sleep(1)
p.join()
print('主进程结束')
'''
'''
#获取进程ID号os.getpid()获取当前进程ID号, os.getppid()获取父进程的ID号
#终止子进程
from multiprocessing import Process
import time, os
class MyProcess(Process):
"""docstring for MyProcess"""
def __init__(self, name):
super(MyProcess, self).__init__()
self.name = name
# 重写一个run方法,这个run方法就是进程执行的函数
#通过类创建一个进程,进程默认执行的就是run方法不能改名字
def run(self):
print('子进程的id为%s,主进程的id为%s' % (os.getpid(), os.getppid()))
for x in range(5):
print('子进程在跑')
time.sleep(1)
if __name__ == '__main__':
print('主进程id号为%s' % os.getpid())
p = MyProcess('小进程')
p.start()
for x in range(3):
print('主进程在跑')
time.sleep(1)
# 终止子进程
#主进程跑3次子进程跑5次肯定主进程先跑完主进程跑完后终止子进程
p.terminate()
print('主进程结束')
'''
全局变量
主进程和子进程不共享全局变量,只要创建进程,就会将代码复制一份,主进程里面有代码,子进程里面也有代码,他们都拥有自己的全局变量
#全局变量和局部变量
#进程之间是否共享全局变量和局部变量?
#思路
#demo函数修改全局变量那么子进程运行会修改子进程的全局变量
#若主进程的全局变量也修改了说明主进程和子进程共享全局变量
#若主进程的全局变量没有修改说明子进程和主进程不共享全局变量
#代码
from multiprocessing import Process
import os
count = 1000 #全局变量
def demo():
print('子进程%d在运行' % os.getpid())
#修改全局变量
global count
count += 100
print('子进程里面的count为%d' % count)
if __name__ == '__main__':
# 创建一个进程
p = Process(target=demo)
# 启动一个进程
p.start()
# 让主进程等待子进程结束之后再结束
p.join()
print('主进程里面的count为%d' % count)
print('主进程子进程都结束')
#输出结果表明 主进程和子进程不共享全局变量
#创建进程之后全局变量在主进程中会有一份,在子进程中也会有一份,子进程的全局变量是子进程的全局变量,主进程是主进程的
进程池(pool)
见代码
#进程池
#进程池也是用来创建进程的,能一次创建多个进程
#思路
#pol = Pool(3)说明只能创建三个进程 向池子中添加任务创建无数个任务会创建无数个进程
#最终通过ID号判断有几个进程
from multiprocessing import Pool
import os, time, random
def test(name):
print('子进程%s开始运行,其id号为%s' % (name, os.getpid()))
lt = [1, 2, 3]
time.sleep(random.choice(lt))
if __name__ == '__main__':
print('主进程开始执行,id号为%d' % os.getpid())
#创建进程池
pol = Pool(3)
# 3代表最多创建3个进程,
#向池子中添加任务,添加一个任务就会创建一个进程,
#如果进程数没有超过3,直接创建,
#如果进程数超过3了,等待里面的进程结束,再创建
for x in ['张飞', '赵云', '关羽', '黄忠', '典韦', '貂蝉', '吕布', '刘备']:
# 向池子中添加任务
pol.apply_async(test, args=(x,))
# 用完之后,关闭池子
pol.close()
pol.join()# 让主进程等待子进程结束
print('主进程、子进程全部结束')
进程间通信(队列queue)
先进先出
3、单元测试