进程
1、进程和线程的联系及区别:
进程和线程都可以实现多任务。(1)进程是资源分配的基本单位,程序运行时会系统创建一个进程,并为它分配资源。线程是程序执行的最小单位。(2)线程不可独立存在,需要依赖于进程。(3)一个进程可以有多个线程,多个线程共享进程的资源,而多个进程间资源是独立的,所以CPU切换一个线程的花费比切换进程的小,同时创建一个线程的开销也比进程要小很多。(4)多进程稳定性高,一个子进程崩了不会影响其他进程,因为每个进程都有独立的资源;但是任何一个线程挂掉都可能直接造成整个进程崩溃,因为多个线程共享一个进程资源(5)由于GIL锁的缘故,python 中线程实际上是并发运行(即便有多个cpu,线程会在其中一个cpu来回切换,只占用一个cpu资源),而进程才是真正的并行(同时执行多个任务,占用多个cpu资源)(6)IO密集型使用多线程,计算密集型使用多进程。
2、进程状态:就绪、运行、等待(阻塞)、死亡
3、Ps -aux 可查看某个程序的进程数
4、代码实现同线程类似
import multiprocessing
def test_1():
while True:
print("进程1")
def test_2():
while True:
print("进程2")
if __name__=="__main__":
p1 = multiprocessing.Process(target=test_1)
p2 = multiprocessing.Process(target=test_2)
p1.start()
p2.start()
5、一个程序至少有一个进程,一个进程至少有一个主线程。
6、可以通过socket、文件读写、Queue队列完成进程间通信
进程池
1、主进程或者主线程会等待子进程或者子线程执行完毕后结束,但是创建线程池后,主进程不会等待进程池中的代码执行完,可以使用进程池的.join()方法等待子进程执行完。
2、示例:
import os
import multiprocessing
def copy_file(file_name):
with open(file_name) as f:
cont = f.read()
with open(file_name,'wb') as f2:
f2.write(cont)
def main():
folder_name = ""
try:
os.mkdir(folder_name+"复制")
except Exception as e:
pass
files_names = os.listdir(folder_name)
po = multiprocessing.Pool(5)
for fi in files_names:
po.apply_async(copy_file, args = (fi,))
po.close()
po.join()
if __name__ == "__main__":
main()
线程
1、多线程比单线程抓取数据的时候快,
2、线程实现方式有两种:
(1)类式
import threading,time
class myThread(threading.Thread):
def __init__(self, name, delay, counter):
super().__init__()
self.name = name
self.delay = delay
self.counter = counter
# 重写run
def run(self):
self.name = threading.Thread.getName(self)
print_time(self.name, self.delay, self.counter)
print("线程名:"+self.name)
def print_time(name, delay, counter):
while counter:
time.sleep(delay)
print("{}: {}".format(name, time.ctime(time.time())))
counter -= 1
#创建线程
thread1 = myThread("THREAD-1", 2, 3)
thread2 = myThread("THREAD-2", 5, 2)
print("THREAD-1 is alive?", thread1.is_alive())
thread1.start()
thread2.start()
print("THREAD-1 is alive?", thread1.is_alive())
thread1.join()
thread2.join()
print("THREAD-1 is alive?", thread1.is_alive())
(2)函数式
import threading
def A():
print("线程1")
def B():
print("线程2")
if __name__ == "__main__":
t1 = threading.Thread(target = A, args=(,))
t2 = threading.Thread(target = B)
t1.start() //start 创建线程,start前不会创建线程
t2.start()
print “主线程”
3、主线程在子线程执行结束后结束
4、一个程序运行起来,一定有一个主线程
5、多线程共享全局变量有抢占资源的问题,可采用同步的互斥锁来解决问题
6、threading常用方法
threading.enumerate() 返回当前正在运行的线程list,正在运行是指线程启动后、结束前;
threading.current_thread() 返回当前线程变量
threading.active_count() 返回当前正在运行的线程数
同步 互斥锁
同步是“协同步骤”,不是“一起”
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能保证多个线程安全访问竞争资源。最简单的同步机制是引入互斥锁。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程先数据的准确性。
创建锁 :mt = threading.Lock()
锁定 :mt.acquire()
释放 :mt.release()
死锁
两个锁互相等待时会出现死锁,此时可以添加超时时间等来解决死锁问题。
线程并发
并发是指两个或多个事件在同一时间间隔发生,python中的线程属于并发,不管计算机有多少个CPU,不管你开了多少个线程,同一时间多个任务会在其中一个CPU来回切换,只占用一个CPU,效率并不高;
进程并行
并行是指两个或者多个事件在同一时刻发生,python中的进程属于并行,能充分利用计算机资源,效率最高,同时执行多个任务,占用多个cpu资源;
堆
栈
1、栈是先进后出型数据结构
2、写一个模拟栈的程序。用户可以输入的参数如下:
import sys
class Stack():
def __init__(self):
self.stack = []
def push_stack(self, arg):
self.stack.append(arg)
return self.stack
def get_top(self):
if self.stack:
return self.stack[-1]
else:
return None
def get_len(self):
return len(self.stack)
def is_empty(self):
return self.stack == []
def pop_stack(self):
self.stack.pop()
return self.stack
def show_stack(self):
return self.stack
def do_exit(self):
sys.exit()
s = Stack()
s.push_stack("aaa")
s.push_stack("bbb")
s.push_stack("ccc")
s.push_stack("ddd")
r = s.get_len()
print(r)
q = s.get_top()
print(q)
print(s.is_empty())
s.pop_stack()
print(s.show_stack())
队列Queue
1、队列是先进先出型数据结构
2、写一个模拟队列的程序。用户可以输入的参数如下
import sys
class Queue():
def __init__(self, size):
self.queue = []
self.size = size
def enqueue(self, ele):
if self.isfull():
raise Exception("queue is full")
else:
self.queue.append(ele)
def dequeue(self):
if self.isempty():
raise Exception("queue is empty")
else:
self.queue.pop(0)
def top_queue(self):
return self.queue[0]
def last_queue(self):
return self.queue[-1]
def len_queue(self):
return len(self.queue)
def isfull(self):
return len(self.queue) == self.size
def isempty(self):
return self.queue == []
def do_exit(self):
sys.exit()
def show_queue(self):
return self.queue
q = Queue(3)
q.enqueue("a")
q.enqueue("b")
q.enqueue("c")
q.dequeue()
q.enqueue("d")
print(q.show_queue())
全局解释器锁(GIL)
1、每个线程执行前都需要先获取到GIL,保证同一时刻只能有一个线程在执行
2、全局解释器锁只对线程有影响,对进程、协程没有影响
3、如果用cpython 解释器,建议用多进程;如果不使用cpython解释器,多线程比多进程更节省资源
迭代器
1、迭代是访问集合元素的一种方式。
2、可直接作用于for循环的对象为可迭代对象Iterable。判断是否可迭代,可以使用isinstance()判断一个对象是否是Iterable对象。如果想让一个对象成为可迭代对象,类必须实现__iter__()方法。
3、迭代器是一个可以记住遍历位置的对象,用来帮助我们记录每次迭代访问到的位置。
迭代器从集合的第一个元素开始访问,直到所有元素都被访问完结束。迭代器只能往前不能往后。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
使用next()函数的时候,调用的就是迭代器对象的__next__方法,python要求迭代器本身是可迭代的,所以我们还要为迭代器实现__iter__方法。一个实现了__iter__方法和__next__方法的对象,就是迭代器。
4、判断一个对象是否可以使用for循序,先判断对象是否可迭代,实现__iter__()的对象即可迭代;再判断__iter__()方法是否返回迭代器,iter()需返回一个具有iter方法和next方法的对象引用,这个对象引用叫作迭代器)。
for循环的本质就是先通过iter()函数获取可迭代对象的迭代器,然后调用迭代器的next()方法获取下一个值,直到报出StopIteration.。
5、判断是否可迭代:
from collections import Iterable
print(isinstance([], Iterable))
6、判断__iter__()方法是否返回一个具有iter方法和next方法的对象引用(迭代器),可直接使用iter(对象)。iter函数会直接调用对象中的__iter__(),并返回__iter__()的返回值。
7、可迭代对象一类是集合数据类型,如str, list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的generator function。
8、迭代器协议:具有next和iter方法的对象
7、list、dict等是可迭代对象,但不是迭代器。执行iter方法可以把可迭代对象变成迭代器
8、迭代器存储的是生成数据的方式,而不是生成数据的结果,迭代器占用内存小。示例:
class Fibonacci(object):
def __init__(self, count):
self.m = 0
self.n = 1
self.count = count
def __iter__(self):
return self
def __next__(self):
if self.count<0:
raise StopIteration
temp = self.m
self.m, self.n = self.n, self.m+self.n
self.count -= 1
return temp
for i in Fibonacci(10):
print(i)
9、不是只有for循环可以接受可迭代对象,list,tuple等都可以接收可迭代对象。list(),tuple()转换
10、除了next()方法外,还可以使用send()方法唤醒迭代器
生成器
1、生成器是一种特殊的迭代器。
2、创建生成器
方法1(不常用):
‘’‘把列表生成式的中括号变成小括号’’’
nums = [x*2 for x in range(10)] //列表生成式
print(nums)
nums = (x*2 for x in range(10)) //生成器
print(nums)
方法2:
'''如果一个函数中有yield语句,那么这个函数就不是一个函数,而是一个生成器的模板'''
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
'''每次yield a 返回的值 其实是next(s)'''
yield a
a, b = b, a+b
current_num += 1
'''py3中的生成器允许使用return返回最终运行的返回值,py2不允许 '''
return "ok"
'''调用create_num 不是调用函数,而是创建一个生成器对象'''
s = create_num(10)
'''调用next()函数唤醒生成器'''
qq = next(s)
print(qq)
'''可以使用next()函数让生成器从断点处继续执行'''
qq = next(s)
print(qq)
qq = next(s)
print(qq)
//for num in s:
// print(num)
yield
yield关键字有两个作用:
(1)保存当前运行状态,然后暂停执行,即将生成器函数挂起
(2)将yield关键字后面表达式的值作为返回值返回,此时可理解为起到return作用
Python 的 generator 提供了一种实现迭代器协议的便捷方式。 如果容器对象 iter() 方法被实现为一个生成器,它将自动返回一个迭代器对象(从技术上说是一个生成器对象),该对象提供 iter() 和 next() 方法。
generator 就是生成器
协程
协程切换任务占用资源很少,效率高
1、使用yield实现多任务,就是协程
def test1():
while True:
print("--1--")
yield
def test2():
while True:
print("--2--")
yield
def main():
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
2、为了更好的使用协程来完成多任务,可使用greenlet模块来实现(使用比较少)
安装:sudo pip3 install greenlet
from greenlet import greenlet
def test1():
while True:
print("--1--")
gr2.switch()
def test2():
while True:
print("--2--")
gr1.switch()
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr2.switch()
3、使用gevent实现协程(常使用方法)
安装sudo pip3 install gevent
gevent不同于yield和greenlet,它有个优点,遇到延时会自动切换
程序中有耗时时,将程序中用到的耗时操作的代码自动换成gevent.sleep()
import gevent
from gevent import monkey
monkey.patch_all()
批量添加任务
gevent.joinall(
[
gevent.spawn(函数名, "参数1"),
gevent.spawn(函数名, "参数2")
]
)
装饰器
1、python装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。
装饰器函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原”函数的函数。
2、python内置装饰器:
- staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
- classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
- property 是属性的意思,表示可以通过类实例直接访问的信息
- unique 定义枚举函数, value值唯一
3、原始装饰器实现方法:
import time
def deco(f):
def wrapper():
start_time = time.time()
f()
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" %execution_time )
return wrapper
@deco
def f():
print("hello")
time.sleep(1)
print("world")
if __name__ == '__main__':
f()
@deco //等价于 f = deco(f)
4、带有固定参数的装饰器
import time
def deco(f):
def wrapper(a,b):
start_time = time.time()
f(a,b)
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" % execution_time)
return wrapper
@deco
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f(3,4)
5、无固定参数的装饰器
import time
def deco(f):
def wrapper(*args, **kwargs):
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time_ = (end_time - start_time)*1000
print("time is %d ms" %execution_time)
return wrapper
@deco
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
@deco
def f2(a,b,c):
print("be on")
time.sleep(1)
print("result is %d" %(a+b+c))
if __name__ == '__main__':
f2(3,4,5)
f(3,4)
6、使用多个装饰器,装饰一个函数
import time
def deco01(f):
def wrapper(*args, **kwargs):
print("this is deco01")
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" % execution_time)
print("deco01 end here")
return wrapper
def deco02(f):
def wrapper(*args, **kwargs):
print("this is deco02")
f(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f(3,4)
”f(3, 4) = deco01(deco02(f(3, 4)))”
7、装饰器的作用和功能
引入日志
函数执行时间统计
执行函数前预备处理
执行函数后的清理功能
权限校验等场景
缓存
8、带有返回值的装饰器
def line(f):
def create_y():
return f()
return create_y
@line
def test1():
return "test1"
ret = test1()
print(ret)
9、用类对函数进行装饰
class Test(object):
def __init__(self, func):
self.func = func
def __call__(self):
print("为装饰器添加功能")
return self.func()
@Test
def get_str():
return "haha"
print(get_str())
框架搭建
1、开发时,python代码模式
导入模块
定义全局变量
定义类
定义函数
def main():
pass
if name==“main”:
main()
设计模式
单例设计模型
1、用类创建的对象,在系统中只有唯一的实例(每次执行类名创建对象, 返回对象的内存地址都是一样的)
2、实现
class A():
instance = None
def __new__(cls, *args, **kwargs):
print("创建对象分配空间")
if A.instance is None:
A.instance = super().__new__(cls)
return A.instance
p1 = A()
p2 = A()
print(id(p1))
print(id(p2))
虽然每次都使用第一次创建的对象,但是会多次调用初始化方法。
修改方法:
class A():
instance = None
init_flag = False
def __new__(cls, *args, **kwargs):
print("创建对象分配空间")
if A.instance is None:
A.instance = super().__new__(cls)
return A.instance
def __init__(self):
if not init_flag:
print(“初始化”)
init_flag = True
p1 = A()
p2 = A()
print(id(p1))
print(id(p2))
哈希
1、哈希是一种算法,hash函数接受一个不可变类型的数据作为参数,返回一个整数。传入数据相等返回的哈希值也相等。
深拷贝、浅拷贝
1、浅拷贝是对一个对象的顶层拷贝。浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象;改变原始对象中为不可变类型的元素的值,不会响拷贝对象。
2、深拷贝,除了顶层拷贝,还对子元素也进行了拷贝。经过深拷贝后,原始对象和拷贝对象所有的可变元素地址都不相同。
3、不论深拷贝还是浅拷贝,拷贝list时 会新建地址,但是拷贝int、str不可变数据类型时不会新建地址。
4、元组中没有可变类型时,深拷贝浅拷贝都不会拷贝,都是引用指向;但是元组中有可变类型时,浅拷贝不拷,深拷贝会新建地址
5、copy.copy()是浅拷贝
copy.deepcopy()是深拷贝
6、另外 列表的切片是浅拷贝;
字典的.copy()方法也是浅拷贝