day34 python 对象的特殊方法__setattr__() 进程 requests模块 bs4模块
一.面向对象简单补充
1.给对象设置值的本质: 是__setattr__() 这句
class Foo(object):
def __init__(self):
self.name = "bajie" #默认自己的类中执行 __setattr__, 自己没有这个方法去object里去找
def __setattr__(self, key, value):
object.__setattr__(self,key,value) #本质: name = 'bajie' 是父类的这个方法设置的
obj = Foo()
print(obj.name)
2.当在自己类里有写 __setattr__()方法时, 用自己的
当自己类中没有__setattr__()方法时,找父类的, 如下面例子中找的是 object.__setattr__()
class Foo(object):
def __init__(self):
self.name = 'bajie' #没设置上值,因为自己的__setattr__()里面没有语句
object.__setattr__(self,'info',{}) #设置上了
def __setattr__(self, key, value):
pass
obj = Foo()
print(obj.name) #没设置上值,因为自己的__setattr__()里面没有语句
print(obj.info) #设置上了
3.原码里是这样操作的
class Foo(object):
def __init__(self):
object.__setattr__(self,'info',{})
def __setattr__(self, key, value):
self.info[key] = value
def __getattr__(self, item):
return self.info[item]
obj = Foo()
obj.bajie = 'wukong'
print(obj.bajie) #print(obj.info['bajie']) 实际上是这个
二.进程
1.进程的写法(和线程是一样的) (multi 多种, processing 处理)
linux下 正常写就行,没问题
windows下 的多进程要写在 __main__ 里面,才能正常运行
#linux下 这样写没问题
import multiprocessing
def task(arg):
print(arg)
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
#windows下 的多进程要写在 __main__ 里面,才能正常运行
import multiprocessing
def task(arg):
print(arg)
def run():
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
if __name__ == '__main__':
run()
#以后的代码没有在windows上运行, 都是在linux, 因为liunux免费
2.进程本身: 进程间数据不共享
import multiprocessing
data_list = []
def task(arg):
data_list.append(arg)
print(data_list)
def run():
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
if __name__ == '__main__':
run()
>>>[2] #每个进程创建属于自己的内存空间, 空间里有各自的 data_list 变量
[3]
[1]
[0]
[6]
[5]
[7]
[4]
[8]
[9]
3.进程常用方法, 和线程使用一样(关键词类似)
p.join()/p.join(5)
p.deamon = True #默认为False
p.name = 'process1'
p = multiprocessing.current_process()
print(p.name)
print(p.ident)(和p.pid是一个值)
4.进程的创建方法(两种)
4.1.直接使用多进程模块: multiprocessing
import multiprocessing
def task(arg):
print(arg)
def run():
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
if __name__ == '__main__':
run()
4.2.使用类的继承方式创建进程
import multiprocessing
class MyProcess(multiprocessing.Process):
def run(self):
print('当前进程', multiprocessing.current_process())
def run():
p = MyProcess()
p.start()
p1 = MyProcess()
p1.start()
if __name__ == '__main__':
run()
三.进程之间的数据共享
企业中基本用不到:这个是内存级别的, 一般大量数据不放到内存里, 而是放到redis和mysql里
进程间数据本身不共享, 我就想让进程间数据共享
用特殊的容器来实现(先介绍两种)
1.数据共享方式一
普通的队列不能实现: 普通queue不能做进程间的数据共享
q = queue.Queue()
用特殊队列来实现: 多进程模块中的队列
q = multiprocessing.Queue()
linux下 完全没有问题, 正常使用多进程中的queue即可
windows下, 要把queue当成参数传给函数才行
#linux下 完全没有问题: q = multiprocessing.Queue(),正常写
import multiprocessing
import time
q = multiprocessing.Queue()
def task(arg):
q.put(arg)
def run():
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
time.sleep(2)
if __name__ == '__main__':
run()
v = q.get()
print(v)
v = q.get()
print(v)
#windows下, 要把queue当成参数传进去才行
import multiprocessing
import time
def task(arg, q): #要把queue当成参数传进去才行
q.put(arg)
if __name__ == '__main__':
q = multiprocessing.Queue()
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,q,))
p.start()
time.sleep(2)
while 1:
v = q.get()
print(v)
2.数据共享方式二
用特殊的dict来实现: 多进程中的字典
dic = multiprocessing.Manager().dict()
2.1.直接上windows下的运行代码
当使用dict或socket通信, 最终都是写到了一个文件里
有个问题: 当主进程执行完, 就会把公共"字典文件"释放掉, 子进程没有了这个字典文件就会报错
import multiprocessing
import time
def task(arg, dic):
dic[arg] = 100
if __name__ == '__main__':
m = multiprocessing.Manager()
dic = m.dict()
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,dic,))
p.start()
p.join() #方法一: 等子进程一个一个执行完, 我主进程才开始执行,但是这样进程就不能并发了
#time.sleep(2) #方法二: 加延迟时间只能测试时用, 因为你不知道进程到底确切地执行了多长时间
print(dic.values())
2.2.如何解决上面的问题
方法三: 加检测, 不停地检测子进程的存活状态, 当子进程全都终止,才退出检测程序
p.is_alive()
#直接上windows下的运行代码
import multiprocessing
import time
def task(arg, dic):
dic[arg] = 100
process_list = []
if __name__ == '__main__':
dic = multiprocessing.Manager().dict()
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,dic,))
p.start()
process_list.append(p)
while 1:
count = 0
for p in process_list:
if not p.is_alive():
count += 1
if count == len(process_list):
break
print(dic)
3.数据共享方式三
进程间的数据共享依赖其他设备的软件(例如: redis, mysql)
经常用到的是这个
四.进程锁 (和线程锁一毛一样)
import multiprocessing
import time
lock = multiprocessing.RLock()
def task(arg):
print('bajie ai shuishui')
lock.acquire()
time.sleep(5)
print(arg)
lock.release()
if __name__ == '__main__':
for i in range(10):
p = multiprocessing.Process(target=task, args=(i,))
p.start()
五.进程池
python3.3之后才有的, 如果2.x的版本要使用, 只要装相关的模块就行了
线程的个数没法判断, 但是进程数可以确定: 和cpu的个数一样即可
和线程池一毛一样
pool = ProcessPoolExecutor(5)
def task(arg):pass
pool.submit(task,i)
#直接上windows下的代码
import time
from concurrent.futures import ProcessPoolExecutor
def task(arg):
time.sleep(2)
print(arg)
if __name__ == '__main__':
pool = ProcessPoolExecutor(5)
for i in range(20):
pool.submit(task,i)
五.requests模块,bs4模块
与进程和线程无关,与io多路复用有关的两个模块
装包: pip3 install requests
pip3 install beautifulsoup4
'''
重要的不是爬虫,而是下面这些:
a.下面这个示例进程好还是线程好?
线程好, 因为是下载, 是io密集型操作
b.requests模块是做什么的
模拟浏览器发送请求, 本质:创建socket客户端-连接-发送请求-接收请求-断开连接
c.线程池或进程池
'''
import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def task(url):
r1= requests.get(url=url,headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"})
#模拟浏览器发送请求,内部建立socket连接,和服务端建立连接,
#print(r1.content) #这个是字节
#print(r1.text) #查看下载的文本
soup = BeautifulSoup(r1.text, 'html.parser') #把文本加持html解释器
div_con = soup.find('div',attrs={'class':'link-con'})
#print(div_con)
for item in div_con.find_all('div',attrs = {'class':'link-detail'}):
title = item.find_all('a')
title_url = item.find_all('bref')
print(title,title_url)
def run():
pool = ThreadPoolExecutor(5)
for i in range(1,50):
pool.submit(task,'https://dig.chouti.com/%s' % (i,))
if __name__ == '__main__':
run()