一篇文章掌握心理学 一篇文章掌握Python生成器与匿名函数,多线程与多进程

测试奇谭,BUG不见 。
讲解之前,我先说说我的教程和网上其他教程的区别:
1 我分享的是我在工作中高频使用的场景,是精华内容;很多人学习蟒蛇,不知道从何学起 。很多人学习寻找python,掌握了基本语法之后,不知道在哪里案例上手 。很多已经可能知道案例的人,却不怎么去学习更多高深的知识 。这三类人,我给大家提供一个好的学习平台,免费获取视频教程,电子书,以及课程的源代码!QQ群:101677771欢迎加入,一起讨论学习2 我分享的是学习方法,亦或说,是指明你该学哪些、该重点掌握哪些内容;3 基于1和2,你可以按照我的教程学,也可以网上找视频学,也可以看书学……你得明白,掌握学习方法比找学习资料重要得多 。
前5期,我已经分享了python的基础语法,如果你按照我的文章,一步一步练习,保准你对python代码的语法特点、书写方式有所了解,并能独立的写一些简单的函数方法,甚至能尝试开始自动化测试的实践(基于python语法) 。
当然,如果你想更进一步掌握python语法的特点,那接下来的几篇文章一定不要错过 。
这一场,主讲python的 生成器和匿名函数 。
目的:掌握这两个知识点的概念和使用 。
生成器01 什么是生成器?记住两个关键:

  • 生成器是一种特殊的函数方法 。 意味着它和函数(def)密不可分 。
  • 基于上一点, 只要函数中出现yield关键字,就是生成器函数  。
初学的你,还是太难理解?
02 通俗的讲解你可以将生成器理解为一个盒子,你可以向这个盒子里随意添加元素,当你需要的时候,再取出来用 。
请看下面的例子:
# 普通函数def func():return 1f = func()print("函数返回值:",f)->函数返回值:1print("函数返回值的类型:",type(f))->函数返回值的类型:<class 'int'># 生成器def gen_func():yield 1yield 2g = gen_func()print("生成器对象:",g)->生成器对象:<generator object gen_func at 0x00000189B8CFF7C8>print("生成器对象的类型:",type(g))->生成器对象的类型:<class 'generator'># 读取生成器对象的值,因为生成器也是一个迭代器,实现了python的迭代协议(即实现了__iter__方法)for i in g:print("生成器对象的值:",i)->生成器对象的值: 1->生成器对象的值: 203 生成器到底有什么用?作用:惰性求值(一边循环一边计算的机制),节省性能
04 生成器的常见用途?
  • 读大文件
  • 网络爬虫 scrapy 框架
  • 协程
举个例子:斐波那契数列(0,1,1,2,3,5...),打印斐波那契数列前50个元素
# 不使用生成器,会消耗大量内存def fib(idx):res=[]n, a, b = 0, 0, 1while n < idx:res.append(b)a, b = b, a+bn += 1return resres = fib(100)print(res)# 使用生成器,可节约大量内存def gen_fib(idx):n, a, b = 0, 0, 1while n < idx:yield ba, b = b, a+bn += 1for i in gen_fib(100):print(i)匿名函数01 什么是匿名函数?当:
  • 函数实现比较简单
  • 函数不需要被多个地方调用
  • 懒得给这个函数起名字
时,我们可以使用匿名函数 。
初学的你,还是太难理解?
02 通俗的讲解你想实现一个求x的平方的函数,但是这个函数太简单,不值得专门def定义,同时,你忘记了平方的英文如何拼写,要是命名成 "pingfang",又显得自己太low,于是乎,你可以不给这个函数起名字,还能实现它 。这就是匿名函数lambda表达式 。
比如:求一个数的平方
# 不用 lambda 表达式def square(x):return x * xprint(square(2))# 使用 lambda 表达式# 写法:lambda 返回值:计算表达式s = lambda x: x * xprint(s(2))一如既往,做个总结01 如果你是初学者,可以先不掌握生成器和匿名函数,待学成python后,再行琢磨;02 在实际工作中,生成器和匿名函数的使用频次,相对较高,并且在面试中是高频问点 。测试奇谭,BUG不见 。
大家好,我是谭叔 。
这一场,主讲python的 进程和线程  。
目的:掌握初学必须的进程和线程知识 。
进程和线程的区别和联系终于开始加深难度,来到进程和线程的知识点~
单就这两个概念,就难倒过不少初学者——今天学了概念,明天就忘记;明天学了例子,又忘记了概念 。
要理解进程和线程的联系和区别,我举个特简单的例子:
你的电脑有两个浏览器,一个谷歌浏览器,一个qq浏览器 。
一个浏览器就是一个进程 。然后,你打开了谷歌浏览器,百度搜索了测试奇谭,又新开一个标签页,打开谭叔的文章,如下图所示:
一篇文章掌握心理学 一篇文章掌握Python生成器与匿名函数,多线程与多进程

文章插图
 
【一篇文章掌握心理学 一篇文章掌握Python生成器与匿名函数,多线程与多进程】你可以这样理解—— 在同一个浏览器打开的两个网页就是两个线程  。 如果我关闭了浏览器,这两个线程也没有了 。
好了,当你有了概念后,我才好讲两者的区别和联系 。
进程
  • 优点稳定性高 ,当程序崩溃后,不影响其他进程的使用 。即谷歌浏览器崩溃后,qq浏览器还可以正常使用 。
  • 缺点创建进程的代价大  。即当你开了各种各样的浏览器后,你的电脑会特别卡,因为电脑内存和CPU有上限 。
线程
  • 优点多线程通常比多进程快一点,但优势不大
  • 缺点任何一个线程挂掉会造成整个进程崩溃,因为线程共享进程的内存(浏览器的例子不再适用,可以理解为绑在一条船上的蚂蚱)
多进程因为大多数小伙伴用的Windows操作系统,所以针对Unix/Linux的fork()调用抛开不谈 。
在python,一般使用multiprocessing实现多进程 。
import osfrom multiprocessing import Processdef run_proc(name):print('开始执行子进程 %s (%s)…' % (name, os.getpid()))# 子进程要执行的代码if __name__ == '__main__':print('父进程 %s.' % os.getpid())p = Process(target=run_proc, args=('test',)) # 创建一个Process实例print('子进程即将开始.')p.start()# 用start()方法启动实例p.join()# join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步print('子进程结束.')要细讲吗?
其实,我的备注写得很全,你只需copy代码下来执行一次,或者debug一次,就能明白 。
线程池如何理解?
即代码可运行的进程数量,有个地方管控它 。
from multiprocessing import Poolimport osimport timeimport randomdef long_time_task(name):print('开始 task %s (%s)...' % (name, os.getpid()))start = time.time()time.sleep(random.random() * 3)end = time.time()print('Task %s 执行花费 %0.2f 秒.' % (name, (end - start)))if __name__ == '__main__':print('父亲进程 %s.' % os.getpid())p = Pool(3)# 因为Pool的默认大小是4,故task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,但最多同时执行4个进程# 由于Pool的默认大小是CPU的核数,如果你拥有8核CPU,要提交至少9个子进程才能看到等待效果for i in range(5):p.apply_async(long_time_task, args=(i,))print('等待子进程结束...')p.close()# 对Pool对象调用join()方法会等待所有子进程执行完毕# 调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了p.join()print('所有子进程已执行.')以上,是必知必会的 。
当然,最好的学习时机是:当你要用时,再来复盘学,效果最佳 。
如果你学了,没有使用场景,我建议缓一缓学或者作为知识储备 。
多线程多线程有三点必须提前明确:
  • 多任务需求可以由多进程完成,也可以由一个进程内的多线程完成
  • 进程是由若干线程组成
  • 一个进程至少有一个线程
Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,我们一般使用高级模块threading(对_thread进行过封装) 。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行,我们看一个简单的例子:
import timeimport threading# 新线程执行的代码def loop():# 由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,# Python的threading模块有个current_thread()函数,它永远返回当前线程的实例print('线程ss %s 运行中…' % threading.current_thread().name)n = 0# 主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程,在打印输出的时候显示名字while n < 5:n = n + 1print('线程ss %s >>> %s' % (threading.current_thread().name, n))time.sleep(1)print('线程ss %s 已结束.' % threading.current_thread().name)print('线程 %s is 运行中…' % threading.current_thread().name)t = threading.Thread(target=loop, name='LoopThread')t.start()t.join()'''对于 join()方法而言,其另一个重要方面是其实它根本不需要调用 。一旦线程启动,它们 就会一直执行,直到给定的函数完成后退出 。如果主线程还有其他事情要去做,而不是等待这些线程完成(例如其他处理或者等待新的客户端请求),就可以不调用 join() 。join()方法只有在你需要等待线程完成的时候才是有用的'''print('线程 %s 已结束.' % threading.current_thread().name)同样,以上内容,是必知必会的 。
并且,工作场景,我们一般会使用多线程处理问题,而非多进程 。(注意:是一般)
至于进程间通信、线程锁、GIL锁、多线程变量、线程间通信、异步协程等知识,讲起来比较复杂,也不是极简教程的核心,你可以先自学,或者当你真正要使用它的时候再去看,再去学 。
一如既往,做个总结01 多线程、多进程,是必知必会的;02 学习进度自己琢磨,既不能死磕,亦不能简单跳过;03 小伙伴比较关心,面试时会不会被问到 。答:一般公司不会,so?